KJS

function.cpp
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 1999-2002 Harri Porten ([email protected])
4  * Copyright (C) 2001 Peter Kelly ([email protected])
5  * Copyright (C) 2003 Apple Computer, Inc.
6  * Copyright (C) 2007 Cameron Zwarich ([email protected])
7  * Copyright (C) 2007 Maksim Orlovich ([email protected])
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 
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 
67 void FunctionImp::mark()
68 {
69  InternalFunctionImp::mark();
70  _scope.mark();
71 }
72 
73 FunctionImp::~FunctionImp()
74 {
75 }
76 
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 
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
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 
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 
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 
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 
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 
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 
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 
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 
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 
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  */
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 
342 bool FunctionImp::implementsConstruct() const
343 {
344  return true;
345 }
346 
347 // ECMA 13.2.2 [[Construct]]
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 
371 Thrower::Thrower(ErrorType type)
372  : JSObject(),
373  m_type(type)
374 {
375 }
376 
377 JSValue *Thrower::callAsFunction(ExecState *exec, JSObject * /*thisObj*/, const List & /*args*/)
378 {
379  return throwError(exec, m_type);
380 }
381 
382 // ------------------------------ BoundFunction ---------------------------------
383 
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
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
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
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 
441 void BoundFunction::setTargetFunction(JSObject *targetFunction)
442 {
443  m_targetFunction = targetFunction;
444 }
445 
446 void BoundFunction::setBoundArgs(const List &boundArgs)
447 {
448  m_boundArgs = boundArgs;
449 }
450 
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 
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 
479 IndexToNameMap::~IndexToNameMap()
480 {
481  delete [] _map;
482 }
483 
484 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 
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 
514 int IndexToNameMap::size() const
515 {
516  return _size;
517 }
518 
519 Identifier &IndexToNameMap::operator[](int index)
520 {
521  return _map[index];
522 }
523 
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
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 
557 void Arguments::mark()
558 {
559  JSObject::mark();
560  if (_activationObject && !_activationObject->marked()) {
561  _activationObject->mark();
562  }
563 }
564 
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 
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 
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 
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 
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 
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
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 
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 
757 void ActivationImp::requestTearOff()
758 {
759  tearOffNeededSlot() = true;
760 }
761 
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 
773 PropertySlot::GetValueFunc ActivationImp::getArgumentsGetter()
774 {
775  return ActivationImp::argumentsGetter;
776 }
777 
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 
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 
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 
824 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 
835 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 
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 
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 
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 
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 
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 
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 
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 
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 
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  "!#$&'()*+,-./:;[email protected]_~";
1095  static const char do_not_unescape_when_decoding_URI[] =
1096  "#$&+,/:;[email protected]";
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 
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
KOPENINGHOURS_EXPORT QString currentState(const OpeningHours &oh)
Native list type.
Definition: list.h:52
QString name(StandardShortcut id)
static const List & empty()
Returns a pointer to a static instance of an empty list.
Definition: list.cpp:311
KGuiItem cont()
virtual QVariant callAsFunction(ScriptableExtension *callerPrincipal, quint64 objId, const ArgList &args)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
bool isNaN(double x)
bool isInf(double x)
virtual bool put(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName, const QVariant &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Oct 6 2022 04:21:54 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.