25 #include "interpreter.h"
26 #include "operations.h"
27 #include "array_object.h"
29 #include "error_object.h"
31 #include "array_object.lut.h"
37 #define MAX_INDEX 4294967294U // 2^32-2
43 const unsigned sparseArrayCutoff = 10000;
45 const ClassInfo ArrayInstanceImp::info = {
"Array", 0, 0, 0};
47 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto,
unsigned initialLength)
49 , length(initialLength)
50 , storageLength(initialLength < sparseArrayCutoff ? initialLength : 0)
51 , capacity(storageLength)
56 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto,
const List &list)
59 , storageLength(length)
60 , capacity(storageLength)
65 for (
unsigned i = 0; i < l; ++i) {
66 storage[i] = (it++).imp();
70 ArrayInstanceImp::~ArrayInstanceImp()
77 if (propertyName == lengthPropertyName)
81 unsigned index = propertyName.toArrayIndex(&ok);
85 if (index < storageLength) {
91 return ObjectImp::get(exec, propertyName);
97 if (index > MAX_INDEX)
98 return ObjectImp::get(exec, Identifier::from(index));
101 if (index < storageLength) {
106 return ObjectImp::get(exec, Identifier::from(index));
112 if (propertyName == lengthPropertyName) {
113 unsigned int newLen = value.
toUInt32(exec);
114 if (value.
toNumber(exec) != double(newLen)) {
115 Object err = Error::create(exec, RangeError,
"Invalid array length.");
116 exec->setException(err);
119 setLength(newLen, exec);
124 unsigned index = propertyName.toArrayIndex(&ok);
126 putPropertyByIndex(exec, index, value, attr);
130 ObjectImp::put(exec, propertyName, value, attr);
133 void ArrayInstanceImp::putPropertyByIndex(
ExecState *exec,
unsigned index,
134 const Value &value,
int attr)
136 if (index < sparseArrayCutoff && index >= storageLength) {
137 resizeStorage(index + 1);
140 if (index >= length && index <= MAX_INDEX) {
144 if (index < storageLength) {
145 storage[index] = value.imp();
149 assert(index >= sparseArrayCutoff);
150 ObjectImp::put(exec, Identifier::from(index), value, attr);
155 if (propertyName == lengthPropertyName)
159 unsigned index = propertyName.toArrayIndex(&ok);
163 if (index < storageLength) {
165 return v && v != UndefinedImp::staticUndefined;
169 return ObjectImp::hasProperty(exec, propertyName);
172 bool ArrayInstanceImp::hasPropertyByIndex(
ExecState *exec,
unsigned index)
const
174 if (index > MAX_INDEX)
175 return ObjectImp::hasProperty(exec, Identifier::from(index));
178 if (index < storageLength) {
180 return v && v != UndefinedImp::staticUndefined;
183 return ObjectImp::hasProperty(exec, Identifier::from(index));
188 if (propertyName == lengthPropertyName)
192 unsigned index = propertyName.toArrayIndex(&ok);
196 if (index < storageLength) {
202 return ObjectImp::deleteProperty(exec, propertyName);
205 bool ArrayInstanceImp::deletePropertyByIndex(
ExecState *exec,
unsigned index)
207 if (index > MAX_INDEX)
208 return ObjectImp::deleteProperty(exec, Identifier::from(index));
211 if (index < storageLength) {
216 return ObjectImp::deleteProperty(exec, Identifier::from(index));
221 ReferenceList properties = ObjectImp::propList(exec,recursive);
224 ValueImp *undefined = UndefinedImp::staticUndefined;
226 for (
unsigned i = 0; i < storageLength; ++i) {
228 if (imp && imp != undefined && !ObjectImp::hasProperty(exec,Identifier::from(i))) {
235 void ArrayInstanceImp::resizeStorage(
unsigned newLength)
237 if (newLength < storageLength) {
238 memset(storage + newLength, 0,
sizeof(
ValueImp *) * (storageLength - newLength));
240 if (newLength > capacity) {
241 unsigned newCapacity;
242 if (newLength > sparseArrayCutoff) {
243 newCapacity = newLength;
245 newCapacity = (newLength * 3 + 1) / 2;
246 if (newCapacity > sparseArrayCutoff) {
247 newCapacity = sparseArrayCutoff;
251 memset(storage + capacity, 0,
sizeof(
ValueImp *) * (newCapacity - capacity));
252 capacity = newCapacity;
254 storageLength = newLength;
257 void ArrayInstanceImp::setLength(
unsigned newLength,
ExecState *exec)
259 if (newLength <= storageLength) {
260 resizeStorage(newLength);
263 if (newLength < length) {
266 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties,
Object(
this));
269 while (it != sparseProperties.end()) {
273 if (ok && index > newLength) {
274 ref.deleteValue(exec);
282 void ArrayInstanceImp::mark()
285 unsigned l = storageLength;
286 for (
unsigned i = 0; i < l; ++i) {
288 if (imp && !imp->marked())
293 static ExecState *execForCompareByStringForQSort;
295 static int compareByStringForQSort(
const void *a,
const void *b)
297 ExecState *exec = execForCompareByStringForQSort;
300 if (va->dispatchType() == UndefinedType) {
301 return vb->dispatchType() == UndefinedType ? 0 : 1;
303 if (vb->dispatchType() == UndefinedType) {
306 return compare(va->dispatchToString(exec), vb->dispatchToString(exec));
309 void ArrayInstanceImp::sort(
ExecState *exec)
311 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
313 execForCompareByStringForQSort = exec;
314 qsort(storage, lengthNotIncludingUndefined,
sizeof(
ValueImp *), compareByStringForQSort);
315 execForCompareByStringForQSort = 0;
320 struct CompareWithCompareFunctionArguments {
321 CompareWithCompareFunctionArguments(
ExecState *e, ObjectImp *cf)
323 , compareFunction(cf)
324 , globalObject(e->dynamicInterpreter()->globalObject())
331 ObjectImp *compareFunction;
338 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
340 static int compareWithCompareFunctionForQSort(
const void *a,
const void *b)
342 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
346 if (va->dispatchType() == UndefinedType) {
347 return vb->dispatchType() == UndefinedType ? 0 : 1;
349 if (vb->dispatchType() == UndefinedType) {
353 args->arguments.clear();
354 args->arguments.append(va);
355 args->arguments.append(vb);
356 double compareResult = args->compareFunction->call
357 (args->exec, args->globalObject, args->arguments).toNumber(args->exec);
358 return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
363 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
365 CompareWithCompareFunctionArguments args(exec, compareFunction.imp());
366 compareWithCompareFunctionArguments = &args;
367 qsort(storage, lengthNotIncludingUndefined,
sizeof(
ValueImp *), compareWithCompareFunctionForQSort);
368 compareWithCompareFunctionArguments = 0;
371 unsigned ArrayInstanceImp::pushUndefinedObjectsToEnd(
ExecState *exec)
373 ValueImp *undefined = UndefinedImp::staticUndefined;
377 for (
unsigned i = 0; i != storageLength; ++i) {
379 if (v && v != undefined) {
387 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties,
Object(
this));
388 unsigned newLength = o + sparseProperties.length();
390 if (newLength > storageLength) {
391 resizeStorage(newLength);
395 while (it != sparseProperties.end()) {
397 storage[o] = ref.
getValue(exec).imp();
402 if (newLength != storageLength)
403 memset(storage + o, 0,
sizeof(
ValueImp *) * (storageLength - o));
410 const ClassInfo ArrayPrototypeImp::info = {
"Array", &ArrayInstanceImp::info, &arrayTable, 0};
430 ArrayPrototypeImp::ArrayPrototypeImp(
ExecState *,
431 ObjectPrototypeImp *objProto)
432 : ArrayInstanceImp(objProto, 0)
435 setInternalValue(
Null());
441 return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this );
446 ArrayProtoFuncImp::ArrayProtoFuncImp(
ExecState *exec,
int i,
int len)
448 static_cast<
FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
452 put(exec,lengthPropertyName,
Number(len),DontDelete|ReadOnly|DontEnum);
455 bool ArrayProtoFuncImp::implementsCall()
const
463 unsigned int length = thisObj.
get(exec,lengthPropertyName).
toUInt32(exec);
470 if (!thisObj.inherits(&ArrayInstanceImp::info)) {
471 Object err = Error::create(exec,TypeError);
472 exec->setException(err);
481 if (
id == Join && args.
size() > 0 && !args[0].isA(UndefinedType))
482 separator = args[0].toString(exec);
483 for (
unsigned int k = 0; k < length; k++) {
487 Value element = thisObj.
get(exec, k);
488 if (element.
type() == UndefinedType || element.
type() == NullType)
491 bool fallback =
false;
492 if (
id == ToLocaleString) {
494 Object conversionFunction =
495 Object::dynamicCast(o.
get(exec, toLocaleStringPropertyName));
496 if (conversionFunction.
isValid() &&
504 if (
id == ToString ||
id == Join || fallback) {
505 if (element.
type() == ObjectType) {
506 Object o = Object::dynamicCast(element);
507 Object conversionFunction =
508 Object::dynamicCast(o.
get(exec, toStringPropertyName));
509 if (conversionFunction.
isValid() &&
515 Object error = Error::create(exec, RangeError,
517 exec->setException(error);
524 if ( exec->hadException() )
533 Value curArg = thisObj;
534 Object curObj = Object::dynamicCast(thisObj);
537 if (curArg.
type() == ObjectType &&
538 curObj.inherits(&ArrayInstanceImp::info)) {
542 length = curObj.
get(exec,lengthPropertyName).
toUInt32(exec);
545 arr.
put(exec, n, curObj.
get(exec, k));
550 arr.
put(exec, n, curArg);
553 if (it == args.
end())
556 curObj = Object::dynamicCast(it++);
558 arr.
put(exec,lengthPropertyName,
Number(n), DontEnum | DontDelete);
565 thisObj.
put(exec, lengthPropertyName,
Number(length), DontEnum | DontDelete);
568 result = thisObj.
get(exec, length - 1);
569 thisObj.
put(exec, lengthPropertyName,
Number(length - 1), DontEnum | DontDelete);
574 for (
int n = 0; n < args.
size(); n++)
575 thisObj.
put(exec, length + n, args[n]);
576 length += args.
size();
577 thisObj.
put(exec,lengthPropertyName,
Number(length), DontEnum | DontDelete);
583 unsigned int middle = length / 2;
585 for (
unsigned int k = 0; k < middle; k++) {
586 unsigned lk1 = length - k - 1;
591 thisObj.
put(exec, k, obj2);
592 thisObj.
put(exec, lk1, obj);
594 thisObj.
put(exec, k, obj2);
600 thisObj.
put(exec, lk1, obj);
613 thisObj.
put(exec, lengthPropertyName,
Number(length), DontEnum | DontDelete);
616 result = thisObj.
get(exec, 0);
617 for(
unsigned int k = 1; k < length; k++) {
620 thisObj.
put(exec, k-1, obj);
625 thisObj.
put(exec, lengthPropertyName,
Number(length - 1), DontEnum | DontDelete);
636 if (args[0].type() != UndefinedType) {
637 begin = args[0].toInteger(exec);
639 begin = maxInt( begin + length, 0 );
641 begin = minInt( begin, length );
644 if (args[1].type() != UndefinedType)
646 end = args[1].toInteger(exec);
648 end = maxInt( end + length, 0 );
650 end = minInt( end, length );
655 for(
int k = begin; k < end; k++, n++) {
658 resObj.
put(exec, n, obj);
661 resObj.
put(exec, lengthPropertyName,
Number(n), DontEnum | DontDelete);
666 printf(
"KJS Array::Sort length=%d\n", length);
667 for (
unsigned int i = 0 ; i<length ; ++i )
668 printf(
"KJS Array::Sort: %d: %s\n", i, thisObj.
get(exec, i).
toString(exec).
ascii() );
671 bool useSortFunction = (args[0].type() != UndefinedType);
674 sortFunction = args[0].toObject(exec);
676 useSortFunction =
false;
679 if (thisObj.imp()->classInfo() == &ArrayInstanceImp::info) {
681 ((ArrayInstanceImp *)thisObj.imp())->sort(exec, sortFunction);
683 ((ArrayInstanceImp *)thisObj.imp())->sort(exec);
689 thisObj.
put(exec, lengthPropertyName,
Number(0), DontEnum | DontDelete);
696 for (
unsigned int i = 0 ; i<length-1 ; ++i )
699 unsigned int themin = i;
701 for (
unsigned int j = i+1 ; j<length ; ++j )
705 if (jObj.
type() == UndefinedType) {
707 }
else if (minObj.
type() == UndefinedType) {
709 }
else if (useSortFunction) {
727 thisObj.
put( exec, i, minObj );
728 thisObj.
put( exec, themin, iObj );
732 printf(
"KJS Array::Sort -- Resulting array:\n");
733 for (
unsigned int i = 0 ; i<length ; ++i )
734 printf(
"KJS Array::Sort: %d: %s\n", i, thisObj.
get(exec, i).
toString(exec).
ascii() );
743 int begin = args[0].toUInt32(exec);
745 begin = maxInt( begin + length, 0 );
747 begin = minInt( begin, length );
748 unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin );
751 for(
unsigned int k = 0; k < deleteCount; k++) {
753 Value obj = thisObj.
get(exec, k+begin);
754 resObj.
put(exec, k, obj);
757 resObj.
put(exec, lengthPropertyName,
Number(deleteCount), DontEnum | DontDelete);
759 unsigned int additionalArgs = maxInt( args.
size() - 2, 0 );
760 if ( additionalArgs != deleteCount )
762 if ( additionalArgs < deleteCount )
764 for (
unsigned int k = begin; k < length - deleteCount; ++k )
767 Value obj = thisObj.
get(exec, k+deleteCount);
768 thisObj.
put(exec, k+additionalArgs, obj);
773 for (
unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
778 for (
unsigned int k = length - deleteCount; (int)k > begin; --k )
781 Value obj = thisObj.
get(exec, k+deleteCount-1);
782 thisObj.
put(exec, k+additionalArgs-1, obj);
789 for (
unsigned int k = 0; k < additionalArgs; ++k )
791 thisObj.
put(exec, k+begin, args[k+2]);
793 thisObj.
put(exec, lengthPropertyName,
Number(length - deleteCount + additionalArgs), DontEnum | DontDelete);
797 unsigned int nrArgs = args.
size();
798 for (
unsigned int k = length; k > 0; --k )
802 thisObj.
put(exec, k+nrArgs-1, obj);
807 for (
unsigned int k = 0; k < nrArgs; ++k )
808 thisObj.
put(exec, k, args[k]);
809 result =
Number(length + nrArgs);
810 thisObj.
put(exec, lengthPropertyName, result, DontEnum | DontDelete);
822 ArrayObjectImp::ArrayObjectImp(
ExecState *exec,
824 ArrayPrototypeImp *arrayProto)
829 put(exec,prototypePropertyName,
Object(arrayProto), DontEnum|DontDelete|ReadOnly);
832 put(exec,lengthPropertyName,
Number(1), ReadOnly|DontDelete|DontEnum);
835 bool ArrayObjectImp::implementsConstruct()
const
844 if (args.
size() == 1 && args[0].type() == NumberType) {
845 unsigned int n = args[0].toUInt32(exec);
846 if (n != args[0].toNumber(exec)) {
847 Object error = Error::create(exec, RangeError,
"Invalid array length.");
848 exec->setException(error);
858 bool ArrayObjectImp::implementsCall()
const
867 return construct(exec,args);
Value objects are act as wrappers ("smart pointers") around ValueImp objects and their descendents...
Type type() const
Returns the type of value.
Base class for all function objects.
Interpreter * dynamicInterpreter() const
Returns the interpreter associated with this execution state.
Value get(ExecState *exec, const Identifier &propertyName) const
Retrieves the specified property from the object.
Object construct(ExecState *exec, const List &args)
Creates a new object based on this object.
Represents an primitive Number value.
An iterator for a ReferenceList.
void append(const Value &val)
Append an object to the end of the list.
Object & globalObject() const
Returns the object that is used as the global object during all script execution performed by this in...
Object builtinArrayPrototype() const
Returns the builtin "Array.prototype" object.
bool deleteProperty(ExecState *exec, const Identifier &propertyName)
Removes the specified property from the object.
Represents an primitive Null value.
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
UString className() const
Returns the class name of the object.
bool hasProperty(ExecState *exec, const Identifier &propertyName) const
Checks to see whether the object (or any object in it's prototype chain) has a property with the spec...
The initial value of Function.prototype (and thus all objects created with the Function constructor) ...
Identifier getPropertyName(ExecState *exec) const
Performs the GetPropertyName type conversion operation on this value (ECMA 8.7)
double toNumber(ExecState *exec) const
Performs the ToNumber type conversion operation on this value (ECMA 9.3)
unsigned int toUInt32(ExecState *exec) const
Performs the ToUInt32 type conversion operation on this value (ECMA 9.6)
const TDEShortcut & end()
ListIterator begin() const
Represents an primitive Undefined value.
Value getValue(ExecState *exec) const
Performs the GetValue type conversion operation on this value (ECMA 8.7.1)
Object toObject(ExecState *exec) const
Performs the ToObject type conversion operation on this value (ECMA 9.9)
bool implementsCall() const
Whether or not the object implements the call() method.
Represents an primitive String value.
Defines a Javascript reference.
ValueImp is the base type for all primitives (Undefined, Null, Boolean, String, Number) and objects i...
A list of Reference objects.
void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr=None)
Sets the specified property.
UString toString(ExecState *exec) const
Performs the ToString type conversion operation on this value (ECMA 9.8)
Object builtinArray() const
Returns the builtin "Array" object.
Value call(ExecState *exec, Object &thisObj, const List &args)
Calls this object as if it is a function.
bool isValid() const
Returns whether or not this is a valid value.
char * ascii() const
Convert the Unicode string to plain ASCII chars chopping of any higher bytes.
Represents the current state of script execution.
Represents an Identifier for a Javascript object.
Iterator for KJS::List objects.