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
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);
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)
const TDEShortcut & end()