1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
4  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
5  *  Copyright (C) 2003 Apple Computer, Inc.
6  *  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
7  *  Copyright (C) 2007 Maksim Orlovich (maksim@kde.org)
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Library General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Library General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Library General Public License
20  *  along with this library; see the file COPYING.LIB.  If not, write to
21  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 #include "function.h"
27 #include "scriptfunction.h"
28 #include "dtoa.h"
29 #include "internal.h"
30 #include "function_object.h"
31 #include "lexer.h"
32 #include "nodes.h"
33 #include "operations.h"
34 #include "debugger.h"
35 #include "PropertyNameArray.h"
36 #include "commonunicode.h"
37 
38 #include <stdio.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <assert.h>
42 #include <string.h>
43 #include <string>
44 #include "wtf/DisallowCType.h"
45 #include "wtf/ASCIICType.h"
46 #include "bytecode/machine.h"
47 
48 using namespace WTF;
49 
50 //#define KJS_VERBOSE
51 
52 namespace KJS
53 {
54 
55 // ----------------------------- FunctionImp ----------------------------------
56 
57 const ClassInfo FunctionImp::info = {"Function", &InternalFunctionImp::info, nullptr, nullptr};
58 
FunctionImp(ExecState * exec,const Identifier & n,FunctionBodyNode * b,const ScopeChain & sc)59 FunctionImp::FunctionImp(ExecState *exec, const Identifier &n, FunctionBodyNode *b, const ScopeChain &sc)
60     : InternalFunctionImp(static_cast<FunctionPrototype *>
61                           (exec->lexicalInterpreter()->builtinFunctionPrototype()), n)
62     , body(b)
63     , _scope(sc)
64 {
65 }
66 
mark()67 void FunctionImp::mark()
68 {
69     InternalFunctionImp::mark();
70     _scope.mark();
71 }
72 
~FunctionImp()73 FunctionImp::~FunctionImp()
74 {
75 }
76 
initialCompile(ExecState * newExec)77 void FunctionImp::initialCompile(ExecState *newExec)
78 {
79     FunctionBodyNode *body = this->body.get();
80 
81     // Reserve various slots needed for the activation object. We do it only once,
82     // --- isCompiled() would return true even if debugging state changed
83     body->reserveSlot(ActivationImp::LengthSlot, false);
84     body->reserveSlot(ActivationImp::TearOffNeeded, false);
85     body->reserveSlot(ActivationImp::ScopeLink, false /* will mark via ScopeChain::mark() */);
86     body->reserveSlot(ActivationImp::FunctionSlot, true);
87     body->reserveSlot(ActivationImp::ArgumentsObjectSlot, true);
88 
89     // Create declarations for parameters, and allocate the symbols.
90     // We always just give them sequential positions, to make passInParameters
91     // simple (though perhaps wasting memory in the trivial case)
92     for (size_t i = 0; i < body->numParams(); ++i) {
93         body->addSymbolOverwriteID(i + ActivationImp::NumReservedSlots, body->paramName(i), DontDelete);
94     }
95 
96     body->processDecls(newExec);
97     body->compile(FunctionCode, newExec->dynamicInterpreter()->debugger() ? Debug : Release);
98 }
99 
100 #ifdef KJS_VERBOSE
101 static int           callDepth;
102 static std::string   callIndent;
103 
ind()104 static const char *ind()
105 {
106     callIndent = "";
107     for (int i = 0; i < callDepth; ++i) {
108         callIndent += "     ";
109     }
110     return callIndent.c_str();
111 }
112 
113 // Multiline print adding indentation
printInd(const char * str)114 static void printInd(const char *str)
115 {
116     fprintf(stderr, "%s", ind());
117     for (const char *c = str; *c; ++c) {
118         if (*c != '\n') {
119             fprintf(stderr, "%c", *c);
120         } else {
121             fprintf(stderr, "\n%s", ind());
122         }
123     }
124 }
125 
126 #endif
127 
callAsFunction(ExecState * exec,JSObject * thisObj,const List & args)128 JSValue *FunctionImp::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
129 {
130     assert(thisObj);
131 
132 #ifdef KJS_VERBOSE
133     ++callDepth;
134 #endif
135 
136     Debugger *dbg = exec->dynamicInterpreter()->debugger();
137 
138     // enter a new execution context
139     FunctionExecState newExec(exec->dynamicInterpreter(), thisObj, body.get(), exec, this);
140     if (exec->hadException()) {
141         newExec.setException(exec->exception());
142     }
143 
144     FunctionBodyNode *body = this->body.get();
145 
146     // The first time we're called, compute the set of local variables,
147     // and compile the body. (note that parameters have been collected
148     // during the AST build)
149     CompileType currentState = body->compileState();
150     if (currentState == NotCompiled) {
151         initialCompile(&newExec);
152     } else {
153         // Otherwise, we may still need to recompile due to debug...
154         CompileType desiredState = dbg ? Debug : Release;
155         if (desiredState != currentState) {
156             body->compile(FunctionCode, desiredState);
157         }
158     }
159 
160     size_t stackSize              = 0;
161     LocalStorageEntry *stackSpace = nullptr;
162 
163     // We always allocate on stack initially, and tearoff only after we're done.
164     int regs   = body->numLocalsAndRegisters();
165     stackSize  = sizeof(LocalStorageEntry) * regs;
166     stackSpace = (LocalStorageEntry *)exec->dynamicInterpreter()->stackAlloc(stackSize);
167 
168     ActivationImp *activation = static_cast<ActivationImp *>(newExec.activationObject());
169     activation->setup(&newExec, this, &args, stackSpace);
170     activation->tearOffNeededSlot() = body->tearOffAtEnd();
171 
172     newExec.initLocalStorage(stackSpace, regs);
173 
174     JSValue *result = Machine::runBlock(&newExec, body->code(), exec);
175 
176     // If we need to tear off now --- either due to static flag above, or
177     // if execution requested it dynamically --- do so now.
178     if (activation->tearOffNeededSlot()) {
179         activation->performTearOff();
180     } else {
181         // Otherwise, we recycle the activation object; we must clear its
182         // data pointer, though, since that may become dead.
183         // (we also unlink it from the scope chain at this time)
184         activation->scopeLink().deref();
185         activation->localStorage = nullptr;
186         exec->dynamicInterpreter()->recycleActivation(activation);
187     }
188 
189     // Now free the stack space..
190     exec->dynamicInterpreter()->stackFree(stackSize);
191 
192 #ifdef KJS_VERBOSE
193     fprintf(stderr, "%s", ind());
194     if (exec->exception()) {
195         printInfo(exec, "throwing", exec->exception());
196     } else {
197         printInfo(exec, "returning", result);
198     }
199 
200     --callDepth;
201 #endif
202 
203     return result;
204 }
205 
argumentsGetter(ExecState * exec,JSObject *,const Identifier & propertyName,const PropertySlot & slot)206 JSValue *FunctionImp::argumentsGetter(ExecState *exec, JSObject *, const Identifier &propertyName, const PropertySlot &slot)
207 {
208     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
209     ExecState *context = exec;
210     while (context) {
211         if (context->function() == thisObj) {
212             return static_cast<ActivationImp *>(context->activationObject())->get(exec, propertyName);
213         }
214         context = context->callingExecState();
215     }
216     return jsNull();
217 }
218 
callerGetter(ExecState * exec,JSObject *,const Identifier &,const PropertySlot & slot)219 JSValue *FunctionImp::callerGetter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot)
220 {
221     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
222     ExecState *context = exec;
223     while (context) {
224         if (context->function() == thisObj) {
225             break;
226         }
227         context = context->callingExecState();
228     }
229 
230     if (!context) {
231         return jsNull();
232     }
233 
234     ExecState *callingContext = context->callingExecState();
235     if (!callingContext) {
236         return jsNull();
237     }
238 
239     FunctionImp *callingFunction = callingContext->function();
240     if (!callingFunction) {
241         return jsNull();
242     }
243 
244     return callingFunction;
245 }
246 
lengthGetter(ExecState *,JSObject *,const Identifier &,const PropertySlot & slot)247 JSValue *FunctionImp::lengthGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
248 {
249     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
250     return jsNumber(thisObj->body->numParams());
251 }
252 
nameGetter(ExecState *,JSObject *,const Identifier &,const PropertySlot & slot)253 JSValue *FunctionImp::nameGetter(ExecState *, JSObject *, const Identifier &, const PropertySlot &slot)
254 {
255     FunctionImp *thisObj = static_cast<FunctionImp *>(slot.slotBase());
256     return jsString(thisObj->functionName().ustring());
257 }
258 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)259 bool FunctionImp::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
260 {
261     // Find the arguments from the closest context.
262     if (propertyName == exec->propertyNames().arguments) {
263         slot.setCustom(this, argumentsGetter);
264         return true;
265     }
266 
267     // Compute length of parameters.
268     if (propertyName == exec->propertyNames().length) {
269         slot.setCustom(this, lengthGetter);
270         return true;
271     }
272 
273     // Calling function (Mozilla-extension)
274     if (propertyName == exec->propertyNames().caller) {
275         slot.setCustom(this, callerGetter);
276         return true;
277     }
278 
279     // Function name (Mozilla-extension)
280     if (propertyName == exec->propertyNames().name) {
281         slot.setCustom(this, nameGetter);
282         return true;
283     }
284 
285     return InternalFunctionImp::getOwnPropertySlot(exec, propertyName, slot);
286 }
287 
getOwnPropertyDescriptor(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & desc)288 bool FunctionImp::getOwnPropertyDescriptor(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc)
289 {
290     if (propertyName == exec->propertyNames().length) {
291         desc.setPropertyDescriptorValues(exec, jsNumber(body->numParams()), ReadOnly | DontDelete | DontEnum);
292         return true;
293     }
294 
295     return KJS::JSObject::getOwnPropertyDescriptor(exec, propertyName, desc);
296 }
297 
put(ExecState * exec,const Identifier & propertyName,JSValue * value,int attr)298 void FunctionImp::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
299 {
300     if (propertyName == exec->propertyNames().arguments ||
301             propertyName == exec->propertyNames().length ||
302             propertyName == exec->propertyNames().name) {
303         return;
304     }
305     InternalFunctionImp::put(exec, propertyName, value, attr);
306 }
307 
deleteProperty(ExecState * exec,const Identifier & propertyName)308 bool FunctionImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
309 {
310     if (propertyName == exec->propertyNames().arguments ||
311             propertyName == exec->propertyNames().length ||
312             propertyName == exec->propertyNames().name) {
313         return false;
314     }
315     return InternalFunctionImp::deleteProperty(exec, propertyName);
316 }
317 
318 /* Returns the parameter name corresponding to the given index. eg:
319  * function f1(x, y, z): getParameterName(0) --> x
320  *
321  * If a name appears more than once, only the last index at which
322  * it appears associates with it. eg:
323  * function f2(x, x): getParameterName(0) --> null
324  */
getParameterName(size_t index)325 Identifier FunctionImp::getParameterName(size_t index)
326 {
327     if (index >= body->numParams()) {
328         return CommonIdentifiers::shared()->nullIdentifier;
329     }
330 
331     Identifier name = body->paramName(index);
332 
333     // Are there any subsequent parameters with the same name?
334     for (size_t pos = index + 1; pos < body->numParams(); ++pos)
335         if (body->paramName(pos) == name) {
336             return CommonIdentifiers::shared()->nullIdentifier;
337         }
338 
339     return name;
340 }
341 
implementsConstruct() const342 bool FunctionImp::implementsConstruct() const
343 {
344     return true;
345 }
346 
347 // ECMA 13.2.2 [[Construct]]
construct(ExecState * exec,const List & args)348 JSObject *FunctionImp::construct(ExecState *exec, const List &args)
349 {
350     JSObject *proto;
351     JSValue *p = get(exec, exec->propertyNames().prototype);
352     if (JSValue::isObject(p)) {
353         proto = static_cast<JSObject *>(p);
354     } else {
355         proto = exec->lexicalInterpreter()->builtinObjectPrototype();
356     }
357 
358     JSObject *obj(new JSObject(proto));
359 
360     JSValue *res = call(exec, obj, args);
361 
362     if (JSValue::isObject(res)) {
363         return static_cast<JSObject *>(res);
364     } else {
365         return obj;
366     }
367 }
368 
369 // ------------------------------ Thrower ---------------------------------
370 
Thrower(ErrorType type)371 Thrower::Thrower(ErrorType type)
372     : JSObject(),
373       m_type(type)
374 {
375 }
376 
callAsFunction(ExecState * exec,JSObject *,const List &)377 JSValue *Thrower::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List & /*args*/)
378 {
379     return throwError(exec, m_type);
380 }
381 
382 // ------------------------------ BoundFunction ---------------------------------
383 
BoundFunction(ExecState * exec,JSObject * targetFunction,JSObject * boundThis,KJS::List boundArgs)384 BoundFunction::BoundFunction(ExecState *exec, JSObject *targetFunction, JSObject *boundThis, KJS::List boundArgs)
385     : InternalFunctionImp(static_cast<FunctionPrototype *>(exec->lexicalInterpreter()->builtinFunctionPrototype())),
386       m_targetFunction(targetFunction),
387       m_boundThis(boundThis),
388       m_boundArgs(boundArgs)
389 {
390 }
391 
392 // ECMAScript Edition 5.1r6 - 15.3.4.5.2
construct(ExecState * exec,const List & extraArgs)393 JSObject *BoundFunction::construct(ExecState *exec, const List &extraArgs)
394 {
395     JSObject *target = m_targetFunction;
396     if (!target->implementsConstruct()) {
397         return throwError(exec, TypeError);
398     }
399     List boundArgs = m_boundArgs;
400 
401     List args;
402     for (int i = 0; i < boundArgs.size(); ++i) {
403         args.append(boundArgs.at(i));
404     }
405     for (int i = 0; i < extraArgs.size(); ++i) {
406         args.append(extraArgs.at(i));
407     }
408 
409     return target->construct(exec, args);
410 }
411 
412 // ECMAScript Edition 5.1r6 - 15.3.4.5.1
callAsFunction(ExecState * exec,JSObject *,const List & extraArgs)413 JSValue *BoundFunction::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List &extraArgs)
414 {
415     List boundArgs = m_boundArgs;
416     JSObject *boundThis = m_boundThis;
417     JSObject *target = m_targetFunction;
418 
419     List args;
420     for (int i = 0; i < boundArgs.size(); ++i) {
421         args.append(boundArgs.at(i));
422     }
423     for (int i = 0; i < extraArgs.size(); ++i) {
424         args.append(extraArgs.at(i));
425     }
426 
427     return target->callAsFunction(exec, boundThis, args);
428 }
429 
430 // ECMAScript Edition 5.1r6 - 15.3.4.5.3
hasInstance(ExecState * exec,JSValue * value)431 bool BoundFunction::hasInstance(ExecState *exec, JSValue *value)
432 {
433     JSObject *target = m_targetFunction;
434     if (!target->implementsHasInstance()) {
435         return throwError(exec, TypeError);
436     }
437 
438     return target->hasInstance(exec, value);
439 }
440 
setTargetFunction(JSObject * targetFunction)441 void BoundFunction::setTargetFunction(JSObject *targetFunction)
442 {
443     m_targetFunction = targetFunction;
444 }
445 
setBoundArgs(const List & boundArgs)446 void BoundFunction::setBoundArgs(const List &boundArgs)
447 {
448     m_boundArgs = boundArgs;
449 }
450 
setBoundThis(JSObject * boundThis)451 void BoundFunction::setBoundThis(JSObject *boundThis)
452 {
453     m_boundThis = boundThis;
454 }
455 
456 // ------------------------------ IndexToNameMap ---------------------------------
457 
458 // We map indexes in the arguments array to their corresponding argument names.
459 // Example: function f(x, y, z): arguments[0] = x, so we map 0 to Identifier("x").
460 
461 // Once we have an argument name, we can get and set the argument's value in the
462 // activation object.
463 
464 // We use Identifier::null to indicate that a given argument's value
465 // isn't stored in the activation object.
466 
IndexToNameMap(FunctionImp * func,const List & args)467 IndexToNameMap::IndexToNameMap(FunctionImp *func, const List &args)
468 {
469     _map = new Identifier[args.size()];
470     this->_size = args.size();
471 
472     size_t i = 0;
473     ListIterator iterator = args.begin();
474     for (; iterator != args.end(); i++, iterator++) {
475         _map[i] = func->getParameterName(i);    // null if there is no corresponding parameter
476     }
477 }
478 
~IndexToNameMap()479 IndexToNameMap::~IndexToNameMap()
480 {
481     delete [] _map;
482 }
483 
isMapped(const Identifier & index) const484 bool IndexToNameMap::isMapped(const Identifier &index) const
485 {
486     bool indexIsNumber;
487     int indexAsNumber = index.toStrictUInt32(&indexIsNumber);
488 
489     if (!indexIsNumber) {
490         return false;
491     }
492 
493     if (indexAsNumber >= _size) {
494         return false;
495     }
496 
497     if (_map[indexAsNumber].isNull()) {
498         return false;
499     }
500 
501     return true;
502 }
503 
unMap(const Identifier & index)504 void IndexToNameMap::unMap(const Identifier &index)
505 {
506     bool indexIsNumber;
507     int indexAsNumber = index.toStrictUInt32(&indexIsNumber);
508 
509     assert(indexIsNumber && indexAsNumber < _size);
510 
511     _map[indexAsNumber] = CommonIdentifiers::shared()->nullIdentifier;;
512 }
513 
size() const514 int IndexToNameMap::size() const
515 {
516     return _size;
517 }
518 
operator [](int index)519 Identifier &IndexToNameMap::operator[](int index)
520 {
521     return _map[index];
522 }
523 
operator [](const Identifier & index)524 Identifier &IndexToNameMap::operator[](const Identifier &index)
525 {
526     bool indexIsNumber;
527     int indexAsNumber = index.toStrictUInt32(&indexIsNumber);
528 
529     assert(indexIsNumber && indexAsNumber < _size);
530 
531     return (*this)[indexAsNumber];
532 }
533 
534 // ------------------------------ Arguments ---------------------------------
535 
536 const ClassInfo Arguments::info = {"Arguments", nullptr, nullptr, nullptr};
537 
538 // ECMA 10.1.8
Arguments(ExecState * exec,FunctionImp * func,const List & args,ActivationImp * act)539 Arguments::Arguments(ExecState *exec, FunctionImp *func, const List &args, ActivationImp *act)
540     : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()),
541       _activationObject(act),
542       indexToNameMap(func, args)
543 {
544     putDirect(exec->propertyNames().callee, func, DontEnum);
545     putDirect(exec->propertyNames().length, args.size(), DontEnum);
546 
547     int i = 0;
548     ListIterator iterator = args.begin();
549     for (; iterator != args.end(); i++, iterator++) {
550         if (!indexToNameMap.isMapped(Identifier::from(i))) {
551             //ECMAScript Edition 5.1r6 - 10.6.11.b, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true
552             JSObject::put(exec, Identifier::from(i), *iterator, None);
553         }
554     }
555 }
556 
mark()557 void Arguments::mark()
558 {
559     JSObject::mark();
560     if (_activationObject && !_activationObject->marked()) {
561         _activationObject->mark();
562     }
563 }
564 
mappedIndexGetter(ExecState * exec,JSObject *,const Identifier & propertyName,const PropertySlot & slot)565 JSValue *Arguments::mappedIndexGetter(ExecState *exec, JSObject *, const Identifier &propertyName, const PropertySlot &slot)
566 {
567     Arguments *thisObj = static_cast<Arguments *>(slot.slotBase());
568     return thisObj->_activationObject->get(exec, thisObj->indexToNameMap[propertyName]);
569 }
570 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)571 bool Arguments::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
572 {
573     if (indexToNameMap.isMapped(propertyName)) {
574         slot.setCustom(this, mappedIndexGetter);
575         return true;
576     }
577 
578     return JSObject::getOwnPropertySlot(exec, propertyName, slot);
579 }
580 
put(ExecState * exec,const Identifier & propertyName,JSValue * value,int attr)581 void Arguments::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
582 {
583     if (indexToNameMap.isMapped(propertyName)) {
584         unsigned attr = 0;
585         JSObject::getPropertyAttributes(propertyName, attr);
586         if (attr & ReadOnly) {
587             return;
588         }
589 
590         _activationObject->put(exec, indexToNameMap[propertyName], value, attr);
591     } else {
592         JSObject::put(exec, propertyName, value, attr);
593     }
594 }
595 
deleteProperty(ExecState * exec,const Identifier & propertyName)596 bool Arguments::deleteProperty(ExecState *exec, const Identifier &propertyName)
597 {
598     if (indexToNameMap.isMapped(propertyName)) {
599         bool result = JSObject::deleteProperty(exec, propertyName);
600         if (result) {
601             _activationObject->deleteProperty(exec, indexToNameMap[propertyName]);
602             indexToNameMap.unMap(propertyName);
603         }
604         return true;
605     } else {
606         return JSObject::deleteProperty(exec, propertyName);
607     }
608 }
609 
getOwnPropertyNames(ExecState * exec,PropertyNameArray & propertyNames,PropertyMap::PropertyMode mode)610 void Arguments::getOwnPropertyNames(ExecState *exec, PropertyNameArray &propertyNames, PropertyMap::PropertyMode mode)
611 {
612     unsigned int length = indexToNameMap.size();
613     unsigned attr;
614     for (unsigned int i = 0; i < length; ++i) {
615         attr = 0;
616         Identifier ident = Identifier::from(i);
617 
618         if (indexToNameMap.isMapped(ident) &&
619                 _activationObject->getPropertyAttributes(indexToNameMap[ident], attr)) {
620             if (PropertyMap::checkEnumerable(attr, mode)) {
621                 propertyNames.add(ident);
622             }
623         }
624     }
625 
626     JSObject::getOwnPropertyNames(exec, propertyNames, mode);
627 }
628 
defineOwnProperty(ExecState * exec,const Identifier & propertyName,PropertyDescriptor & desc,bool shouldThrow)629 bool Arguments::defineOwnProperty(ExecState *exec, const Identifier &propertyName, PropertyDescriptor &desc, bool shouldThrow)
630 {
631     bool isMapped = indexToNameMap.isMapped(propertyName);
632 
633     Identifier mappedName;
634     if (isMapped) {
635         mappedName = indexToNameMap[propertyName];
636     } else {
637         mappedName = propertyName;
638     }
639 
640     bool allowed = JSObject::defineOwnProperty(exec, propertyName, desc, false);
641 
642     if (!allowed) {
643         if (shouldThrow) {
644             throwError(exec, TypeError);
645         }
646         return false;
647     }
648     if (isMapped) {
649         if (desc.isAccessorDescriptor()) {
650             indexToNameMap.unMap(propertyName);
651         } else {
652             if (desc.value()) {
653                 _activationObject->putDirect(mappedName, desc.value(), desc.attributes());
654             }
655             if (desc.writableSet() && desc.writable() == false) {
656                 indexToNameMap.unMap(propertyName);
657             }
658         }
659     }
660 
661     return true;
662 }
663 
664 // ------------------------------ ActivationImp --------------------------------
665 
666 const ClassInfo ActivationImp::info = {"Activation", nullptr, nullptr, nullptr};
667 
668 // ECMA 10.1.6
setup(ExecState * exec,FunctionImp * function,const List * arguments,LocalStorageEntry * entries)669 void ActivationImp::setup(ExecState *exec, FunctionImp *function,
670                           const List *arguments, LocalStorageEntry *entries)
671 {
672     FunctionBodyNode *body = function->body.get();
673 
674     size_t total = body->numLocalsAndRegisters();
675     localStorage  = entries;
676     lengthSlot()  = total;
677 
678     // we can now link ourselves into the scope, which will also fix up our scopeLink().
679     exec->pushVariableObjectScope(this);
680 
681     const FunctionBodyNode::SymbolInfo *symInfo = body->getLocalInfo();
682 
683     // Setup our fields
684     this->arguments = arguments;
685     functionSlot()  = function;
686     argumentsObjectSlot() = jsUndefined();
687     symbolTable     = &body->symbolTable();
688 
689     // Set the mark/don't mark flags and attributes for everything
690     for (size_t p = 0; p < total; ++p) {
691         entries[p].attributes = symInfo[p].attr;
692     }
693 
694     // Pass in the parameters (ECMA 10.1.3q)
695 #ifdef KJS_VERBOSE
696     fprintf(stderr, "%s---------------------------------------------------\n"
697             "%sprocessing parameters for %s call\n", ind(), ind(),
698             function->functionName().isEmpty() ? "(internal)" : function->functionName().ascii());
699 #endif
700     size_t numParams   = body->numParams();
701     size_t numPassedIn = min(numParams, static_cast<size_t>(arguments->size()));
702 
703     size_t pos = 0;
704     for (; pos < numPassedIn; ++pos) {
705         size_t symNum = pos + ActivationImp::NumReservedSlots;
706         JSValue *v = arguments->atUnchecked(pos);
707 
708         entries[symNum].val.valueVal = v;
709 
710 #ifdef KJS_VERBOSE
711         fprintf(stderr, "%s setting parameter %s", ind(), body->paramName(pos).ascii());
712         printInfo(exec, "to", v);
713 #endif
714     }
715 
716     for (; pos < numParams; ++pos) {
717         size_t symNum = pos + ActivationImp::NumReservedSlots;
718         entries[symNum].val.valueVal = jsUndefined();
719 
720 #ifdef KJS_VERBOSE
721         fprintf(stderr, "%s setting parameter %s to undefined (not passed in)", ind(), body->paramName(pos).ascii());
722 #endif
723     }
724 
725 #ifdef KJS_VERBOSE
726     fprintf(stderr, "\n%s---------------------------------\n", ind());
727     fprintf(stderr, "%sBody:\n", ind());
728     fprintf(stderr, "%s---------------------------------\n", ind());
729     printInd(body->toString().ascii());
730     fprintf(stderr, "\n%s---------------------------------\n\n", ind());
731 #endif
732 
733     // Initialize the rest of the locals to 'undefined'
734     for (size_t pos = numParams + ActivationImp::NumReservedSlots; pos < total; ++pos) {
735         entries[pos].val.valueVal = jsUndefined();
736     }
737 
738     // Finally, put in the functions. Note that this relies on above
739     // steps to have completed, since it can trigger a GC.
740     size_t  numFuns  = body->numFunctionLocals();
741     size_t *funsData = body->getFunctionLocalInfo();
742     for (size_t fun = 0; fun < numFuns; ++fun) {
743         size_t id = funsData[fun];
744         entries[id].val.valueVal = symInfo[id].funcDecl->makeFunctionObject(exec);
745     }
746 }
747 
performTearOff()748 void ActivationImp::performTearOff()
749 {
750     // Create a new local array, copy stuff over
751     size_t total = lengthSlot();
752     LocalStorageEntry *entries = new LocalStorageEntry[total];
753     std::memcpy(entries, localStorage, total * sizeof(LocalStorageEntry));
754     localStorage  = entries;
755 }
756 
requestTearOff()757 void ActivationImp::requestTearOff()
758 {
759     tearOffNeededSlot() = true;
760 }
761 
argumentsGetter(ExecState * exec,JSObject *,const Identifier &,const PropertySlot & slot)762 JSValue *ActivationImp::argumentsGetter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot)
763 {
764     ActivationImp *thisObj = static_cast<ActivationImp *>(slot.slotBase());
765 
766     if (thisObj->argumentsObjectSlot() == jsUndefined()) {
767         thisObj->createArgumentsObject(exec);
768     }
769 
770     return thisObj->argumentsObjectSlot();
771 }
772 
getArgumentsGetter()773 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
774 {
775     return ActivationImp::argumentsGetter;
776 }
777 
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)778 bool ActivationImp::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
779 {
780     if (symbolTableGet(propertyName, slot)) {
781         return true;
782     }
783 
784     if (JSValue **location = getDirectLocation(propertyName)) {
785         slot.setValueSlot(this, location);
786         return true;
787     }
788 
789     // Only return the built-in arguments object if it wasn't overridden above.
790     if (propertyName == exec->propertyNames().arguments) {
791         slot.setCustom(this, getArgumentsGetter());
792         return true;
793     }
794 
795     // We don't call through to JSObject because there's no way to give an
796     // activation object getter properties or a prototype.
797     ASSERT(!_prop.hasGetterSetterProperties());
798     ASSERT(prototype() == jsNull());
799     return false;
800 }
801 
deleteProperty(ExecState * exec,const Identifier & propertyName)802 bool ActivationImp::deleteProperty(ExecState *exec, const Identifier &propertyName)
803 {
804     if (propertyName == exec->propertyNames().arguments) {
805         return false;
806     }
807 
808     return JSVariableObject::deleteProperty(exec, propertyName);
809 }
810 
putDirect(const Identifier & propertyName,JSValue * value,int attr)811 void ActivationImp::putDirect(const Identifier &propertyName, JSValue *value, int attr)
812 {
813     size_t index = symbolTable->get(propertyName.ustring().rep());
814     if (index != missingSymbolMarker()) {
815         LocalStorageEntry &entry = localStorage[index];
816         entry.val.valueVal = value;
817         entry.attributes = attr;
818         return;
819     }
820 
821     JSVariableObject::putDirect(propertyName, value, attr);
822 }
823 
getDirect(const Identifier & propertyName) const824 JSValue *ActivationImp::getDirect(const Identifier &propertyName) const
825 {
826     size_t index = symbolTable->get(propertyName.ustring().rep());
827     if (index != missingSymbolMarker()) {
828         LocalStorageEntry &entry = localStorage[index];
829         return entry.val.valueVal;
830     }
831 
832     return JSVariableObject::getDirect(propertyName);
833 }
834 
getPropertyAttributes(const Identifier & propertyName,unsigned int & attributes) const835 bool ActivationImp::getPropertyAttributes(const Identifier &propertyName, unsigned int &attributes) const
836 {
837     size_t index = symbolTable->get(propertyName.ustring().rep());
838     if (index != missingSymbolMarker()) {
839         LocalStorageEntry &entry = localStorage[index];
840         attributes = entry.attributes;
841         return true;
842     }
843 
844     return JSVariableObject::getPropertyAttributes(propertyName, attributes);
845 }
846 
put(ExecState *,const Identifier & propertyName,JSValue * value,int attr)847 void ActivationImp::put(ExecState *, const Identifier &propertyName, JSValue *value, int attr)
848 {
849     // If any bits other than DontDelete are set, then we bypass the read-only check.
850     bool checkReadOnly = !(attr & ~DontDelete);
851     if (symbolTablePut(propertyName, value, checkReadOnly)) {
852         return;
853     }
854 
855     // We don't call through to JSObject because __proto__ and getter/setter
856     // properties are non-standard extensions that other implementations do not
857     // expose in the activation object.
858     ASSERT(!_prop.hasGetterSetterProperties());
859     _prop.put(propertyName, value, attr, checkReadOnly);
860 }
861 
createArgumentsObject(ExecState * exec)862 void ActivationImp::createArgumentsObject(ExecState *exec)
863 {
864     requestTearOff();
865     argumentsObjectSlot() = new Arguments(exec, static_cast<FunctionImp *>(functionSlot()),
866                                           *arguments, const_cast<ActivationImp *>(this));
867 }
868 
869 // ------------------------------ GlobalFunc -----------------------------------
870 
GlobalFuncImp(ExecState * exec,FunctionPrototype * funcProto,int i,int len,const Identifier & name)871 GlobalFuncImp::GlobalFuncImp(ExecState *exec, FunctionPrototype *funcProto, int i, int len, const Identifier &name)
872     : InternalFunctionImp(funcProto, name)
873     , id(i)
874 {
875     putDirect(exec->propertyNames().length, len, DontDelete | ReadOnly | DontEnum);
876 }
877 
encode(ExecState * exec,const List & args,const char * do_not_escape)878 static JSValue *encode(ExecState *exec, const List &args, const char *do_not_escape)
879 {
880     UString r = "", s, str = JSValue::toString(args[0], exec);
881     CString cstr = str.UTF8String();
882     const char *p = cstr.c_str();
883     for (size_t k = 0; k < cstr.size(); k++, p++) {
884         char c = *p;
885         if (c && strchr(do_not_escape, c)) {
886             r.append(c);
887         } else {
888             char tmp[4];
889             sprintf(tmp, "%%%02X", (unsigned char)c);
890             r += tmp;
891         }
892     }
893     return jsString(r);
894 }
895 
decode(ExecState * exec,const List & args,const char * do_not_unescape)896 static JSValue *decode(ExecState *exec, const List &args, const char *do_not_unescape)
897 {
898     UString s = "", str = JSValue::toString(args[0], exec);
899     int k = 0, len = str.size();
900     const UChar *d = str.data();
901     UChar u;
902     while (k < len) {
903         const UChar *p = d + k;
904         UChar c = *p;
905         if (c == '%') {
906             int charLen = 0;
907             if (k <= len - 3 && isASCIIHexDigit(p[1].uc) && isASCIIHexDigit(p[2].uc)) {
908                 const char b0 = Lexer::convertHex(p[1].uc, p[2].uc);
909                 const int sequenceLen = UTF8SequenceLength(b0);
910                 if (sequenceLen != 0 && k <= len - sequenceLen * 3) {
911                     charLen = sequenceLen * 3;
912                     char sequence[5];
913                     sequence[0] = b0;
914                     for (int i = 1; i < sequenceLen; ++i) {
915                         const UChar *q = p + i * 3;
916                         if (q[0] == '%' && isASCIIHexDigit(q[1].uc) && isASCIIHexDigit(q[2].uc)) {
917                             sequence[i] = Lexer::convertHex(q[1].uc, q[2].uc);
918                         } else {
919                             charLen = 0;
920                             break;
921                         }
922                     }
923                     if (charLen != 0) {
924                         sequence[sequenceLen] = 0;
925                         const int character = decodeUTF8Sequence(sequence);
926                         if (character < 0 || character >= 0x110000) {
927                             charLen = 0;
928                         } else if (character >= 0x10000) {
929                             // Convert to surrogate pair.
930                             s.append(static_cast<unsigned short>(0xD800 | ((character - 0x10000) >> 10)));
931                             u = static_cast<unsigned short>(0xDC00 | ((character - 0x10000) & 0x3FF));
932                         } else {
933                             u = static_cast<unsigned short>(character);
934                         }
935                     }
936                 }
937             }
938             if (charLen == 0) {
939                 return throwError(exec, URIError);
940             }
941             if (u.uc == 0 || u.uc >= 128 || !strchr(do_not_unescape, u.low())) {
942                 c = u;
943                 k += charLen - 1;
944             }
945         }
946         k++;
947         s.append(c);
948     }
949     return jsString(s);
950 }
951 
parseDigit(unsigned short c,int radix)952 static int parseDigit(unsigned short c, int radix)
953 {
954     int digit = -1;
955 
956     if (c >= '0' && c <= '9') {
957         digit = c - '0';
958     } else if (c >= 'A' && c <= 'Z') {
959         digit = c - 'A' + 10;
960     } else if (c >= 'a' && c <= 'z') {
961         digit = c - 'a' + 10;
962     }
963 
964     if (digit >= radix) {
965         return -1;
966     }
967     return digit;
968 }
969 
parseIntOverflow(const char * s,int length,int radix)970 double parseIntOverflow(const char *s, int length, int radix)
971 {
972     double number = 0.0;
973     double radixMultiplier = 1.0;
974 
975     for (const char *p = s + length - 1; p >= s; p--) {
976         if (radixMultiplier == Inf) {
977             if (*p != '0') {
978                 number = Inf;
979                 break;
980             }
981         } else {
982             int digit = parseDigit(*p, radix);
983             number += digit * radixMultiplier;
984         }
985 
986         radixMultiplier *= radix;
987     }
988 
989     return number;
990 }
991 
parseInt(const UString & s,int radix)992 double parseInt(const UString &s, int radix)
993 {
994     int length = s.size();
995     int p = 0;
996 
997     while (p < length && CommonUnicode::isStrWhiteSpace(s[p].uc)) {
998         ++p;
999     }
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 
parseFloat(const UString & s)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 
callAsFunction(ExecState * exec,JSObject *,const List & args)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 
1257