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
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);
Represents the current state of script execution.
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Interpreter * dynamicInterpreter() const
Returns the interpreter associated with this execution state.
The initial value of Function.prototype (and thus all objects created with the Function constructor)
Represents an Identifier for a Javascript object.
Base class for all function objects.
Object builtinArrayPrototype() const
Returns the builtin "Array.prototype" object.
Object builtinArray() const
Returns the builtin "Array" object.
Object & globalObject() const
Returns the object that is used as the global object during all script execution performed by this in...
Iterator for KJS::List objects.
void append(const Value &val)
Append an object to the end of the list.
ListIterator begin() const
Represents an primitive Null value.
Represents an primitive Number value.
bool deleteProperty(ExecState *exec, const Identifier &propertyName)
Removes the specified property from the object.
bool implementsCall() const
Whether or not the object implements the call() method.
Object construct(ExecState *exec, const List &args)
Creates a new object based on this object.
void put(ExecState *exec, const Identifier &propertyName, const Value &value, int attr=None)
Sets the specified property.
Value call(ExecState *exec, Object &thisObj, const List &args)
Calls this object as if it is a function.
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...
Value get(ExecState *exec, const Identifier &propertyName) const
Retrieves the specified property from the object.
UString className() const
Returns the class name of the object.
An iterator for a ReferenceList.
A list of Reference objects.
Defines a Javascript reference.
Identifier getPropertyName(ExecState *exec) const
Performs the GetPropertyName type conversion operation on this value (ECMA 8.7)
Value getValue(ExecState *exec) const
Performs the GetValue type conversion operation on this value (ECMA 8.7.1)
Represents an primitive String value.
char * ascii() const
Convert the Unicode string to plain ASCII chars chopping of any higher bytes.
Represents an primitive Undefined value.
ValueImp is the base type for all primitives (Undefined, Null, Boolean, String, Number) and objects i...
Value objects are act as wrappers ("smart pointers") around ValueImp objects and their descendents.
UString toString(ExecState *exec) const
Performs the ToString type conversion operation on this value (ECMA 9.8)
Object toObject(ExecState *exec) const
Performs the ToObject type conversion operation on this value (ECMA 9.9)
unsigned int toUInt32(ExecState *exec) const
Performs the ToUInt32 type conversion operation on this value (ECMA 9.6)
bool isValid() const
Returns whether or not this is a valid value.
Type type() const
Returns the type of value.
double toNumber(ExecState *exec) const
Performs the ToNumber type conversion operation on this value (ECMA 9.3)