예제 #1
0
JSContext *
js_ContextIterator(JSRuntime *rt, JSContext **iterp)
{
    JSContext *cx = *iterp;

    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));
    if (!cx)
	cx = (JSContext *)rt->contextList.next;
    if ((void *)cx == &rt->contextList)
	return NULL;
    *iterp = (JSContext *)cx->links.next;
    return cx;
}
예제 #2
0
JSBool
js_FindProperty(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp)
{
    JSRuntime *rt;
    JSObject *obj, *parent, *lastobj;
    JSProperty *prop;

    rt = cx->runtime;
    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));

    for (obj = cx->fp->scopeChain; obj; obj = parent) {
	/* Try the property cache and return immediately on cache hit. */
	PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);
	if (PROP_FOUND(prop)) {
	    *objp = obj;
	    *propp = prop;
	    return JS_TRUE;
	}

	/*
	 * Set parent here, after cache hit to minimize cycles in that case,
	 * but before js_LookupProperty, which might change obj.
	 */
	parent = OBJ_GET_PARENT(obj);

	/* If cache miss (not cached-as-not-found), take the slow path. */
	if (!prop) {
	    if (!js_LookupProperty(cx, obj, id, &obj, &prop))
		return JS_FALSE;
	    if (prop) {
		PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
		*objp = obj;
		*propp = prop;
		return JS_TRUE;
	    }

	    /* No such property -- cache obj[id] as not-found. */
	    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
				PROP_NOT_FOUND);
	}
	lastobj = obj;
    }
    *objp = lastobj;
    *propp = NULL;
    return JS_TRUE;
}
예제 #3
0
void
js_DestroyContext(JSContext *cx)
{
    JSRuntime *rt;
    JSBool rtempty;

    rt = cx->runtime;
    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));

    /* Remove cx from context list first. */
    PR_REMOVE_LINK(&cx->links);
    rtempty = (rt->contextList.next == (PRCList *)&rt->contextList);

    if (js_InterpreterHooks && js_InterpreterHooks->destroyContext) {
	/* This is a stub, but in case it removes roots, call it now. */
        js_InterpreterHooks->destroyContext(cx);
    }

    if (rtempty) {
	/* No more contexts: clear debugging state to remove GC roots. */
	JS_ClearAllTraps(cx);
	JS_ClearAllWatchPoints(cx);
    }

    /* Remove more GC roots in regExpStatics, then collect garbage. */
#if JS_HAS_REGEXPS
    js_FreeRegExpStatics(cx, &cx->regExpStatics);
#endif
    js_ForceGC(cx);

    if (rtempty) {
	/* Free atom state now that we've run the GC. */
	js_FreeAtomState(cx, &rt->atomState);
    }

    /* Free the stuff hanging off of cx. */
    PR_FinishArenaPool(&cx->stackPool);
    PR_FinishArenaPool(&cx->codePool);
    PR_FinishArenaPool(&cx->tempPool);
    if (cx->lastMessage)
	free(cx->lastMessage);
    free(cx);
}
예제 #4
0
JSProperty *
js_DefineProperty(JSContext *cx, JSObject *obj, jsval id, jsval value,
		  JSPropertyOp getter, JSPropertyOp setter, uintN flags)
{
    JSRuntime *rt;
    JSScope *scope;
    JSProperty *prop;

    rt = cx->runtime;
    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));

    /* Handle old bug that treated empty string as zero index. */
    CHECK_FOR_FUNNY_INDEX(id);

    /* Use the object's class getter and setter by default. */
    if (!getter)
	getter = obj->map->clasp->getProperty;
    if (!setter)
	setter = obj->map->clasp->setProperty;

    /* Find a sharable scope, or get a new one for obj. */
    scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop);
    if (!scope)
	return NULL;

    /* Add the property only if MutateScope didn't find a shared scope. */
    if (!prop) {
	prop = js_NewProperty(cx, scope, id, getter, setter, flags);
	if (!prop)
	    return NULL;
	if (!obj->map->clasp->addProperty(cx, obj, prop->id, &value) ||
	    !scope->ops->add(cx, scope, id, prop)) {
	    js_DestroyProperty(cx, prop);
	    return NULL;
	}
	PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
    }

    PR_ASSERT(prop->slot < obj->map->freeslot);
    obj->slots[prop->slot] = value;
    return prop;
}
예제 #5
0
JSBool
js_FindVariable(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp)
{
    JSRuntime *rt;
    JSObject *obj;
    JSProperty *prop;

    rt = cx->runtime;
    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));

    /*
     * First look for id's property along the "with" statement and the
     * statically-linked scope chains.
     */
    if (!js_FindProperty(cx, id, objp, propp))
	return JS_FALSE;
    if (*propp)
	return JS_TRUE;

    /*
     * Use the top-level scope from the scope chain, which won't end in the
     * same scope as cx->globalObject's for cross-context function calls.
     */
    obj = *objp;
    PR_ASSERT(obj);

    /*
     * Make a top-level variable.
     */
    prop = js_DefineProperty(cx, obj, id, JSVAL_VOID, NULL, NULL,
			     JSPROP_ENUMERATE);
    if (!prop)
	return JS_FALSE;
    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
    *propp = prop;
    return JS_TRUE;
}
예제 #6
0
JSBool
js_DeleteProperty2(JSContext *cx, JSObject *obj, JSProperty *prop, jsval id,
		   jsval *rval)
{
#if JS_HAS_PROP_DELETE
    JSRuntime *rt;
    JSString *str;
    JSScope *scope;
    JSObject *proto;
    PRHashNumber hash;
    JSSymbol *sym;

    rt = cx->runtime;
    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));

    *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;

    if (prop->flags & JSPROP_PERMANENT) {
	if (JSVERSION_IS_ECMA(cx->version)) {
	    *rval = JSVAL_FALSE;
	    return JS_TRUE;
	}
	str = js_ValueToSource(cx, js_IdToValue(id));
	if (str)
	    JS_ReportError(cx, "%s is permanent", JS_GetStringBytes(str));
	return JS_FALSE;
    }

    if (!obj->map->clasp->delProperty(cx, obj, prop->id,
				      &prop->object->slots[prop->slot])) {
	return JS_FALSE;
    }

    /* Handle old bug that treated empty string as zero index. */
    CHECK_FOR_FUNNY_INDEX(id);

    GC_POKE(cx, prop->object->slots[prop->slot]);
    scope = (JSScope *)obj->map;
    proto = scope->object;
    if (proto == obj) {
	/* The object has its own scope, so remove id if it was found here. */
	if (prop->object == obj) {
	    /* Purge cache only if prop is not about to be destroyed. */
	    if (prop->nrefs != 1) {
		PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
				    PROP_NOT_FOUND);
	    }
#if JS_HAS_OBJ_WATCHPOINT
	    if (prop->setter == js_watch_set) {
		/*
		 * Keep the symbol around with null value in case of re-set.
		 * The watchpoint will hold the "deleted" property until it
		 * is removed by obj_unwatch or a native JS_ClearWatchPoint.
		 * See js_SetProperty for the re-set logic.
		 */
		for (sym = prop->symbols; sym; sym = sym->next) {
		    if (sym_id(sym) == id) {
			sym->entry.value = NULL;
			prop = js_DropProperty(cx, prop);
			PR_ASSERT(prop);
			return JS_TRUE;
		    }
		}
	    }
#endif
	    scope->ops->remove(cx, scope, id);
	}
	proto = OBJ_GET_PROTO(obj);
	if (!proto)
	    return JS_TRUE;
    }

    /* Search shared prototype scopes for an inherited property to hide. */
    hash = js_HashValue(id);
    do {
	scope = (JSScope *)proto->map;
	sym = scope->ops->lookup(cx, scope, id, hash);
	if (sym) {
	    /* Add a null-valued symbol to hide the prototype property. */
	    scope = js_GetMutableScope(cx, obj);
	    if (!scope)
		return JS_FALSE;
	    if (!scope->ops->add(cx, scope, id, NULL))
		return JS_FALSE;
	    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,
				PROP_NOT_FOUND);
	    return JS_TRUE;
	}
	proto = OBJ_GET_PROTO(proto);
    } while (proto);
    return JS_TRUE;
#else
    jsval null = JSVAL_NULL;
    return (js_SetProperty(cx, obj, id, &null) != NULL);
#endif
}
예제 #7
0
JSProperty *
js_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    JSRuntime *rt;
    JSScope *scope, *protoScope;
    JSProperty *prop, *protoProp;
    PRHashNumber hash;
    JSSymbol *sym, *protoSym;
    JSObject *proto, *assignobj;
    jsval pval, aval, rval;
    jsint slot;
    JSErrorReporter older;
    JSString *str;

    rt = cx->runtime;
    PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt));

#ifdef SCOPE_TABLE_NOTYET
    /* XXX hash recompute */
    /* XXX protoProp->getter, protoProp->setter, protoProp->flags */
    scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop);
#endif
    scope = js_GetMutableScope(cx, obj);
    if (!scope)
	return NULL;

    /* Handle old bug that treated empty string as zero index. */
    CHECK_FOR_FUNNY_INDEX(id);

    hash = js_HashValue(id);
    sym = scope->ops->lookup(cx, scope, id, hash);
    if (sym) {
	prop = sym_property(sym);
#if JS_HAS_OBJ_WATCHPOINT
	if (!prop) {
	    /*
	     * Deleted property place-holder, could have a watchpoint that
	     * holds the deleted-but-watched property.
	     */
	    prop = js_FindWatchPoint(rt, obj, js_IdToValue(id));
	}
#endif
    } else {
	prop = NULL;
    }

    if (!prop) {
	/* Find a prototype property with the same id. */
	proto = OBJ_GET_PROTO(obj);
	protoProp = NULL;
	while (proto) {
	    protoScope = (JSScope *)proto->map;
	    protoSym = protoScope->ops->lookup(cx, protoScope, id, hash);
	    if (protoSym) {
		protoProp = sym_property(protoSym);
		if (protoProp)
		    break;
	    }
	    proto = OBJ_GET_PROTO(proto);
	}

	/* Make a new property descriptor with the right heritage. */
	if (protoProp) {
	    prop = js_NewProperty(cx, scope, id,
				  protoProp->getter, protoProp->setter,
				  protoProp->flags | JSPROP_ENUMERATE);
	    prop->id = protoProp->id;
	} else {
	    prop = js_NewProperty(cx, scope, id,
				  scope->map.clasp->getProperty,
				  scope->map.clasp->setProperty,
				  JSPROP_ENUMERATE);
	}
	if (!prop)
	    return NULL;

	if (!obj->map->clasp->addProperty(cx, obj, prop->id, vp)) {
	    js_DestroyProperty(cx, prop);
	    return NULL;
	}

	/* Initialize new properties to undefined. */
	obj->slots[prop->slot] = JSVAL_VOID;

	if (sym) {
	    /* Null-valued symbol left behind from a delete operation. */
	    sym->entry.value = js_HoldProperty(cx, prop);
	}
    }

    if (!sym) {
	/* Need a new symbol as well as a new property. */
	sym = scope->ops->add(cx, scope, id, prop);
	if (!sym)
	    return NULL;
#if JS_BUG_AUTO_INDEX_PROPS
	{
	    jsval id2 = INT_TO_JSVAL(prop->slot - JSSLOT_START);
	    if (!scope->ops->add(cx, scope, id2, prop)) {
		scope->ops->remove(cx, scope, id);
		return NULL;
	    }
	    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2, prop);
	}
#endif
	PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop);
    }

    /* Get the current property value from its slot. */
    PR_ASSERT(prop->slot < obj->map->freeslot);
    slot = prop->slot;
    pval = obj->slots[slot];

    /* Evil overloaded operator assign() hack. */
    if (JSVAL_IS_OBJECT(pval)) {
	assignobj = JSVAL_TO_OBJECT(pval);
	if (assignobj) {
	    older = JS_SetErrorReporter(cx, NULL);
	    if (js_GetProperty(cx, assignobj, (jsval)rt->atomState.assignAtom,
			       &aval) &&
		JSVAL_IS_FUNCTION(aval) &&
		js_Call(cx, assignobj, aval, 1, vp, &rval)) {
		*vp = rval;
		JS_SetErrorReporter(cx, older);
		prop->flags |= JSPROP_ASSIGNHACK;
		return prop;
	    }
	    JS_SetErrorReporter(cx, older);
	}
    }

    /* Check for readonly *after* the assign() hack. */
    if (prop->flags & JSPROP_READONLY) {
	if (!JSVERSION_IS_ECMA(cx->version)) {
	    str = js_ValueToSource(cx, js_IdToValue(id));
	    if (str) {
		JS_ReportError(cx, "%s is read-only",
			       JS_GetStringBytes(str));
	    }
	}
	return NULL;
    }

    /* Let the setter modify vp before copying from it to obj->slots[slot]. */
    if (!prop->setter(cx, obj, prop->id, vp))
	return NULL;
    GC_POKE(cx, pval);
    obj->slots[slot] = *vp;

    /* Setting a property makes it enumerable. */
    prop->flags |= JSPROP_ENUMERATE;
    return prop;
}