26 #include "interpreter.h" 27 #include "operations.h" 28 #include "array_object.h" 30 #include "error_object.h" 32 #include "array_object.lut.h" 38 #define MAX_INDEX 4294967294U // 2^32-2 44 const unsigned sparseArrayCutoff = 10000;
46 const ClassInfo ArrayInstanceImp::info = {
"Array", 0, 0, 0};
48 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto,
unsigned initialLength)
50 , length(initialLength)
51 , storageLength(initialLength < sparseArrayCutoff ? initialLength : 0)
52 , capacity(storageLength)
57 ArrayInstanceImp::ArrayInstanceImp(ObjectImp *proto,
const List &list)
60 , storageLength(length)
61 , capacity(storageLength)
66 for (
unsigned i = 0; i < l; ++i) {
67 storage[i] = (it++).imp();
71 ArrayInstanceImp::~ArrayInstanceImp()
78 if (propertyName == lengthPropertyName)
82 unsigned index = propertyName.toArrayIndex(&ok);
86 if (index < storageLength) {
92 return ObjectImp::get(exec, propertyName);
98 if (index > MAX_INDEX)
99 return ObjectImp::get(exec, Identifier::from(index));
102 if (index < storageLength) {
107 return ObjectImp::get(exec, Identifier::from(index));
113 if (propertyName == lengthPropertyName) {
114 unsigned int newLen = value.
toUInt32(exec);
115 if (value.
toNumber(exec) != double(newLen)) {
116 Object err = Error::create(exec, RangeError,
"Invalid array length.");
117 exec->setException(err);
120 setLength(newLen, exec);
125 unsigned index = propertyName.toArrayIndex(&ok);
127 putPropertyByIndex(exec, index, value, attr);
131 ObjectImp::put(exec, propertyName, value, attr);
134 void ArrayInstanceImp::putPropertyByIndex(
ExecState *exec,
unsigned index,
135 const Value &value,
int attr)
137 if (index < sparseArrayCutoff && index >= storageLength) {
138 resizeStorage(index + 1);
141 if (index >= length && index <= MAX_INDEX) {
145 if (index < storageLength) {
146 storage[index] = value.imp();
150 assert(index >= sparseArrayCutoff);
151 ObjectImp::put(exec, Identifier::from(index), value, attr);
156 if (propertyName == lengthPropertyName)
160 unsigned index = propertyName.toArrayIndex(&ok);
164 if (index < storageLength) {
166 return v && v != UndefinedImp::staticUndefined;
170 return ObjectImp::hasProperty(exec, propertyName);
173 bool ArrayInstanceImp::hasPropertyByIndex(
ExecState *exec,
unsigned index)
const 175 if (index > MAX_INDEX)
176 return ObjectImp::hasProperty(exec, Identifier::from(index));
179 if (index < storageLength) {
181 return v && v != UndefinedImp::staticUndefined;
184 return ObjectImp::hasProperty(exec, Identifier::from(index));
189 if (propertyName == lengthPropertyName)
193 unsigned index = propertyName.toArrayIndex(&ok);
197 if (index < storageLength) {
203 return ObjectImp::deleteProperty(exec, propertyName);
206 bool ArrayInstanceImp::deletePropertyByIndex(
ExecState *exec,
unsigned index)
208 if (index > MAX_INDEX)
209 return ObjectImp::deleteProperty(exec, Identifier::from(index));
212 if (index < storageLength) {
217 return ObjectImp::deleteProperty(exec, Identifier::from(index));
222 ReferenceList properties = ObjectImp::propList(exec,recursive);
225 ValueImp *undefined = UndefinedImp::staticUndefined;
227 for (
unsigned i = 0; i < storageLength; ++i) {
229 if (imp && imp != undefined && !ObjectImp::hasProperty(exec,Identifier::from(i))) {
236 void ArrayInstanceImp::resizeStorage(
unsigned newLength)
238 if (newLength < storageLength) {
239 memset(storage + newLength, 0,
sizeof(
ValueImp *) * (storageLength - newLength));
241 if (newLength > capacity) {
242 unsigned newCapacity;
243 if (newLength > sparseArrayCutoff) {
244 newCapacity = newLength;
246 newCapacity = (newLength * 3 + 1) / 2;
247 if (newCapacity > sparseArrayCutoff) {
248 newCapacity = sparseArrayCutoff;
252 memset(storage + capacity, 0,
sizeof(
ValueImp *) * (newCapacity - capacity));
253 capacity = newCapacity;
255 storageLength = newLength;
258 void ArrayInstanceImp::setLength(
unsigned newLength,
ExecState *exec)
260 if (newLength <= storageLength) {
261 resizeStorage(newLength);
264 if (newLength < length) {
267 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties,
Object(
this));
270 while (it != sparseProperties.end()) {
274 if (ok && index > newLength) {
275 ref.deleteValue(exec);
283 void ArrayInstanceImp::mark()
286 unsigned l = storageLength;
287 for (
unsigned i = 0; i < l; ++i) {
289 if (imp && !imp->marked())
294 static ExecState *execForCompareByStringForQSort;
296 static int compareByStringForQSort(
const void *a,
const void *b)
298 ExecState *exec = execForCompareByStringForQSort;
301 if (va->dispatchType() == UndefinedType) {
302 return vb->dispatchType() == UndefinedType ? 0 : 1;
304 if (vb->dispatchType() == UndefinedType) {
307 return compare(va->dispatchToString(exec), vb->dispatchToString(exec));
310 void ArrayInstanceImp::sort(
ExecState *exec)
312 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
314 execForCompareByStringForQSort = exec;
315 qsort(storage, lengthNotIncludingUndefined,
sizeof(
ValueImp *), compareByStringForQSort);
316 execForCompareByStringForQSort = 0;
321 struct CompareWithCompareFunctionArguments {
322 CompareWithCompareFunctionArguments(
ExecState *e, ObjectImp *cf)
324 , compareFunction(cf)
325 , globalObject(e->dynamicInterpreter()->globalObject())
332 ObjectImp *compareFunction;
339 static CompareWithCompareFunctionArguments *compareWithCompareFunctionArguments;
341 static int compareWithCompareFunctionForQSort(
const void *a,
const void *b)
343 CompareWithCompareFunctionArguments *args = compareWithCompareFunctionArguments;
347 if (va->dispatchType() == UndefinedType) {
348 return vb->dispatchType() == UndefinedType ? 0 : 1;
350 if (vb->dispatchType() == UndefinedType) {
354 args->arguments.clear();
355 args->arguments.append(va);
356 args->arguments.append(vb);
357 double compareResult = args->compareFunction->call
358 (args->exec, args->globalObject, args->arguments).toNumber(args->exec);
359 return compareResult < 0 ? -1 : compareResult > 0 ? 1 : 0;
364 int lengthNotIncludingUndefined = pushUndefinedObjectsToEnd(exec);
366 CompareWithCompareFunctionArguments args(exec, compareFunction.imp());
367 compareWithCompareFunctionArguments = &args;
368 qsort(storage, lengthNotIncludingUndefined,
sizeof(
ValueImp *), compareWithCompareFunctionForQSort);
369 compareWithCompareFunctionArguments = 0;
372 unsigned ArrayInstanceImp::pushUndefinedObjectsToEnd(
ExecState *exec)
374 ValueImp *undefined = UndefinedImp::staticUndefined;
378 for (
unsigned i = 0; i != storageLength; ++i) {
380 if (v && v != undefined) {
388 _prop.addSparseArrayPropertiesToReferenceList(sparseProperties,
Object(
this));
389 unsigned newLength = o + sparseProperties.length();
391 if (newLength > storageLength) {
392 resizeStorage(newLength);
396 while (it != sparseProperties.end()) {
398 storage[o] = ref.
getValue(exec).imp();
403 if (newLength != storageLength)
404 memset(storage + o, 0,
sizeof(
ValueImp *) * (storageLength - o));
411 const ClassInfo ArrayPrototypeImp::info = {
"Array", &ArrayInstanceImp::info, &arrayTable, 0};
431 ArrayPrototypeImp::ArrayPrototypeImp(
ExecState *,
432 ObjectPrototypeImp *objProto)
433 : ArrayInstanceImp(objProto, 0)
436 setInternalValue(
Null());
442 return lookupGetFunction<ArrayProtoFuncImp, ArrayInstanceImp>( exec, propertyName, &arrayTable, this );
447 ArrayProtoFuncImp::ArrayProtoFuncImp(
ExecState *exec,
int i,
int len)
449 static_cast<
FunctionPrototypeImp*>(exec->lexicalInterpreter()->builtinFunctionPrototype().imp())
453 put(exec,lengthPropertyName,
Number(len),DontDelete|ReadOnly|DontEnum);
456 bool ArrayProtoFuncImp::implementsCall()
const 464 unsigned int length = thisObj.
get(exec,lengthPropertyName).
toUInt32(exec);
471 if (!thisObj.inherits(&ArrayInstanceImp::info)) {
472 Object err = Error::create(exec,TypeError);
473 exec->setException(err);
482 if (
id == Join && args.
size() > 0 && !args[0].isA(UndefinedType))
483 separator = args[0].toString(exec);
484 for (
unsigned int k = 0; k < length; k++) {
488 Value element = thisObj.
get(exec, k);
489 if (element.
type() == UndefinedType || element.
type() == NullType)
492 bool fallback =
false;
493 if (
id == ToLocaleString) {
495 Object conversionFunction =
496 Object::dynamicCast(o.
get(exec, toLocaleStringPropertyName));
497 if (conversionFunction.
isValid() &&
505 if (
id == ToString ||
id == Join || fallback) {
506 if (element.
type() == ObjectType) {
507 Object o = Object::dynamicCast(element);
508 Object conversionFunction =
509 Object::dynamicCast(o.
get(exec, toStringPropertyName));
510 if (conversionFunction.
isValid() &&
516 Object error = Error::create(exec, RangeError,
518 exec->setException(error);
525 if ( exec->hadException() )
534 Value curArg = thisObj;
535 Object curObj = Object::dynamicCast(thisObj);
538 if (curArg.
type() == ObjectType &&
539 curObj.inherits(&ArrayInstanceImp::info)) {
543 length = curObj.
get(exec,lengthPropertyName).
toUInt32(exec);
546 arr.
put(exec, n, curObj.
get(exec, k));
551 arr.
put(exec, n, curArg);
554 if (it == args.
end())
557 curObj = Object::dynamicCast(it++);
559 arr.
put(exec,lengthPropertyName,
Number(n), DontEnum | DontDelete);
566 thisObj.
put(exec, lengthPropertyName,
Number(length), DontEnum | DontDelete);
569 result = thisObj.
get(exec, length - 1);
570 thisObj.
put(exec, lengthPropertyName,
Number(length - 1), DontEnum | DontDelete);
575 for (
int n = 0; n < args.
size(); n++)
576 thisObj.
put(exec, length + n, args[n]);
577 length += args.
size();
578 thisObj.
put(exec,lengthPropertyName,
Number(length), DontEnum | DontDelete);
584 unsigned int middle = length / 2;
586 for (
unsigned int k = 0; k < middle; k++) {
587 unsigned lk1 = length - k - 1;
592 thisObj.
put(exec, k, obj2);
593 thisObj.
put(exec, lk1, obj);
595 thisObj.
put(exec, k, obj2);
601 thisObj.
put(exec, lk1, obj);
614 thisObj.
put(exec, lengthPropertyName,
Number(length), DontEnum | DontDelete);
617 result = thisObj.
get(exec, 0);
618 for(
unsigned int k = 1; k < length; k++) {
621 thisObj.
put(exec, k-1, obj);
626 thisObj.
put(exec, lengthPropertyName,
Number(length - 1), DontEnum | DontDelete);
637 if (args[0].type() != UndefinedType) {
638 begin = args[0].toInteger(exec);
640 begin = maxInt( begin + length, 0 );
642 begin = minInt( begin, length );
645 if (args[1].type() != UndefinedType)
647 end = args[1].toInteger(exec);
649 end = maxInt( end + length, 0 );
651 end = minInt( end, length );
656 for(
int k = begin; k < end; k++, n++) {
659 resObj.
put(exec, n, obj);
662 resObj.
put(exec, lengthPropertyName,
Number(n), DontEnum | DontDelete);
667 printf(
"KJS Array::Sort length=%d\n", length);
668 for (
unsigned int i = 0 ; i<length ; ++i )
669 printf(
"KJS Array::Sort: %d: %s\n", i, thisObj.
get(exec, i).
toString(exec).
ascii() );
672 bool useSortFunction = (args[0].type() != UndefinedType);
675 sortFunction = args[0].toObject(exec);
677 useSortFunction =
false;
680 if (thisObj.imp()->classInfo() == &ArrayInstanceImp::info) {
682 ((ArrayInstanceImp *)thisObj.imp())->sort(exec, sortFunction);
684 ((ArrayInstanceImp *)thisObj.imp())->sort(exec);
690 thisObj.
put(exec, lengthPropertyName,
Number(0), DontEnum | DontDelete);
697 for (
unsigned int i = 0 ; i<length-1 ; ++i )
700 unsigned int themin = i;
702 for (
unsigned int j = i+1 ; j<length ; ++j )
706 if (jObj.
type() == UndefinedType) {
708 }
else if (minObj.
type() == UndefinedType) {
710 }
else if (useSortFunction) {
728 thisObj.
put( exec, i, minObj );
729 thisObj.
put( exec, themin, iObj );
733 printf(
"KJS Array::Sort -- Resulting array:\n");
734 for (
unsigned int i = 0 ; i<length ; ++i )
735 printf(
"KJS Array::Sort: %d: %s\n", i, thisObj.
get(exec, i).
toString(exec).
ascii() );
744 int begin = args[0].toUInt32(exec);
746 begin = maxInt( begin + length, 0 );
748 begin = minInt( begin, length );
749 unsigned int deleteCount = minInt( maxInt( args[1].toUInt32(exec), 0 ), length - begin );
752 for(
unsigned int k = 0; k < deleteCount; k++) {
754 Value obj = thisObj.
get(exec, k+begin);
755 resObj.
put(exec, k, obj);
758 resObj.
put(exec, lengthPropertyName,
Number(deleteCount), DontEnum | DontDelete);
760 unsigned int additionalArgs = maxInt( args.
size() - 2, 0 );
761 if ( additionalArgs != deleteCount )
763 if ( additionalArgs < deleteCount )
765 for (
unsigned int k = begin; k < length - deleteCount; ++k )
768 Value obj = thisObj.
get(exec, k+deleteCount);
769 thisObj.
put(exec, k+additionalArgs, obj);
774 for (
unsigned int k = length ; k > length - deleteCount + additionalArgs; --k )
779 for (
unsigned int k = length - deleteCount; (int)k > begin; --k )
782 Value obj = thisObj.
get(exec, k+deleteCount-1);
783 thisObj.
put(exec, k+additionalArgs-1, obj);
790 for (
unsigned int k = 0; k < additionalArgs; ++k )
792 thisObj.
put(exec, k+begin, args[k+2]);
794 thisObj.
put(exec, lengthPropertyName,
Number(length - deleteCount + additionalArgs), DontEnum | DontDelete);
798 unsigned int nrArgs = args.
size();
799 for (
unsigned int k = length; k > 0; --k )
803 thisObj.
put(exec, k+nrArgs-1, obj);
808 for (
unsigned int k = 0; k < nrArgs; ++k )
809 thisObj.
put(exec, k, args[k]);
810 result =
Number(length + nrArgs);
811 thisObj.
put(exec, lengthPropertyName, result, DontEnum | DontDelete);
823 ArrayObjectImp::ArrayObjectImp(
ExecState *exec,
825 ArrayPrototypeImp *arrayProto)
830 put(exec,prototypePropertyName,
Object(arrayProto), DontEnum|DontDelete|ReadOnly);
833 put(exec,lengthPropertyName,
Number(1), ReadOnly|DontDelete|DontEnum);
836 bool ArrayObjectImp::implementsConstruct()
const 845 if (args.
size() == 1 && args[0].type() == NumberType) {
846 unsigned int n = args[0].toUInt32(exec);
847 if (n != args[0].toNumber(exec)) {
848 Object error = Error::create(exec, RangeError,
"Invalid array length.");
849 exec->setException(error);
859 bool ArrayObjectImp::implementsCall()
const 868 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.