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