view dmdscript_tango/darray.d @ 4:6d905019f7bf

some changes
author saaadel
date Thu, 28 Jan 2010 21:23:27 +0200
parents 8363a4bf6a8f
children
line wrap: on
line source


/* Digital Mars DMDScript source code.
 * Copyright (c) 2000-2002 by Chromium Communications
 * D version Copyright (c) 2004-2005 by Digital Mars
 * All Rights Reserved
 * written by Walter Bright
 * www.digitalmars.com
 * Use at your own risk. There is no warranty, express or implied.
 * License for redistribution is by the GNU General Public License in gpl.txt.
 *
 * A binary, non-exclusive license for commercial use can be
 * purchased from www.digitalmars.com/dscript/buy.html.
 *
 * DMDScript is implemented in the D Programming Language,
 * www.digitalmars.com/d/
 *
 * For a C++ implementation of DMDScript, including COM support,
 * see www.digitalmars.com/dscript/cppscript.html.
 */


module dmdscript_tango.darray;

//import std.string;
//import std.c.stdlib;
import tango.stdc.stdlib;
import tango.text.Text;

import dmdscript_tango.script;
import dmdscript_tango.value;
import dmdscript_tango.dobject;
import dmdscript_tango.threadcontext;
import dmdscript_tango.identifier;
import dmdscript_tango.dfunction;
import dmdscript_tango.text;
import dmdscript_tango.property;
import dmdscript_tango.textgen.errmsgs;
import dmdscript_tango.dnative;
import dmdscript_tango.program;

/* ===================== Darray_constructor ==================== */

class Darray_constructor : Dfunction
{
    this(ThreadContext *tc)
    {
	super(1, tc.Dfunction_prototype);
	name = "Array";
    }

    void* Construct(CallContext *cc, Value *ret, Value[] arglist)
    {
	// ECMA 15.4.2
	Darray a;

	a = new Darray();
	if (arglist.length == 0)
	{
	    a.ulength = 0;
	    a.length.number = 0;
	}
	else if (arglist.length == 1)
	{
	    Value* v = &arglist[0];

	    if (v.isNumber())
	    {
		d_uint32 len;

		len = v.toUint32();
		if (cast(double) len != v.number)
		{   ErrInfo errinfo;

		    ret.putVundefined();
		    return RangeError(&errinfo, ERR_ARRAY_LEN_OUT_OF_BOUNDS, v.number);
		}
		else
		{   a.ulength = len;
		    a.length.number = len;
		    /+
		    if (len > 16)
		    {
			//writef("setting %p dimension to %d\n", &a.proptable, len);
			if (len > 10000)
			    len = 10000;		// cap so we don't run out of memory
			a.proptable.roots.setDim(len);
			a.proptable.roots.zero();
		    }
		    +/
		}
	    }
	    else
	    {
		a.ulength = 1;
		a.length.number = 1;
		a.Put(cast(d_uint32) 0, v, 0);
	    }
	}
	else
	{
	    //if (arglist.length > 10) writef("Array constructor: arglist.length = %d\n", arglist.length);
	    /+
	    if (arglist.length > 16)
	    {
		a.proptable.roots.setDim(arglist.length);
		a.proptable.roots.zero();
	    }
	    +/
	    a.ulength = arglist.length;
	    a.length.number = arglist.length;
	    for (uint k = 0; k < arglist.length; k++)
	    {
		a.Put(k, &arglist[k], 0);
	    }
	}
	Value.copy(ret, &a.value);
	//writef("Darray_constructor.Construct(): length = %g\n", a.length.number);
	return null;
    }

    void* Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
    {
	// ECMA 15.4.1
	return Construct(cc, ret, arglist);
    }
}


/* ===================== Darray_prototype_toString ================= */

void *Darray_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    //writef("Darray_prototype_toString()\n");
    array_join(othis, ret, null);
    return null;
}

/* ===================== Darray_prototype_toLocaleString ================= */

void *Darray_prototype_toLocaleString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.3
    tchar[] separator;
    d_string r;
    d_uint32 len;
    d_uint32 k;
    Value* v;

    //writef("array_join(othis = %p)\n", othis);

    if (!othis.isClass(TEXT_Array))
    {
	ret.putVundefined();
	ErrInfo errinfo;
	return Dobject.RuntimeError(&errinfo, ERR_TLS_NOT_TRANSFERRABLE);
    }

    v = othis.Get(TEXT_length);
    len = v ? v.toUint32() : 0;

    Program prog = cc.prog;
    if (!prog.slist)
    {
	// Determine what list separator is only once per thread
	//prog.slist = list_separator(prog.lcid);
	prog.slist = ",";
    }
    separator = prog.slist;

    for (k = 0; k != len; k++)
    {
	if (k)
	    r ~= separator;
	v = othis.Get(k);
	if (v && !v.isUndefinedOrNull())
	{   Dobject ot;

	    ot = v.toObject();
	    v = ot.Get(TEXT_toLocaleString);
	    if (v && !v.isPrimitive())	// if it's an Object
	    {   void* a;
		Dobject o;
		Value rt;

		o = v.object;
		rt.putVundefined();
		a = o.Call(cc, ot, &rt, null);
		if (a)			// if exception was thrown
		    return a;
		r ~= rt.toString();
	    }
	}
    }

    ret.putVstring(r);
    return null;
}

/* ===================== Darray_prototype_concat ================= */

void *Darray_prototype_concat(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.4
    Darray A;
    Darray E;
    Value* v;
    d_uint32 k;
    d_uint32 n;
    d_uint32 a;

    A = new Darray();
    n = 0;
    v = &othis.value;
    for (a = 0; ; a++)
    {
	if (!v.isPrimitive() && v.object.isDarray())
	{   d_uint32 len;

	    E = cast(Darray)v.object;
	    len = E.ulength;
	    for (k = 0; k != len; k++)
	    {
		v = E.Get(k);
		if (v)
		    A.Put(n, v, 0);
		n++;
	    }
	}
	else
	{
	    A.Put(n, v, 0);
	    n++;
	}
	if (a == arglist.length)
	    break;
	v = &arglist[a];
    }

    A.Put(TEXT_length, n, DontDelete | DontEnum);
    Value.copy(ret, &A.value);
    return null;
}

/* ===================== Darray_prototype_join ================= */

void *Darray_prototype_join(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    array_join(othis, ret, arglist);
    return null;
}

void array_join(Dobject othis, Value* ret, Value[] arglist)
{
    // ECMA 15.4.4.3
    d_string separator;
    d_string r;
    d_uint32 len;
    d_uint32 k;
    Value* v;

    //writef("array_join(othis = %p)\n", othis);
    v = othis.Get(TEXT_length);
    len = v ? v.toUint32() : 0;
    if (arglist.length == 0)
	separator = TEXT_comma;
    else
	separator = arglist[0].toString();

    for (k = 0; k != len; k++)
    {
	if (k)
	    r ~= separator;
	v = othis.Get(k);
	if (v && !v.isUndefinedOrNull())
	    r ~= v.toString();
    }

    ret.putVstring(r);
}

/* ===================== Darray_prototype_toSource ================= */

void *Darray_prototype_toSource(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    tchar[] separator;
    d_string r;
    d_uint32 len;
    d_uint32 k;
    Value* v;

    v = othis.Get(TEXT_length);
    len = v ? v.toUint32() : 0;
    separator = ",";

    r = "[";
    for (k = 0; k != len; k++)
    {
	if (k)
	    r ~= separator;
	v = othis.Get(k);
	if (v && !v.isUndefinedOrNull())
	    r ~= v.toSource();
    }
    r ~= "]";

    ret.putVstring(r);
    return null;
}


/* ===================== Darray_prototype_pop ================= */

void *Darray_prototype_pop(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.6
    Value* v;
    d_uint32 u;

    // If othis is a Darray, then we can optimize this significantly
    v = othis.Get(TEXT_length);
    if (!v)
	v = &vundefined;
    u = v.toUint32();
    if (u == 0)
    {
	othis.Put(TEXT_length, 0.0, DontDelete | DontEnum);
	ret.putVundefined();
    }
    else
    {
	v = othis.Get(u - 1);
	if (!v)
	    v = &vundefined;
	Value.copy(ret, v);
	othis.Delete(u - 1);
	othis.Put(TEXT_length, u - 1, DontDelete | DontEnum);
    }
    return null;
}

/* ===================== Darray_prototype_push ================= */

void *Darray_prototype_push(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.7
    Value* v;
    d_uint32 u;
    d_uint32 a;

    // If othis is a Darray, then we can optimize this significantly
    v = othis.Get(TEXT_length);
    if (!v)
	v = &vundefined;
    u = v.toUint32();
    for (a = 0; a < arglist.length; a++)
    {
	othis.Put(u + a, &arglist[a], 0);
    }
    othis.Put(TEXT_length, u + a, DontDelete | DontEnum);
    ret.putVnumber(u + a);
    return null;
}

/* ===================== Darray_prototype_reverse ================= */

void *Darray_prototype_reverse(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA 15.4.4.4
    d_uint32 a;
    d_uint32 b;
    Value* va;
    Value* vb;
    Value* v;
    d_uint32 pivot;
    d_uint32 len;
    Value tmp;

    v = othis.Get(TEXT_length);
    len = v ? v.toUint32() : 0;
    pivot = len / 2;
    for (a = 0; a != pivot; a++)
    {
	b = len - a - 1;
	//writef("a = %d, b = %d\n", a, b);
	va = othis.Get(a);
	if (va)
	    Value.copy(&tmp, va);
	vb = othis.Get(b);
	if (vb)
	    othis.Put(a, vb, 0);
	else
	    othis.Delete(a);

	if (va)
	    othis.Put(b, &tmp, 0);
	else
	    othis.Delete(b);
    }
    Value.copy(ret, &othis.value);
    return null;
}

/* ===================== Darray_prototype_shift ================= */

void *Darray_prototype_shift(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.9
    Value* v;
    Value* result;
    d_uint32 len;
    d_uint32 k;

    // If othis is a Darray, then we can optimize this significantly
    //writef("shift(othis = %p)\n", othis);
    v = othis.Get(TEXT_length);
    if (!v)
	v = &vundefined;
    len = v.toUint32();
    if (len)
    {
	result = othis.Get(0u);
	Value.copy(ret, result);
	for (k = 1; k != len; k++)
	{
	    v = othis.Get(k);
	    if (v)
	    {
		othis.Put(k - 1, v, 0);
	    }
	    else
	    {
		othis.Delete(k - 1);
	    }
	}
	othis.Delete(len - 1);
	len--;
    }
    else
	Value.copy(ret, &vundefined);

    othis.Put(TEXT_length, len, DontDelete | DontEnum);
    return null;
}

/* ===================== Darray_prototype_slice ================= */

void *Darray_prototype_slice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.10
    d_uint32 len;
    d_uint32 n;
    d_uint32 k;
    d_uint32 r8;
    d_number start;
    d_number end;
    Value* v;
    Darray A;

    v = othis.Get(TEXT_length);
    if (!v)
	v = &vundefined;
    len = v.toUint32();

    switch (arglist.length)
    {
	case 0:
	    start = vundefined.toUint32();
	    end = start;
	    break;

	case 1:
	    start = arglist[0].toInteger();
	    end = len;
	    break;

	default:
	    start = arglist[0].toInteger();
	    end   = arglist[1].toInteger();
	    break;
    }

    if (start < 0)
    {
	k = len + cast(d_uint32) start;
	if (cast(d_int32)k < 0)
	    k = 0;
    }
    else
    {
	k = cast(d_uint32) start;
	if (len < k)
	    k = len;
    }

    if (end < 0)
    {
	r8 = len + cast(d_uint32) end;
	if (cast(d_int32)r8 < 0)
	    r8 = 0;
    }
    else
    {
	r8 = cast(d_uint32) end;
	if (len < cast(d_uint32) end)
	    r8 = len;
    }

    A = new Darray();
    for (n = 0; k < r8; k++)
    {
	v = othis.Get(k);
	if (v)
	{
	    A.Put(n, v, 0);
	}
	n++;
    }

    A.Put(TEXT_length, n, DontDelete | DontEnum);
    Value.copy(ret, &A.value);
    return null;
}

/* ===================== Darray_prototype_sort ================= */

static Dobject comparefn;
static CallContext *comparecc;

extern (C) int compare_value(void* x, void* y)
{
    Value* vx = cast(Value*)x;
    Value* vy = cast(Value*)y;
    d_string sx;
    d_string sy;
    int cmp;

    //writef("compare_value()\n");
    if (vx.isUndefined())
    {
	cmp = (vy.isUndefined()) ? 0 : 1;
    }
    else if (vy.isUndefined())
	cmp = -1;
    else
    {
	if (comparefn)
	{   Value arglist[2];
	    Value ret;
	    Value* v;
	    d_number n;

	    Value.copy(&arglist[0], vx);
	    Value.copy(&arglist[1], vy);
	    ret.putVundefined();
	    comparefn.Call(comparecc, comparefn, &ret, arglist);
	    n = ret.toNumber();
	    if (n < 0)
		cmp = -1;
	    else if (n > 0)
		cmp = 1;
	    else
		cmp = 0;
	}
	else
	{
	    sx = vx.toString();
	    sy = vy.toString();
	    scope tv = new TextView!(tchar)(sx, false);
	    cmp = tv.compare(sy);
	    //cmp = std.string.cmp(sx, sy);
	    if (cmp < 0)
		cmp = -1;
	    else if (cmp > 0)
		cmp = 1;
	}
    }
    return cmp;
}

void *Darray_prototype_sort(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.11
    Value* v;
    d_uint32 len;
    uint u;

    //writef("Array.prototype.sort()\n");
    v = othis.Get(TEXT_length);
    len = v ? v.toUint32() : 0;

    // This is not optimal, as isArrayIndex is done at least twice
    // for every array member. Additionally, the qsort() by index
    // can be avoided if we can deduce it is not a sparse array.

    Property *p;
    Value[] pvalues;
    d_uint32[] pindices;
    d_uint32 parraydim;
    d_uint32 nprops;

    // First, size & alloc our temp array
    if (len < 100)
    {	// Probably not too sparse an array
	parraydim = len;
    }
    else
    {
	parraydim = 0;
	foreach (inout Property p; *othis.proptable)
	{   if (p.attributes == 0)	// don't count special properties
		parraydim++;
	}
	if (parraydim > len)		// could theoretically happen
	    parraydim = len;
    }

    Value[] p1 = null;
    Value* v1;
    version (Win32)	// eh and alloca() not working under linux
    {
	if (parraydim < 128)
	    v1 = cast(Value*)alloca(parraydim * Value.sizeof);
    }
    if (v1)
	pvalues = v1[0 .. parraydim];
    else
    {
	p1 = new Value[parraydim];
	pvalues = p1;
    }

    d_uint32[] p2 = null;
    d_uint32* p3;
    version (Win32)
    {
	if (parraydim < 128)
	    p3 = cast(d_uint32*)alloca(parraydim * d_uint32.sizeof);
    }
    if (p3)
	pindices = p3[0 .. parraydim];
    else
    {
	p2 = new d_uint32[parraydim];
	pindices = p2;
    }

    // Now fill it with all the Property's that are array indices
    nprops = 0;
    foreach (Value key, inout Property p; *othis.proptable)
    {	d_uint32 index;

	if (p.attributes == 0 && key.isArrayIndex(index))
	{
	    pindices[nprops] = index;
	    Value.copy(&pvalues[nprops], &p.value);
	    nprops++;
	}
    }

    synchronized
    {
	comparefn = null;
	comparecc = cc;
	if (arglist.length)
	{
	    if (!arglist[0].isPrimitive())
		comparefn = arglist[0].object;
	}

	// Sort pvalues[]
	tango.stdc.stdlib.qsort(pvalues.ptr, nprops, Value.sizeof, &compare_value);
	//std.c.stdlib.qsort(pvalues.ptr, nprops, Value.sizeof, &compare_value);

	comparefn = null;
	comparecc = null;
    }

    // Stuff the sorted value's back into the array
    for (u = 0; u < nprops; u++)
    {	d_uint32 index;

	othis.Put(u, &pvalues[u], 0);
	index = pindices[u];
	if (index >= nprops)
	{
	    othis.Delete(index);
	}
    }

    delete p1;
    delete p2;

    ret.putVobject(othis);
    return null;
}

/* ===================== Darray_prototype_splice ================= */

void *Darray_prototype_splice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.12
    d_uint32 len;
    d_uint32 k;
    d_number start;
    d_number deleteCount;
    Value* v;
    Darray A;
    d_uint32 a;
    d_uint32 delcnt;
    d_uint32 inscnt;
    d_uint32 startidx;

    v = othis.Get(TEXT_length);
    if (!v)
	v = &vundefined;
    len = v.toUint32();

    switch (arglist.length)
    {
	case 0:
	    start = vundefined.toUint32();
	    deleteCount = start;
	    break;

	case 1:
	    start = arglist[0].toInteger();
	    deleteCount = vundefined.toUint32();
	    break;

	default:
	    start = arglist[0].toInteger();
	    deleteCount = arglist[1].toInteger();
	    break;
    }

    if (start < 0)
    {
	startidx = len + cast(d_uint32) start;
	if (cast(d_int32)startidx < 0)
	    startidx = 0;
    }
    else
    {
	startidx = cast(d_uint32) start;
	if (len < startidx)
	    startidx = len;
    }

    A = new Darray();

    delcnt = (deleteCount > 0) ? cast(d_uint32) deleteCount : 0;
    if (delcnt > len - startidx)
	delcnt = len - startidx;

    // If deleteCount is not specified, ECMA implies it should
    // be 0, while "JavaScript The Definitive Guide" says it should
    // be delete to end of array. Jscript doesn't implement splice().
    // We'll do it the Guide way.
    if (arglist.length < 2)
	delcnt = len - startidx;

    //writef("Darray.splice(startidx = %d, delcnt = %d)\n", startidx, delcnt);
    for (k = 0; k != delcnt; k++)
    {
	v = othis.Get(startidx + k);
	if (v)
	    A.Put(k, v, 0);
    }

    A.Put(TEXT_length, delcnt, DontDelete | DontEnum);
    inscnt = (arglist.length > 2) ? arglist.length - 2 : 0;
    if (inscnt != delcnt)
    {
	if (inscnt <= delcnt)
	{
	    for (k = startidx; k != (len - delcnt); k++)
	    {
		v = othis.Get(k + delcnt);
		if (v)
		    othis.Put(k + inscnt, v, 0);
		else
		    othis.Delete(k + inscnt);
	    }

	    for (k = len; k != (len - delcnt + inscnt); k--)
		othis.Delete(k - 1);
	}
	else
	{
	    for (k = len - delcnt; k != startidx; k--)
	    {
		v = othis.Get(k + delcnt - 1);
		if (v)
		    othis.Put(k + inscnt - 1, v, 0);
		else
		    othis.Delete(k + inscnt - 1);
	    }
	}
    }
    k = startidx;
    for (a = 2; a < arglist.length; a++)
    {
	v = &arglist[a];
	othis.Put(k, v, 0);
	k++;
    }

    othis.Put(TEXT_length, len - delcnt + inscnt, DontDelete | DontEnum);
    Value.copy(ret, &A.value);
    return null;
}

/* ===================== Darray_prototype_unshift ================= */

void *Darray_prototype_unshift(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
{
    // ECMA v3 15.4.4.13
    Value* v;
    d_uint32 len;
    d_uint32 k;

    v = othis.Get(TEXT_length);
    if (!v)
	v = &vundefined;
    len = v.toUint32();

    for (k = len; k; k--)
    {
	v = othis.Get(k - 1);
	if (v)
	    othis.Put(k + arglist.length - 1, v, 0);
	else
	    othis.Delete(k + arglist.length - 1);
    }

    for (k = 0; k < arglist.length; k++)
    {
	othis.Put(k, &arglist[k], 0);
    }
    othis.Put(TEXT_length, len + arglist.length, DontDelete | DontEnum);
    ret.putVnumber(len + arglist.length);
    return null;
}

/* =========================== Darray_prototype =================== */

class Darray_prototype : Darray
{
    this(ThreadContext *tc)
    {
	super(tc.Dobject_prototype);
	Dobject f = tc.Dfunction_prototype;

	Put(TEXT_constructor, tc.Darray_constructor, DontEnum);

	static NativeFunctionData nfd[] =
	[
	    {   &TEXT_toString, &Darray_prototype_toString, 0 },
	    {   &TEXT_toLocaleString, &Darray_prototype_toLocaleString, 0 },
	    {   &TEXT_toSource, &Darray_prototype_toSource, 0 },
	    {   &TEXT_concat, &Darray_prototype_concat, 1 },
	    {   &TEXT_join, &Darray_prototype_join, 1 },
	    {   &TEXT_pop, &Darray_prototype_pop, 0 },
	    {   &TEXT_push, &Darray_prototype_push, 1 },
	    {   &TEXT_reverse, &Darray_prototype_reverse, 0 },
	    {   &TEXT_shift, &Darray_prototype_shift, 0, },
	    {   &TEXT_slice, &Darray_prototype_slice, 2 },
	    {   &TEXT_sort, &Darray_prototype_sort, 1 },
	    {   &TEXT_splice, &Darray_prototype_splice, 2 },
	    {   &TEXT_unshift, &Darray_prototype_unshift, 1 },
	];

	DnativeFunction.init(this, nfd, DontEnum);
    }
}


/* =========================== Darray =================== */

class Darray : Dobject
{
    Value length;		// length property
    d_uint32 ulength;

    this()
    {
	this(getPrototype());
    }

    this(Dobject prototype)
    {
	super(prototype);
	length.putVnumber(0);
	ulength = 0;
	classname = TEXT_Array;
    }

    Value* Put(Identifier* key, Value* value, uint attributes)
    {
	Value* result = proptable.put(&key.value, key.value.hash, value, attributes);
	if (!result)
	    Put(key.value.string, value, attributes);
	return null;
    }

    Value* Put(d_string name, Value* v, uint attributes)
    {
	d_uint32 i;
	uint c;
	Value* result;

	// ECMA 15.4.5.1
	result = proptable.put(name, v, attributes);
	if (!result)
	{
	    if (name == TEXT_length)
	    {
		i = v.toUint32();
		if (i != v.toInteger())
		{   ErrInfo errinfo;

		    return Dobject.RuntimeError(&errinfo, ERR_LENGTH_INT);
		}
		if (i < ulength)
		{
		    // delete all properties with keys >= i
		    d_uint32[] todelete;

		    foreach (Value key, inout Property p; *proptable)
		    {	d_uint32 j;

			j = key.toUint32();
			if (j >= i)
			    todelete ~= j;
		    }
		    foreach (d_uint32 j; todelete)
		    {
			proptable.del(j);
		    }
		}
		ulength = i;
		length.number = i;
		proptable.put(name, v, attributes | DontDelete | DontEnum);
	    }

	    // if (name is an array index i)

	    i = 0;
	    for (size_t j = 0; j < name.length; j++)
	    {   ulong k;

		c = name[j];
		if (c == '0' && i == 0 && name.length > 1)
		    goto Lret;
		if (c >= '0' && c <= '9')
		{   k = i * cast(ulong)10 + c - '0';
		    i = cast(d_uint32)k;
		    if (i != k)
			goto Lret;		// overflow
		}
		else
		    goto Lret;
	    }
	    if (i >= ulength)
	    {
		if (i == 0xFFFFFFFF)
		    goto Lret;
		ulength = i + 1;
		length.number = ulength;
	    }
	}
      Lret:
	return null;
    }

    Value* Put(d_string name, Dobject o, uint attributes)
    {
	return Put(name, &o.value, attributes);
    }

    Value* Put(d_string PropertyName, d_number n, uint attributes)
    {
	Value v;

	v.putVnumber(n);
	return Put(PropertyName, &v, attributes);
    }

    Value* Put(d_string PropertyName, d_string string, uint attributes)
    {
	Value v;

	v.putVstring(string);
	return Put(PropertyName, &v, attributes);
    }

    Value* Put(d_uint32 index, Value* vindex, Value* value, uint attributes)
    {
	if (index >= ulength)
	    ulength = index + 1;

	proptable.put(vindex, index ^ 0x55555555 /*Value.calcHash(index)*/, value, attributes);
	return null;
    }

    Value* Put(d_uint32 index, Value* value, uint attributes)
    {
	if (index >= ulength)
	{   ulength = index + 1;
	    length.number = ulength;
	}

	proptable.put(index, value, attributes);
	return null;
    }

    Value* Put(d_uint32 index, d_string string, uint attributes)
    {
	if (index >= ulength)
	{   ulength = index + 1;
	    length.number = ulength;
	}

	proptable.put(index, string, attributes);
	return null;
    }

    Value* Get(Identifier* id)
    {
	//writef("Darray.Get(%p, '%s')\n", &proptable, PropertyName);
	if (id.value.string == TEXT_length)
	{   length.number = ulength;
	    return &length;
	}
	else
	    return Dobject.Get(id);
    }

    Value* Get(d_string PropertyName, uint hash)
    {
	//writef("Darray.Get(%p, '%s')\n", &proptable, PropertyName);
	if (PropertyName == TEXT_length)
	{   length.number = ulength;
	    return &length;
	}
	else
	    return Dobject.Get(PropertyName, hash);
    }

    Value* Get(d_uint32 index)
    {   Value* v;

	//writef("Darray.Get(%p, %d)\n", &proptable, index);
	v = proptable.get(index);
	return v;
    }

    Value* Get(d_uint32 index, Value* vindex)
    {   Value* v;

	//writef("Darray.Get(%p, %d)\n", &proptable, index);
	v = proptable.get(vindex, index ^ 0x55555555 /*Value.calcHash(index)*/);
	return v;
    }

    int Delete(d_string PropertyName)
    {
	// ECMA 8.6.2.5
	//writef("Darray.Delete('%ls')\n", d_string_ptr(PropertyName));
	if (PropertyName == TEXT_length)
	    return 0;		// can't delete 'length' property
	else
	    return proptable.del(PropertyName);
    }

    int Delete(d_uint32 index)
    {
	// ECMA 8.6.2.5
	return proptable.del(index);
    }


    static Dfunction getConstructor()
    {
	ThreadContext *tc = ThreadContext.getThreadContext();
	assert(tc);
	return tc.Darray_constructor;
    }

    static Dobject getPrototype()
    {
	ThreadContext *tc = ThreadContext.getThreadContext();
	assert(tc);
	return tc.Darray_prototype;
    }

    static void init(ThreadContext *tc)
    {
	tc.Darray_constructor = new Darray_constructor(tc);
	tc.Darray_prototype = new Darray_prototype(tc);

	tc.Darray_constructor.Put(TEXT_prototype, tc.Darray_prototype, DontEnum | DontDelete | ReadOnly);
    }
}