/*
 * 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 or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 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.
 */
/* Header file */
#ifdef VX
#include <taskLib.h>
#endif
#include <string.h>
#include "js-utils.h"
#include "js-scriptruntime.h"
#include "js-ctorfunction.h"
#include "js-hostctorfunction.h"
#include "js-hostmethodfunction.h"
#include "js-nativeobject.h"
#include "js-nativeglobal.h"
#include "js-nativefunction.h"
#include "js-nativehost.h"
#include "js-propertyexception.h"
#include "js-runtimeexception.h"
#include "js-exceptionhandler.h"
#ifdef REGEXP
#include "js-nativeregexp.h"
#endif
#include "js-scriptableobject.h"

/* Internal data */
const jsString_t	JSScriptableObject::className = "ScriptableObject";
const JSScriptableObject::Slot *
			JSScriptableObject::REMOVED = (Slot *) -1;

/* Procedures */
int
JSScriptableObject::getHashCode(jsString_t id)
{
#ifdef FASTER
    const int M = 31;
    int value;

    value = 0;
    for (int i = 0; id[i] != '\0'; ++i) {
	value *= M;
	value += id[i];
    }
    return value & 0x7fffffff;
#else /* !FATSER */
    union {
	int     index;
	uint8_t string[sizeof (int)];
    } sti;

    sti.index = 0;

    for (int i = 0; i < (int) sizeof (int); i++) {
	sti.string[i] = id[i];
	if(id[i] == 0) break;
    }
    return sti.index & 0x7fffffff;
#endif /* FASTER */
}
int
JSScriptableObject::getSlot(jsString_t id, int index)
{
#ifdef COMPACT_MEM
    if (slotsSet == NULL) {
	return SLOT_NOT_FOUND;
    }  
    Slot **slots = slotsSet->slots;
    int slotsLength = slotsSet->slotsLength;
#endif 
    if (slots == NULL) {
	return SLOT_NOT_FOUND;
    }

    int start = (index & 0x7fffffff) % slotsLength;
    int i = start;  
    do {   
	Slot *slot = slots[i];

	if (slot == NULL) return SLOT_NOT_FOUND;
	if (slot != REMOVED) {
	    if (slot->intKey == index
		&& (slot->stringKey == id
		    || (id != NULL && strcmp(id, slot->stringKey) == 0))) {
		return i;
	    }  
	}
	if (++i == slotsLength) {
	    i = 0;
	}
    } while (i != start);
    return SLOT_NOT_FOUND;
}
int
JSScriptableObject::getSlotToSet(JSContext *cx, jsString_t id, int index,
				jsBool_t getterSlot)
{
#ifdef COMPACT_MEM
    if (slotsSet == NULL) {
	slotsSet = (SlotsSet *) JS_NewGCObj(cx, sizeof (*slotsSet));
	memset(slotsSet, 0, sizeof (*slotsSet));
    }
    if (slotsSet->slotsLength == 0) {
	slotsSet->slots = (Slot **) JS_NewGCObj(cx,
					sizeof (*slotsSet->slots) * 5);
	memset(slotsSet->slots, 0, sizeof (*slotsSet->slots) * 5);
	slotsSet->slotsLength = 5;
    } 
    Slot **slots = slotsSet->slots;
    int slotsLength = slotsSet->slotsLength;
#else /* !COMPACT_MEM */
    if (slotsLength == 0) {
	slots = (Slot **) JS_NewGCObj(cx, sizeof (*slots) * 5);
	memset(slots, 0, sizeof (*slots) * 5);
	slotsLength = 5;
    }  
#endif /* COMPACT_MEM */
    int start = (index & 0x7fffffff) % slotsLength;
    jsBool_t sawRemoved = FALSE;
    int i = start;
 
    do {
	Slot *slot = slots[i];
 
	if (slot == NULL) {
	    return addSlot(cx, id, index, getterSlot);
	}
	if (slot != REMOVED) {
	    if (slot->intKey == index
		&& (slot->stringKey == id
		    || (id != NULL && strcmp(id, slot->stringKey) == 0))) {
		return i;
	    }
	} else {
	    sawRemoved = TRUE;
	}
	if (++i == slotsLength) {
	    i = 0;
	}
    } while (i != start);
    if (sawRemoved) {
	return addSlot(cx, id, index, getterSlot);
    }
    cx->exceptionHandler->throwEx(
            cx,
            new (cx) JSRuntimeException("Hashtable internal error"));
    /* NOT REACHED */
    return SLOT_NOT_FOUND;
}
int
JSScriptableObject::addSlot(JSContext *cx, jsString_t id, int index,
			jsBool_t getterSlot)
{
#ifdef COMPACT_MEM
    Slot **slots = slotsSet->slots;
    int slotsLength = slotsSet->slotsLength;
#endif
    int start = (index & 0x7fffffff) % slotsLength;
    int i = start;  
    do { 
	Slot *slot = slots[i];

	if (slot == NULL || slot == REMOVED) {
#ifdef COMPACT_MEM
	    int count = ++slotsSet->count;

	    if (count >= slotsLength) {
		--slotsSet->count;
#else
	    count++;
	    if ((4 * count) > (3 * slotsLength)) {
		--count;
#endif
		grow(cx);
		return getSlotToSet(cx, id, index, getterSlot);
	    }
	    slot = getterSlot ? new (cx) GetterSlot() : new (cx) Slot();
	    slot->stringKey = id != NULL ? JS_NewGCString(cx, id)
					 : (jsString_t) NULL;
	    slot->intKey = index;
	    slot->flags = 0;
	    slot->attributes = 0;
	    slots[i] = slot;
	    return i;
	}
	if (slot->intKey == index
	    && (slot->stringKey == id
		|| (id != NULL && strcmp(id, slot->stringKey) == 0))) {
	    return i;
	}
	if (++i == slotsLength) {
	    i = 0;
	}
    } while (i != start);
    cx->exceptionHandler->throwEx(
            cx,
            new (cx) JSRuntimeException("Hashtable internal error"));
    /* NOT REACHED */
    return SLOT_NOT_FOUND;
}
void
JSScriptableObject::removeSlot(JSContext *cx, jsString_t id, int index)
{
    int slotIndex = getSlot(id, index);
 
    if (slotIndex == SLOT_NOT_FOUND) {
	return;  
    } 
#ifdef COMPACT_MEM
    Slot **slots = slotsSet->slots;
#endif
    if ((slots[slotIndex]->attributes & JS_PA_Permanent) != 0) {
	return;  
    }   
    slots[slotIndex] = (Slot *) REMOVED;
#ifdef COMPACT_MEM
    slotsSet->count--;
#else
    count--;
#endif
}
void
JSScriptableObject::grow(JSContext *cx)
{
#ifdef COMPACT_MEM
    Slot **slots = slotsSet->slots;
    int slotsLength = slotsSet->slotsLength;
#endif
    //20040623 ichino
#if 0
    int  newSlotsLength = slotsLength * 2 + 1;
#else
    int  newSlotsLength;
    if( slotsLength > 256 ){
        newSlotsLength = slotsLength + 256;
    }else{
        newSlotsLength = slotsLength * 2 + 1;
    }
#endif

    Slot **newSlots = (Slot **) JS_NewGCObj(cx,
					(sizeof (*slots) * newSlotsLength));
 
    memset(newSlots, 0, sizeof (*newSlots) * newSlotsLength);
    for (int j = slotsLength - 1; j >= 0 ; j--) {
	Slot *slot = slots[j];
 
	if (slot == NULL || slot == REMOVED) {
	    continue;
	}
	int k = (slot->intKey & 0x7fffffff) % newSlotsLength;
	while (newSlots[k] != NULL) {
	    if (++k == newSlotsLength) {
		k = 0;
	    }
	}
	newSlots[k] = slot;
    }
#ifdef COMPACT_MEM
    slotsSet->slots = newSlots;
    slotsSet->slotsLength = newSlotsLength;
#else 
    slots = newSlots;
    slotsLength = newSlotsLength;
#endif
}
jsVal_t *
JSScriptableObject::getIds(JSContext *cx, int *numIds, jsBool_t getAll)
{
#ifdef COMPACT_MEM
    if (slotsSet == NULL) {
	*numIds = 0;
	return NULL;
    }
    Slot **slots = slotsSet->slots;
    int slotsLength = slotsSet->slotsLength;
#endif
    if (slots == NULL) {
	*numIds = 0;
	return NULL;
    }
    int count = 0;
    int i; // avoid compile error on vxWorks

    for (i = slotsLength - 1; i >= 0; i--) {
	Slot *slot = slots[i];

	if (slot == NULL || slot == REMOVED) {
	    continue;
	}
	if (getAll || (slot->attributes & JS_PA_DontEnum) == 0) {
	    count++;
	}
    }    
    jsVal_t *result;
 
    result = (jsVal_t *) JS_NewZoneObj(cx, count * sizeof (*result));
    *numIds = count;
    for (i = slotsLength - 1; i >= 0; i--) {
	Slot *slot = slots[i];
 
	if (slot == NULL || slot == REMOVED) {
	    continue;
	}
	if (getAll || (slot->attributes & JS_PA_DontEnum) == 0) {
	    jsVal_t val;

	    if (slot->stringKey != NULL) {
		val = JS_STRING_TO_VAL(slot->stringKey);
	    } else {
		val = JSScriptRuntime::toValue(cx, slot->intKey);
	    }      
	    result[count - 1] = val;
	    count -= 1;
	}
    }
    return result;
}
JSFunction *
JSScriptableObject::getFunctionProperty(JSContext *cx, JSFlattenedObject *f,
					jsString_t name)
{
    jsVal_t val;

    jsString_t s = JS_NewZoneString(cx, name);	// align string

    val = f->getProperty(cx, JS_STRING_TO_VAL(s));
    JS_DeleteZoneObj(cx, s);

    if (JS_VAL_IS_OBJECT(val) && val != JS_VAL_NULL) {
	JSScriptable *scriptable;

	scriptable = JS_VAL_TO_OBJECT(val);
	if (scriptable->isInstanceOf(JSNativeFunction::className)) {
	    return (JSNativeFunction *) scriptable;
	}
	if (scriptable->isInstanceOf(JSNativeHost::className)) {
	    return (JSNativeHost *) scriptable;
	}
#ifdef REGEXP
	if (scriptable->isInstanceOf(JSNativeRegExp::className)) {
	    return (JSNativeRegExp *) scriptable;
	}
#endif
    }    
    return NULL;
}

void
JSScriptableObject::defineClass(JSContext *cx, JSScriptable *scope,
				JSScriptable *proto,
				jsConstructor_t constructor,
				jsFinishInit_t finishInit)
{
    JSCtorFunction *ctor;

    proto->setPrototype(cx, getObjectPrototype(cx, scope));
    if (constructor != NULL) {
	jsString_t className = proto->getClassName(cx);

	ctor = new (cx) JSCtorFunction(cx, className, className, constructor,
					scope);
	ctor->addAsConstructor(cx, scope, proto);
    } else {
	ctor = NULL;
    }
    if (finishInit != NULL) {
	(*finishInit)(cx, scope, ctor, proto);
    }
}
void
JSScriptableObject::defineClass(JSContext *cx, JSScriptable *scope,
				JSScriptable *proto,
				::jsConstructor_t constructor)
{
    JSHostCtorFunction *ctor;

    proto->setPrototype(cx, getObjectPrototype(cx, scope));
    if (constructor != NULL) {
	jsString_t className = proto->getClassName(cx);

	ctor = new (cx) JSHostCtorFunction(cx, className, className,
					constructor, scope);
	ctor->addAsConstructor(cx, scope, proto);
    } else {
	ctor = NULL;
    }
}
JSScriptable *   
JSScriptableObject::getClassPrototype(JSContext *cx, JSScriptable *scope,
				jsString_t className)
{
    scope = getTopLevelScope(cx, scope);
 
    jsVal_t ctor;
    jsBool_t ok = JSScriptRuntime::getTopLevelProp(cx, scope, className, &ctor);
 
    if (!ok || !JS_VAL_IS_OBJECT(ctor) || ctor == JS_VAL_NULL) {
	return NULL;
    }
    JSScriptable *ctorObj = JS_VAL_TO_OBJECT(ctor);

    if (!ctorObj->has(cx, "prototype", ctorObj)) {
	return NULL;
    }
    jsVal_t proto;
   
    ok = ctorObj->get(cx, "prototype", ctorObj, &proto);
    if (!ok || !JS_VAL_IS_OBJECT(proto) || proto == JS_VAL_NULL) {
	return NULL;
    }
    return JS_VAL_TO_OBJECT(proto);
}
/**
  * Get the Function.prototype property.
  * See ECMA 15.3.4.
 */  
JSScriptable *
JSScriptableObject::getFunctionPrototype(JSContext *cx, JSScriptable *scope)
{
    return getClassPrototype(cx, scope, JSNativeFunction::className);
}
/**
 * Get the Object.prototype property.
 * See ECMA 15.2.4.
 */  
JSScriptable *
JSScriptableObject::getObjectPrototype(JSContext *cx, JSScriptable *scope)
{
    return getClassPrototype(cx, scope, JSNativeObject::className);
}
JSScriptable *
JSScriptableObject::getTopLevelScope(JSContext *cx, JSScriptable *obj)
{
    JSScriptable *next = obj;

    do {
	obj = next;
	next = obj->getParentScope(cx);
    } while (next != NULL);
    return obj;
}
void
JSScriptableObject::defineFunctionProperties(JSContext *cx, jsString_t clazz,
					int numFunctions,
					jsFunction_t *functions,
					int attributes)
{
    for (int i = 0; i < numFunctions; ++i) {
	JSMethodFunction *function
		= new (cx) JSMethodFunction(cx, functions[i].name, clazz,
				functions[i].cfunc, this);

	if (functions[i].length > 0) {
	    function->setParmsLength(functions[i].length);
	}
	if (functions[i].length >= 0) {
	    function->setLength(functions[i].length);
	}
	defineProperty(cx, functions[i].name, JS_OBJECT_TO_VAL(function),
		attributes);
    }
}
void
JSScriptableObject::defineFunctionProperties(JSContext *cx, jsString_t clazz,
					int numFunctions,
					::jsFunction_t *functions)
{
    for (int i = 0; i < numFunctions; ++i) {
	JSHostMethodFunction *function
		= new (cx) JSHostMethodFunction(cx, functions[i].name, NULL,
				functions[i].cfunc, this);
 
	defineProperty(cx, functions[i].name, JS_OBJECT_TO_VAL(function),
		functions[i].attributes);
    }
}
void
JSScriptableObject::defineProperty(JSContext *cx, jsString_t name,
				jsVal_t value, int attributes)
{
    put(cx, name, this, value);
    setAttributes(cx, name, this, attributes);
}
void
JSScriptableObject::defineProperty(JSContext *cx, jsString_t name,
				jsGetterProc_t getter, jsSetterProc_t setter,
				int attributes)
{
    int slotIndex = getSlotToSet(cx, name, getHashCode(name), TRUE);
#ifdef COMPACT_MEM
    GetterSlot *slot = (GetterSlot *) slotsSet->slots[slotIndex];
#else
    GetterSlot *slot = (GetterSlot *) slots[slotIndex];
#endif

    slot->getter = getter;
    slot->setter = setter;
    slot->value = JS_VAL_NULL;
    slot->attributes = (short) attributes;
    slot->flags = getter != NULL ? Slot::HAS_GETTER : 0;
    slot->flags |= setter != NULL ? Slot::HAS_SETTER : 0;
}
void
JSScriptableObject::defineProperties(JSContext *cx, int numProperties,
				jsProperty_t *properties)
{
    for (int i = 0; i < numProperties; ++i) {
	if (properties[i].getter == NULL && properties[i].setter == NULL) {
	    defineProperty(cx, properties[i].name, JS_VAL_NULL,
			properties[i].attributes);
	} else {
	    defineProperty(cx, properties[i].name, properties[i].getter,
			properties[i].setter, properties[i].attributes);
	}
    }
}
int
JSScriptableObject::getAttributes(JSContext *cx, jsString_t name,
				JSScriptable *start)
{
    int slotIndex = getSlot(name, getHashCode(name));

    if (slotIndex == SLOT_NOT_FOUND) {
	cx->exceptionHandler->throwEx(
                cx,
		new (cx) JSPropertyException(
				JSContext::getMessage(cx,
					"msg.prop.not.found: %s", name)));
	/* NOT REACHED */
    }
#ifdef COMPACT_MEM
    return slotsSet->slots[slotIndex]->attributes;
#else
    return slots[slotIndex]->attributes;
#endif
}
int
JSScriptableObject::getAttributes(JSContext *cx, int index, JSScriptable *start)
{
    int slotIndex = getSlot(NULL, index);

    if (slotIndex == SLOT_NOT_FOUND) {
	cx->exceptionHandler->throwEx(
                cx,
		new (cx) JSPropertyException(
				JSContext::getMessage(cx,
					"msg.prop.not.found: %d", index)));
	/* NOT REACHED */
    }
#ifdef COMPACT_MEM
    return slotsSet->slots[slotIndex]->attributes;
#else
    return slots[slotIndex]->attributes;
#endif
}
void
JSScriptableObject::setAttributes(JSContext *cx, jsString_t name,
				JSScriptable *start, int attributes)
{
    const int mask = JS_PA_ReadOnly | JS_PA_DontEnum | JS_PA_Permanent;

    attributes &= mask; // mask out unused bits

    int slotIndex = getSlot(name, getHashCode(name));

    if (slotIndex == SLOT_NOT_FOUND) {
	cx->exceptionHandler->throwEx(
                cx,
		new (cx) JSPropertyException(
				JSContext::getMessage(cx,
					"msg.prop.not.found: %s", name)));
	/* NOT REACHED */
    }
#ifdef COMPACT_MEM
    slotsSet->slots[slotIndex]->attributes = (short) attributes;
#else
    slots[slotIndex]->attributes = (short) attributes;
#endif
}
void
JSScriptableObject::setAttributes(JSContext *cx, int index, JSScriptable *start,
				int attributes)
{
    const int mask = JS_PA_ReadOnly | JS_PA_DontEnum | JS_PA_Permanent;
 
    attributes &= mask; // mask out unused bits

    int slotIndex = getSlot(NULL, index);

    if (slotIndex == SLOT_NOT_FOUND) {
	cx->exceptionHandler->throwEx(
                cx,
		new (cx) JSPropertyException(
				JSContext::getMessage(cx,
					"msg.prop.not.found: %d", index)));
	/* NOT REACHED */
    }
#ifdef COMPACT_MEM
    slotsSet->slots[slotIndex]->attributes = (short) attributes;
#else
    slots[slotIndex]->attributes = (short) attributes;
#endif
}
jsBool_t
JSScriptableObject::isInstanceOf(const jsString_t className)
{
    return className == JSScriptableObject::className;
}
jsBool_t
JSScriptableObject::has(JSContext *cx, jsString_t name, JSScriptable *start)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    jsBool_t result = getSlot(name, getHashCode(name)) != SLOT_NOT_FOUND;
#ifdef THREADS
    cx->runtime->unlock();
#endif
    return result;
}
jsBool_t
JSScriptableObject::has(JSContext *cx, int index, JSScriptable *start)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    jsBool_t result = getSlot(NULL, index) != SLOT_NOT_FOUND;
#ifdef THREADS
    cx->runtime->unlock();
#endif
    return result;
}
jsBool_t
JSScriptableObject::get(JSContext *cx, jsString_t name, JSScriptable *start,
			jsVal_t *value)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    int hashCode = getHashCode(name);

#ifdef COMPACT_MEM
    if (slotsSet != NULL) {
	if (hashCode == slotsSet->lastHash) {
	    if (slotsSet->lastName != NULL
		&& strcmp(name, slotsSet->lastName) == 0) {
		*value = slotsSet->lastValue;
#ifdef THREADS
		cx->runtime->unlock();
#endif
		return TRUE;
	    }
	}
    }    
#else /* !COMPACT_MEM */
    if (hashCode == lastHash) {
	if (lastName != NULL && strcmp(name, lastName) == 0) {
	    *value = lastValue;
#ifdef THREADS
	    cx->runtime->unlock();
#endif
	    return TRUE;
	}
    }
#endif /* COMPACT_MEM */

    int slotIndex = getSlot(name, hashCode);

    if (slotIndex == SLOT_NOT_FOUND) {
#ifdef THREADS
	cx->runtime->unlock();
#endif
	return FALSE;
    }
#ifdef COMPACT_MEM
    Slot *slot = slotsSet->slots[slotIndex];
#else
    Slot *slot = slots[slotIndex];
#endif
    if ((slot->flags & Slot::HAS_GETTER) != 0) {
	GetterSlot *getterSlot = (GetterSlot *) slot;
	jsBool_t result
		=  getterSlot->getter((jsContext_t) cx, (jsObject_t) this,
					name, (jsObject_t) start, value);
#ifdef THREADS
	cx->runtime->unlock();
#endif
	return result;
    }
#ifdef COMPACT_MEM
    slotsSet->lastName = slot->stringKey;
    slotsSet->lastHash = hashCode;
    slotsSet->lastValue = slot->value;
    *value = slotsSet->lastValue;
#else 
    lastName = slot->stringKey;
    lastHash = hashCode;
    lastValue = slot->value;
    *value = lastValue;
#endif
#ifdef THREADS
    cx->runtime->unlock();
#endif
    return TRUE;
}
jsBool_t
JSScriptableObject::get(JSContext *cx, int index, JSScriptable *start,
			jsVal_t *value)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    int slotIndex = getSlot(NULL, index);

    if (slotIndex == SLOT_NOT_FOUND) {
#ifdef THREADS
	cx->runtime->unlock();
#endif
	return FALSE;
    }
#ifdef COMPACT_MEM
    *value = slotsSet->slots[slotIndex]->value;
#else
    *value = slots[slotIndex]->value;
#endif
    
#ifdef THREADS
    cx->runtime->unlock();
#endif
    return TRUE;
}
void
JSScriptableObject::put(JSContext *cx, jsString_t name, JSScriptable *start,
		jsVal_t value)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    int hashCode = getHashCode(name);

    int slotIndex = getSlot(name, hashCode);

    if (slotIndex == SLOT_NOT_FOUND) {
	if (start != this) {
	    start->put(cx, name, start, value);
#ifdef THREADS
	    cx->runtime->unlock();
#endif
	    return;
	}   
	slotIndex = getSlotToSet(cx, name, hashCode, FALSE);
    }

#ifdef COMPACT_MEM
    Slot *slot = slotsSet->slots[slotIndex];
#else
    Slot *slot = slots[slotIndex];
#endif

    if ((slot->attributes & JS_PA_ReadOnly) != 0) {
#ifdef THREADS
	cx->runtime->unlock();
#endif
	return;
    }
    if ((slot->flags & Slot::HAS_SETTER) != 0) {
	GetterSlot *getterSlot = (GetterSlot *) slot;

	getterSlot->setter((jsContext_t) cx, (jsObject_t) this, name,
			(jsObject_t) start, value);
#ifdef THREADS
	cx->runtime->unlock();
#endif
	return;
    }

    if (this == start) {
	slot->value = value;
#ifdef COMPACT_MEM
	if (hashCode == slotsSet->lastHash) {
	    if (slotsSet->lastName != NULL
		&& strcmp(name, slotsSet->lastName) == 0) {
		slotsSet->lastValue = value;
	    }
	}
#else
	if (hashCode == lastHash) {
	    if (lastName != NULL && strcmp(name, lastName) == 0) {
		lastValue = value;
	    }
	}
#endif
    } else {
	 start->put(cx, name, start, value);
    }
#ifdef THREADS
    cx->runtime->unlock();
#endif
}
void
JSScriptableObject::put(JSContext *cx, int index, JSScriptable *start,
			jsVal_t value)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    int slotIndex = getSlot(NULL, index);

    if (slotIndex == SLOT_NOT_FOUND) {
	if (start != this) {
	    start->put(cx, index, start, value);
#ifdef THREADS
	    cx->runtime->unlock();
#endif
	    return;
	}
	slotIndex = getSlotToSet(cx, NULL, index, false);
    }

#ifdef COMPACT_MEM
    Slot *slot = slotsSet->slots[slotIndex];
#else
    Slot *slot = slots[slotIndex];
#endif

    if ((slot->attributes & JS_PA_ReadOnly) != 0) {
#ifdef THREADS
	cx->runtime->unlock();
#endif
	return;
    }
    if (this == start) {
	slot->value = value;
    } else {
	start->put(cx, index, start, value);
    }      
#ifdef THREADS
    cx->runtime->unlock();
#endif
}
void
JSScriptableObject::del(JSContext *cx, jsString_t name)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    int hashCode = getHashCode(name);

#ifdef COMPACT_MEM
    if (slotsSet->lastHash == hashCode) {
	if (slotsSet->lastName != NULL
	    && strcmp(name, slotsSet->lastName) == 0) {
	    slotsSet->lastName = NULL;
	}
    }
#else  
    if (lastHash == hashCode) {
	if (lastName != NULL && strcmp(name, lastName) == 0) {
	    lastName = NULL;
	}
    }
#endif
    removeSlot(cx, name, hashCode);
#ifdef THREADS
    cx->runtime->unlock();
#endif
}
void
JSScriptableObject::del(JSContext *cx, int index)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    removeSlot(cx, NULL, index);
#ifdef THREADS
    cx->runtime->unlock();
#endif
}
JSScriptable *
JSScriptableObject::getPrototype(JSContext *cx)
{
    return prototype;
}
void
JSScriptableObject::setPrototype(JSContext *cx, JSScriptable *prototype)
{
    this->prototype = prototype;
}
JSScriptable *
JSScriptableObject::getParentScope(JSContext *cx)
{
    return parent;
}
void
JSScriptableObject::setParentScope(JSContext *cx, JSScriptable *parent)
{
    this->parent = parent;
}
jsVal_t *
JSScriptableObject::getIds(JSContext *cx, int *numIds)
{
#ifdef THREADS
    cx->runtime->lock();
#endif
    jsVal_t *ids = getIds(cx, numIds, FALSE);
#ifdef THREADS
    cx->runtime->unlock();
#endif
    return ids;
}
/**
 * Implements the [[DefaultValue]] internal method.
 *   
 * Note that the toPrimitive conversion is a no-op for
 * every type other than Object, for which [[DefaultValue]]
 * is called. See ECMA 9.1.
 *   
 * A hint of null means "no hint".
 *   
 * See ECMA 8.6.2.6.
 */  
jsVal_t
JSScriptableObject::getDefaultValue(JSContext *cx, jsType_t typeHint)
{
    JSFlattenedObject f(this);
    jsVal_t val;
    JSException *ex;

    if ((ex = enterTry(cx)) == NULL) {
	int i;

	for (i = 0; i < 2; i++) {
	    if (typeHint == JS_T_String ? i == 0 : i == 1) {
		JSFunction *fun = getFunctionProperty(cx, &f, "toString");

		if (fun == NULL) {
		    continue;
		}
		val = fun->call(cx,
				((JSNativeFunction *) fun)->getParentScope(cx),
				f.getObject(), 0, NULL);
	    } else {
		jsString_t hint;

		switch (typeHint) {
		case JS_T_Null:
		    hint = (jsString_t) "undefined";
		    break;
		case JS_T_String:
		    hint = (jsString_t) "string";
		    break;
		case JS_T_Object:
		    hint = (jsString_t) "object";
		    break;
		case JS_T_Function:
		    hint = (jsString_t) "function";
		    break;
		case JS_T_Boolean:
		    hint = (jsString_t) "boolean";
		    break;
		default:
		    hint = (jsString_t) "number";
		    break;
		}
		JSFunction *fun = getFunctionProperty(cx, &f, "valueOf");

		if (fun == NULL) {
		    continue;
		}
		jsVal_t args[1];
		double s[2];	// use double to align string

		strcpy((jsChar_t *) s, hint);
		args[0] = JS_STRING_TO_VAL((jsChar_t *) s);
		val = fun->call(cx,
				((JSNativeFunction *) fun)->getParentScope(cx),
				this, 1, args);
	    }
	    if (val != JS_VAL_NULL
		&& (val == JS_VAL_VOID || !JS_VAL_IS_OBJECT(val)
		    || typeHint == JS_T_Object || typeHint == JS_T_Function))
	    {
		cx->exceptionHandler->exitTry();
		return val;  
	    }
	}
	cx->exceptionHandler->exitTry();
     } 
     cx->exceptionHandler->throwEx(
             cx,
             JSNativeGlobal::constructError(cx, "TypeError",
		JSScriptRuntime::getMessage(cx, "msg.default.value"), this));
    /* NOT REACHED */
    return JS_VAL_VOID;
}
jsBool_t
JSScriptableObject::hasInstance(JSContext *cx, JSScriptable *instance)
{
    return JSScriptRuntime::jsDelegatesTo(cx, instance, this);
}
void
JSScriptableObject::mark(JSContext *cx)
{
    JSGCHeap *gcHeap = &cx->runtime->gcHeap;

    if (gcHeap->isMarkObj(this)) return;

    JSScriptable::mark(cx);

    if (parent != NULL) parent->mark(cx);
    if (prototype != NULL) prototype->mark(cx);

#ifdef COMPACT_MEM
    if (slotsSet != NULL) {
	gcHeap->markObj(slotsSet);

	Slot **slots = slotsSet->slots;
	int slotsLength = slotsSet->slotsLength;

	if (slots != NULL) {
	    gcHeap->markObj(slots);
	    for (int i = 0; i < slotsLength; ++i) {
		Slot *slot;

		slot = slots[i];
		if (slot != NULL && slot != REMOVED) {
		    gcHeap->markObj(slot);
		    if (slot->stringKey != NULL) {
			gcHeap->markObj(slot->stringKey);
		    }
		    JS_MarkVal(cx, slot->value);
		}
	    }
	}
    }
#else /* !COMPACT_MEM */
    if (slots != NULL) {
	gcHeap->markObj(slots);
	for (int i = 0; i < slotsLength; ++i) {
	    Slot *slot;

	    slot = slots[i];
	    if (slot != NULL && slot != REMOVED) {
		gcHeap->markObj(slot);
		if (slot->stringKey != NULL) {
		    gcHeap->markObj(slot->stringKey);
		}
		JS_MarkVal(cx, slot->value);
	    }
	}
    }
#endif /* COMPACT_MEM */
}

//2003.10.08 ichino
void
JSScriptableObject::removeAllProperties()
{
    slots = NULL;
    slotsLength = 0;
    count = 0;
    lastName = NULL;
    lastHash = 0; 	
}

JSScriptableObject::JSScriptableObject()
{
    parent = NULL;
    prototype = NULL;
#ifdef COMPACT_MEM
    slotsSet = NULL;
#else
    slots = NULL;
    slotsLength = 0;
    count = 0;
    lastName = NULL;
    lastHash = 0; //avoid purify UMR error

#endif
}

