errors combined. Various reader->pos bugs. Added Error & NativeError types. Added escape & unescape. Fixed bugs in Array.sort & Date settors. git-svn-id: svn://10.0.0.236/trunk@107343 18797224-902f-48f8-a5cc-f745e15eee43
783 lines
25 KiB
C++
783 lines
25 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the JavaScript 2 Prototype.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU Public License (the "GPL"), in which case the
|
|
* provisions of the GPL are applicable instead of those above.
|
|
* If you wish to allow use of your version of this file only
|
|
* under the terms of the GPL and not to allow others to use your
|
|
* version of this file under the NPL, indicate your decision by
|
|
* deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this
|
|
* file under either the NPL or the GPL.
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
// Turn off warnings about identifiers too long in browser information
|
|
#pragma warning(disable: 4786)
|
|
#pragma warning(disable: 4711)
|
|
#pragma warning(disable: 4710)
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
|
|
#include "parser.h"
|
|
#include "numerics.h"
|
|
#include "js2runtime.h"
|
|
|
|
// this is the IdentifierList passed to the name lookup routines
|
|
#define CURRENT_ATTR (NULL)
|
|
|
|
#include "jsarray.h"
|
|
|
|
namespace JavaScript {
|
|
namespace JS2Runtime {
|
|
|
|
JSValue Array_Constructor(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
JSValue thatValue = thisValue;
|
|
if (thatValue.isNull())
|
|
thatValue = Array_Type->newInstance(cx);
|
|
ASSERT(thatValue.isObject());
|
|
JSObject *thisObj = thatValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
if (argc > 0) {
|
|
if (argc == 1) {
|
|
if (argv[0].isNumber()) {
|
|
uint32 i = (uint32)(argv[0].f64);
|
|
if (i == argv[0].f64)
|
|
arrInst->mLength = i;
|
|
else
|
|
cx->reportError(Exception::rangeError, "Array length too large");
|
|
}
|
|
else {
|
|
arrInst->mLength = 1;
|
|
arrInst->defineVariable(cx, widenCString("0"), (NamespaceList *)(NULL), Property::Enumerable, Object_Type, argv[0]);
|
|
}
|
|
}
|
|
else {
|
|
arrInst->mLength = argc;
|
|
for (uint32 i = 0; i < argc; i++) {
|
|
const String *id = numberToString(i);
|
|
arrInst->defineVariable(cx, *id, (NamespaceList *)(NULL), Property::Enumerable, Object_Type, argv[i]);
|
|
delete id;
|
|
}
|
|
}
|
|
}
|
|
return thatValue;
|
|
}
|
|
|
|
|
|
static JSValue Array_toString(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
|
|
{
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
|
|
ContextStackReplacement csr(cx);
|
|
|
|
if (arrInst->mLength == 0)
|
|
return JSValue(&cx->Empty_StringAtom);
|
|
else {
|
|
String *s = new String();
|
|
for (uint32 i = 0; i < arrInst->mLength; i++) {
|
|
const String *id = numberToString(i);
|
|
arrInst->getProperty(cx, *id, NULL);
|
|
JSValue result = cx->popValue();
|
|
if (!result.isUndefined() && !result.isNull())
|
|
s->append(*result.toString(cx).string);
|
|
if (i < (arrInst->mLength - 1))
|
|
s->append(widenCString(","));
|
|
}
|
|
return JSValue(s);
|
|
}
|
|
|
|
}
|
|
|
|
static JSValue Array_toSource(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
|
|
{
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
|
|
ContextStackReplacement csr(cx);
|
|
|
|
if (arrInst->mLength == 0)
|
|
return JSValue(new String(widenCString("[]")));
|
|
else {
|
|
String *s = new String(widenCString("["));
|
|
for (uint32 i = 0; i < arrInst->mLength; i++) {
|
|
const String *id = numberToString(i);
|
|
arrInst->getProperty(cx, *id, NULL);
|
|
JSValue result = cx->popValue();
|
|
if (!result.isUndefined())
|
|
s->append(*result.toString(cx).string);
|
|
if (i < (arrInst->mLength - 1))
|
|
s->append(widenCString(", "));
|
|
}
|
|
s->append(widenCString("]"));
|
|
return JSValue(s);
|
|
}
|
|
|
|
}
|
|
|
|
static JSValue Array_push(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
|
|
for (uint32 i = 0; i < argc; i++) {
|
|
const String *id = numberToString(i + arrInst->mLength);
|
|
arrInst->defineVariable(cx, *id, (NamespaceList *)(NULL), Property::NoAttribute, Object_Type, argv[i]);
|
|
delete id;
|
|
}
|
|
arrInst->mLength += argc;
|
|
return JSValue((float64)arrInst->mLength);
|
|
}
|
|
|
|
static JSValue Array_pop(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
|
|
{
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
|
|
ContextStackReplacement csr(cx);
|
|
|
|
if (arrInst->mLength > 0) {
|
|
const String *id = numberToString(arrInst->mLength - 1);
|
|
arrInst->getProperty(cx, *id, NULL);
|
|
JSValue result = cx->popValue();
|
|
arrInst->deleteProperty(*id, NULL);
|
|
--arrInst->mLength;
|
|
delete id;
|
|
return result;
|
|
}
|
|
else
|
|
return kUndefinedValue;
|
|
}
|
|
|
|
JSValue Array_concat(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
JSValue E = thisValue;
|
|
|
|
JSArrayInstance *A = (JSArrayInstance *)(Array_Type->newInstance(cx));
|
|
uint32 n = 0;
|
|
uint32 i = 0;
|
|
|
|
ContextStackReplacement csr(cx);
|
|
|
|
do {
|
|
if (E.getType() != Array_Type) {
|
|
const String *id = numberToString(n++);
|
|
A->setProperty(cx, *id, CURRENT_ATTR, E);
|
|
}
|
|
else {
|
|
ASSERT(E.isObject());
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(E.object);
|
|
for (uint32 k = 0; k < arrInst->mLength; k++) {
|
|
const String *id = numberToString(k);
|
|
arrInst->getProperty(cx, *id, NULL);
|
|
JSValue result = cx->popValue();
|
|
id = numberToString(n++);
|
|
A->setProperty(cx, *id, CURRENT_ATTR, result);
|
|
}
|
|
}
|
|
E = argv[i++];
|
|
} while (i <= argc);
|
|
|
|
return JSValue(A);
|
|
}
|
|
|
|
static JSValue Array_join(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
ContextStackReplacement csr(cx);
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
|
|
const String *separator;
|
|
if (argc == 0)
|
|
separator = new String(',', 1);
|
|
else
|
|
separator = argv[0].toString(cx).string;
|
|
|
|
thisObj->getProperty(cx, *numberToString(0), CURRENT_ATTR);
|
|
result = cx->popValue();
|
|
|
|
String *S = new String();
|
|
|
|
for (uint32 k = 0; k < length; k++) {
|
|
thisObj->getProperty(cx, *numberToString(0), CURRENT_ATTR);
|
|
result = cx->popValue();
|
|
if (!result.isUndefined() && !result.isNull())
|
|
*S += *result.toString(cx).string;
|
|
|
|
if (k < (length - 1))
|
|
*S += *separator;
|
|
}
|
|
|
|
return JSValue(S);
|
|
}
|
|
|
|
static JSValue Array_reverse(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
|
|
{
|
|
ContextStackReplacement csr(cx);
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
|
|
uint32 halfway = length / 2;
|
|
|
|
for (uint32 k = 0; k < halfway; k++) {
|
|
const String *id1 = numberToString(k);
|
|
const String *id2 = numberToString(length - k - 1);
|
|
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
if (thisObj->hasOwnProperty(*id2, CURRENT_ATTR, Read, &it)) {
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
thisObj->getProperty(cx, *id2, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id1, CURRENT_ATTR, cx->popValue());
|
|
thisObj->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
else {
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
thisObj->deleteProperty(*id1, CURRENT_ATTR);
|
|
}
|
|
}
|
|
else {
|
|
if (thisObj->hasOwnProperty(*id2, CURRENT_ATTR, Read, &it)) {
|
|
thisObj->getProperty(cx, *id2, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id1, CURRENT_ATTR, cx->popValue());
|
|
thisObj->deleteProperty(*id2, CURRENT_ATTR);
|
|
}
|
|
else {
|
|
thisObj->deleteProperty(*id1, CURRENT_ATTR);
|
|
thisObj->deleteProperty(*id2, CURRENT_ATTR);
|
|
}
|
|
}
|
|
}
|
|
|
|
return thisValue;
|
|
}
|
|
|
|
static JSValue Array_shift(Context *cx, const JSValue& thisValue, JSValue * /*argv*/, uint32 /*argc*/)
|
|
{
|
|
ContextStackReplacement csr(cx);
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
|
|
if (length == 0) {
|
|
thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, result);
|
|
return kUndefinedValue;
|
|
}
|
|
|
|
thisObj->getProperty(cx, *numberToString(0), CURRENT_ATTR);
|
|
result = cx->popValue();
|
|
|
|
for (uint32 k = 1; k < length; k++) {
|
|
const String *id1 = numberToString(k);
|
|
const String *id2 = numberToString(k - 1);
|
|
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
else
|
|
thisObj->deleteProperty(*id2, CURRENT_ATTR);
|
|
}
|
|
|
|
thisObj->deleteProperty(*numberToString(length - 1), CURRENT_ATTR);
|
|
thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)(length - 1)) );
|
|
|
|
return result;
|
|
}
|
|
|
|
static JSValue Array_slice(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
ContextStackReplacement csr(cx);
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
|
|
JSArrayInstance *A = (JSArrayInstance *)Array_Type->newInstance(cx);
|
|
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
|
|
uint32 start, end;
|
|
if (argc < 1)
|
|
start = 0;
|
|
else {
|
|
int32 arg0 = (int32)(argv[0].toInt32(cx).f64);
|
|
if (arg0 < 0) {
|
|
arg0 += length;
|
|
if (arg0 < 0)
|
|
start = 0;
|
|
else
|
|
start = toUInt32(arg0);
|
|
}
|
|
else {
|
|
if (toUInt32(arg0) >= length) // cast ok since > 0
|
|
start = length;
|
|
else
|
|
start = toUInt32(arg0);
|
|
}
|
|
}
|
|
|
|
if (argc < 2)
|
|
end = length;
|
|
else {
|
|
int32 arg1 = (int32)(argv[1].toInt32(cx).f64);
|
|
if (arg1 < 0) {
|
|
arg1 += length;
|
|
if (arg1 < 0)
|
|
end = 0;
|
|
else
|
|
end = toUInt32(arg1);
|
|
}
|
|
else {
|
|
if (toUInt32(arg1) >= length)
|
|
end = length;
|
|
else
|
|
end = toUInt32(arg1);
|
|
}
|
|
}
|
|
|
|
uint32 n = 0;
|
|
while (start < end) {
|
|
const String *id1 = numberToString(start);
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
const String *id2 = numberToString(n);
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
A->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
n++;
|
|
start++;
|
|
}
|
|
A->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)n) );
|
|
return JSValue(A);
|
|
}
|
|
|
|
typedef struct CompareArgs {
|
|
Context *context;
|
|
JSFunction *target;
|
|
} CompareArgs;
|
|
|
|
typedef struct QSortArgs {
|
|
JSValue *vec;
|
|
JSValue *pivot;
|
|
CompareArgs *arg;
|
|
} QSortArgs;
|
|
|
|
static int32 sort_compare(JSValue *a, JSValue *b, CompareArgs *arg);
|
|
|
|
static void
|
|
js_qsort_r(QSortArgs *qa, int lo, int hi)
|
|
{
|
|
JSValue *pivot, *vec, *a, *b;
|
|
int i, j, lohi, hilo;
|
|
|
|
CompareArgs *arg;
|
|
|
|
pivot = qa->pivot;
|
|
vec = qa->vec;
|
|
arg = qa->arg;
|
|
|
|
while (lo < hi) {
|
|
i = lo;
|
|
j = hi;
|
|
a = vec + i;
|
|
*pivot = *a;
|
|
while (i < j) {
|
|
b = vec + j;
|
|
if (sort_compare(b, pivot, arg) >= 0) {
|
|
j--;
|
|
continue;
|
|
}
|
|
*a = *b;
|
|
while (sort_compare(a, pivot, arg) <= 0) {
|
|
i++;
|
|
a = vec + i;
|
|
if (i == j)
|
|
goto store_pivot;
|
|
}
|
|
*b = *a;
|
|
}
|
|
if (i > lo) {
|
|
store_pivot:
|
|
*a = *pivot;
|
|
}
|
|
if (i - lo < hi - i) {
|
|
lohi = i - 1;
|
|
if (lo < lohi)
|
|
js_qsort_r(qa, lo, lohi);
|
|
lo = i + 1;
|
|
} else {
|
|
hilo = i + 1;
|
|
if (hilo < hi)
|
|
js_qsort_r(qa, hilo, hi);
|
|
hi = i - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void js_qsort(JSValue *vec, size_t nel, CompareArgs *arg)
|
|
{
|
|
JSValue *pivot;
|
|
QSortArgs qa;
|
|
|
|
pivot = new JSValue();
|
|
qa.vec = vec;
|
|
qa.pivot = pivot;
|
|
qa.arg = arg;
|
|
js_qsort_r(&qa, 0, (int)(nel - 1));
|
|
delete(pivot);
|
|
}
|
|
|
|
static int32 sort_compare(JSValue *a, JSValue *b, CompareArgs *arg)
|
|
{
|
|
JSValue av = *(const JSValue *)a;
|
|
JSValue bv = *(const JSValue *)b;
|
|
CompareArgs *ca = (CompareArgs *) arg;
|
|
Context *cx = ca->context;
|
|
int32 result;
|
|
|
|
if (ca->target == NULL) {
|
|
if (av.isUndefined() || bv.isUndefined()) {
|
|
/* Put undefined properties at the end. */
|
|
result = (av.isUndefined()) ? 1 : -1;
|
|
}
|
|
else {
|
|
const String *astr = av.toString(cx).string;
|
|
const String *bstr = bv.toString(cx).string;
|
|
result = astr->compare(*bstr);
|
|
}
|
|
return result;
|
|
}
|
|
else {
|
|
JSValue argv[2];
|
|
argv[0] = av;
|
|
argv[1] = bv;
|
|
JSValue v = cx->invokeFunction(ca->target, kNullValue, argv, 2);
|
|
float64 f = v.toNumber(cx).f64;
|
|
if (JSDOUBLE_IS_NaN(f) || (f == 0))
|
|
result = 0;
|
|
else
|
|
if (f > 0)
|
|
result = 1;
|
|
else
|
|
result = -1;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
static JSValue Array_sort(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
ASSERT(thisValue.isObject());
|
|
ContextStackReplacement csr(cx);
|
|
|
|
CompareArgs ca;
|
|
ca.context = cx;
|
|
ca.target = NULL;
|
|
|
|
if (argc > 0) {
|
|
if (!argv[0].isUndefined()) {
|
|
if (!argv[0].isFunction())
|
|
cx->reportError(Exception::typeError, "sort needs a compare function");
|
|
ca.target = argv[0].function;
|
|
}
|
|
}
|
|
|
|
JSObject *thisObj = thisValue.object;
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
|
|
if (length > 0) {
|
|
uint32 i;
|
|
JSValue *vec = new JSValue[length];
|
|
|
|
for (i = 0; i < length; i++) {
|
|
const String *id = numberToString(i);
|
|
thisObj->getProperty(cx, *id, CURRENT_ATTR);
|
|
vec[i] = cx->popValue();
|
|
delete id;
|
|
}
|
|
|
|
js_qsort(vec, length, &ca);
|
|
|
|
for (i = 0; i < length; i++) {
|
|
const String *id = numberToString(i);
|
|
thisObj->setProperty(cx, *id, CURRENT_ATTR, vec[i]);
|
|
delete id;
|
|
}
|
|
delete[] vec;
|
|
}
|
|
return thisValue;
|
|
}
|
|
|
|
static JSValue Array_splice(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
if (argc > 1) {
|
|
uint32 k;
|
|
ContextStackReplacement csr(cx);
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
|
|
JSArrayInstance *A = (JSArrayInstance *)Array_Type->newInstance(cx);
|
|
|
|
int32 arg0 = (int32)(argv[0].toInt32(cx).f64);
|
|
uint32 start;
|
|
if (arg0 < 0) {
|
|
arg0 += length;
|
|
if (arg0 < 0)
|
|
start = 0;
|
|
else
|
|
start = toUInt32(arg0);
|
|
}
|
|
else {
|
|
if (toUInt32(arg0) >= length)
|
|
start = length;
|
|
else
|
|
start = toUInt32(arg0);
|
|
}
|
|
|
|
uint32 deleteCount;
|
|
int32 arg1 = (int32)(argv[1].toInt32(cx).f64);
|
|
if (arg1 < 0)
|
|
deleteCount = 0;
|
|
else
|
|
if (toUInt32(arg1) >= (length - start))
|
|
deleteCount = length - start;
|
|
else
|
|
deleteCount = toUInt32(arg1);
|
|
|
|
for (k = 0; k < deleteCount; k++) {
|
|
const String *id1 = numberToString(start + k);
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
const String *id2 = numberToString(k);
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
A->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
}
|
|
A->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)deleteCount) );
|
|
|
|
uint32 newItemCount = argc - 2;
|
|
if (newItemCount < deleteCount) {
|
|
for (k = start; k < (length - deleteCount); k++) {
|
|
const String *id1 = numberToString(k + deleteCount);
|
|
const String *id2 = numberToString(k + newItemCount);
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
else
|
|
thisObj->deleteProperty(*id2, CURRENT_ATTR);
|
|
}
|
|
for (k = length; k > (length - deleteCount + newItemCount); k--) {
|
|
const String *id1 = numberToString(k - 1);
|
|
thisObj->deleteProperty(*id1, CURRENT_ATTR);
|
|
}
|
|
}
|
|
else {
|
|
if (newItemCount > deleteCount) {
|
|
for (k = length - deleteCount; k > start; k--) {
|
|
const String *id1 = numberToString(k + deleteCount - 1);
|
|
const String *id2 = numberToString(k + newItemCount - 1);
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
else
|
|
thisObj->deleteProperty(*id2, CURRENT_ATTR);
|
|
}
|
|
}
|
|
}
|
|
k = start;
|
|
for (uint32 i = 2; i < argc; i++) {
|
|
const String *id1 = numberToString(k++);
|
|
thisObj->setProperty(cx, *id1, CURRENT_ATTR, argv[i]);
|
|
}
|
|
thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)(length - deleteCount + newItemCount)) );
|
|
|
|
return JSValue(A);
|
|
}
|
|
return kUndefinedValue;
|
|
}
|
|
|
|
static JSValue Array_unshift(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
ContextStackReplacement csr(cx);
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
thisObj->getProperty(cx, cx->Length_StringAtom, CURRENT_ATTR);
|
|
JSValue result = cx->popValue();
|
|
uint32 length = (uint32)(result.toUInt32(cx).f64);
|
|
uint32 k;
|
|
|
|
for (k = length; k > 0; k--) {
|
|
const String *id1 = numberToString(k - 1);
|
|
const String *id2 = numberToString(k + argc - 1);
|
|
PropertyIterator it;
|
|
if (thisObj->hasOwnProperty(*id1, CURRENT_ATTR, Read, &it)) {
|
|
thisObj->getProperty(cx, *id1, CURRENT_ATTR);
|
|
thisObj->setProperty(cx, *id2, CURRENT_ATTR, cx->popValue());
|
|
}
|
|
else
|
|
thisObj->deleteProperty(*id2, CURRENT_ATTR);
|
|
}
|
|
|
|
for (k = 0; k < argc; k++) {
|
|
const String *id1 = numberToString(k);
|
|
thisObj->setProperty(cx, *id1, CURRENT_ATTR, argv[k]);
|
|
}
|
|
thisObj->setProperty(cx, cx->Length_StringAtom, CURRENT_ATTR, JSValue((float64)(length + argc)) );
|
|
|
|
return thisValue;
|
|
}
|
|
|
|
// An index has to pass the test that :
|
|
// ToString(ToUint32(ToString(index))) == ToString(index)
|
|
//
|
|
// the 'ToString(index)' operation is the default behaviour of '[]'
|
|
//
|
|
// isArrayIndex() is called when we know that index is a Number
|
|
//
|
|
static bool isArrayIndex(Context *cx, JSValue &index, uint32 &intIndex)
|
|
{
|
|
ASSERT(index.isNumber());
|
|
JSValue v = index.toUInt32(cx);
|
|
if ((v.f64 == index.f64) && (v.f64 < two32minus1)) {
|
|
intIndex = (uint32)v.f64;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
JSValue Array_GetElement(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
// the 'this' value is the first argument, then the index
|
|
if (argc != 2)
|
|
cx->reportError(Exception::referenceError, "[] only supports single dimension");
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
ASSERT(thisObj->mType == Array_Type);
|
|
|
|
JSValue index = argv[1];
|
|
const String *name = index.toString(cx).string;
|
|
|
|
arrInst->getProperty(cx, *name, NULL);
|
|
return cx->popValue();
|
|
}
|
|
|
|
JSValue Array_SetElement(Context *cx, const JSValue& thisValue, JSValue *argv, uint32 argc)
|
|
{
|
|
// the 'this' value is the first argument, then the rValue, and finally the index
|
|
if (argc != 3)
|
|
cx->reportError(Exception::referenceError, "[]= only supports single dimension");
|
|
|
|
ASSERT(thisValue.isObject());
|
|
JSObject *thisObj = thisValue.object;
|
|
JSArrayInstance *arrInst = checked_cast<JSArrayInstance *>(thisObj);
|
|
ASSERT(thisObj->mType == Array_Type);
|
|
|
|
JSValue index = argv[2];
|
|
const String *name = index.toString(cx).string;
|
|
|
|
if (index.isNumber()) {
|
|
uint32 intIndex;
|
|
if (isArrayIndex(cx, index, intIndex)) {
|
|
PropertyIterator it = thisObj->findNamespacedProperty(*name, NULL);
|
|
if (it == thisObj->mProperties.end())
|
|
thisObj->insertNewProperty(*name, NULL, Property::Enumerable, Object_Type, argv[1]);
|
|
else {
|
|
Property *prop = PROPERTY(it);
|
|
ASSERT(prop->mFlag == ValuePointer);
|
|
*prop->mData.vp = argv[1];
|
|
}
|
|
if (intIndex >= arrInst->mLength)
|
|
arrInst->mLength = intIndex + 1;
|
|
}
|
|
}
|
|
else {
|
|
arrInst->setProperty(cx, *name, NULL, argv[1]);
|
|
}
|
|
return argv[0];
|
|
}
|
|
|
|
|
|
|
|
Context::PrototypeFunctions *getArrayProtos()
|
|
{
|
|
Context::ProtoFunDef arrayProtos[] =
|
|
{
|
|
{ "toString", String_Type, 0, Array_toString },
|
|
{ "toLocaleString", String_Type, 0, Array_toString }, // XXX
|
|
{ "toSource", String_Type, 0, Array_toSource },
|
|
{ "push", Number_Type, 1, Array_push },
|
|
{ "pop", Object_Type, 0, Array_pop },
|
|
{ "concat", Array_Type, 1, Array_concat },
|
|
{ "join", String_Type, 1, Array_join },
|
|
{ "reverse", Array_Type, 0, Array_reverse },
|
|
{ "shift", Object_Type, 0, Array_shift },
|
|
{ "slice", Array_Type, 2, Array_slice },
|
|
{ "sort", Array_Type, 1, Array_sort },
|
|
{ "splice", Array_Type, 2, Array_splice },
|
|
{ "unshift", Number_Type, 1, Array_unshift },
|
|
{ NULL }
|
|
};
|
|
return new Context::PrototypeFunctions(&arrayProtos[0]);
|
|
}
|
|
|
|
}
|
|
}
|