/* liberally copied from mozilla shell source (js.cpp) (MPL 1.1/GPL 2.0/LGPL 2.1) */ // Changes from js.cpp: Equality does more checking; I think the // original checking is wrong (if you want inner == outer) // The newer moz sources override thisobject. The code is below but // not used. The current interpreter seems to do inner/outer incorrectly // in one place with that callback in place. Deserves relooking at // when upgrading to new SM. #include "split_global.h" #define LAZY_STANDARD_CLASSES /* A class for easily testing the inner/outer object callbacks. */ typedef struct ComplexObject { JSBool isInner; JSBool frozen; JSObject *inner; JSObject *outer; } ComplexObject; static JSObject * split_create_outer(JSContext *cx); static JSObject * split_create_inner(JSContext *cx, JSObject *outer); static ComplexObject * split_get_private(JSContext *cx, JSObject *obj); static JSBool split_addProperty(JSContext *cx, JSObject *obj, jsval, jsval *vp) { ComplexObject *cpx; jsid asId; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { /* Make sure to define this property on the inner object. */ if (!JS_ValueToId(cx, *vp, &asId)) return JS_FALSE; return OBJ_DEFINE_PROPERTY(cx, cpx->inner, asId, *vp, NULL, NULL, JSPROP_ENUMERATE, NULL); } return JS_TRUE; } static JSBool split_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { ComplexObject *cpx; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { if (JSVAL_IS_STRING(id)) { JSString *str; str = JSVAL_TO_STRING(id); return JS_GetUCProperty(cx, cpx->inner, JS_GetStringChars(str), JS_GetStringLength(str), vp); } if (JSVAL_IS_INT(id)) return JS_GetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); return JS_TRUE; } return JS_TRUE; } static JSBool split_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { ComplexObject *cpx; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { if (JSVAL_IS_STRING(id)) { JSString *str; str = JSVAL_TO_STRING(id); return JS_SetUCProperty(cx, cpx->inner, JS_GetStringChars(str), JS_GetStringLength(str), vp); } if (JSVAL_IS_INT(id)) return JS_SetElement(cx, cpx->inner, JSVAL_TO_INT(id), vp); return JS_TRUE; } return JS_TRUE; } static JSBool split_delProperty(JSContext *cx, JSObject *obj, jsval, jsval *vp) { ComplexObject *cpx; jsid asId; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { /* Make sure to define this property on the inner object. */ if (!JS_ValueToId(cx, *vp, &asId)) return JS_FALSE; return OBJ_DELETE_PROPERTY(cx, cpx->inner, asId, vp); } return JS_TRUE; } static JSBool split_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, jsval *statep, jsid *idp) { ComplexObject *cpx; JSObject *iterator; switch (enum_op) { case JSENUMERATE_INIT: cpx = (ComplexObject *) JS_GetPrivate(cx, obj); if (!cpx->isInner && cpx->inner) obj = cpx->inner; iterator = JS_NewPropertyIterator(cx, obj); if (!iterator) return JS_FALSE; *statep = OBJECT_TO_JSVAL(iterator); if (idp) *idp = JSVAL_ZERO; break; case JSENUMERATE_NEXT: iterator = (JSObject*)JSVAL_TO_OBJECT(*statep); if (!JS_NextProperty(cx, iterator, idp)) return JS_FALSE; if (!JSVAL_IS_VOID(*idp)) break; /* Fall through. */ case JSENUMERATE_DESTROY: /* Let GC at our iterator object. */ *statep = JSVAL_NULL; break; } return JS_TRUE; } static JSBool split_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) { ComplexObject *cpx; cpx = split_get_private(cx, obj); if (!cpx) return JS_TRUE; if (!cpx->isInner && cpx->inner) { jsid asId; JSProperty *prop; if (!JS_ValueToId(cx, id, &asId)) return JS_FALSE; if (!OBJ_LOOKUP_PROPERTY(cx, cpx->inner, asId, objp, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, cpx->inner, prop); return JS_TRUE; } #ifdef LAZY_STANDARD_CLASSES if (!(flags & JSRESOLVE_ASSIGNING)) { JSBool resolved; if (!JS_ResolveStandardClass(cx, obj, id, &resolved)) return JS_FALSE; if (resolved) { *objp = obj; return JS_TRUE; } } #endif /* XXX For additional realism, let's resolve some random property here. */ return JS_TRUE; } static void split_finalize(JSContext *cx, JSObject *obj) { JS_free(cx, JS_GetPrivate(cx, obj)); } static uint32 split_mark(JSContext *cx, JSObject *obj, void *arg) { ComplexObject *cpx; cpx = (ComplexObject *) JS_GetPrivate(cx, obj); if (!cpx->isInner && cpx->inner) { /* Mark the inner object. */ JS_MarkGCThing(cx, cpx->inner, "ComplexObject.inner", arg); } return 0; } static JSObject * split_outerObject(JSContext *cx, JSObject *obj) { ComplexObject *cpx; cpx = (ComplexObject *) JS_GetPrivate(cx, obj); return cpx->isInner ? cpx->outer : obj; } static JSObject * split_thisObject(JSContext *cx, JSObject *obj) { OBJ_TO_OUTER_OBJECT(cx, obj); if (!obj) return NULL; return obj; } static JSObjectOps split_objectops; static JSObjectOps * split_getObjectOps(JSContext *cx, JSClass *clasp) { if (!split_objectops.thisObject) { memcpy(&split_objectops, &js_ObjectOps, sizeof split_objectops); split_objectops.thisObject = split_thisObject; } return &split_objectops; } static JSBool split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); static JSObject * split_innerObject(JSContext *cx, JSObject *obj) { ComplexObject *cpx; cpx = (ComplexObject *) JS_GetPrivate(cx, obj); if (cpx->frozen) { JS_ASSERT(!cpx->isInner); return obj; } return !cpx->isInner ? cpx->inner : obj; } static JSExtendedClass split_global_class = { {"split_global", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE | JSCLASS_GLOBAL_FLAGS | JSCLASS_IS_EXTENDED, split_addProperty, split_delProperty, split_getProperty, split_setProperty, (JSEnumerateOp)split_enumerate, (JSResolveOp)split_resolve, JS_ConvertStub, split_finalize, /* split_getObjectOps */ NULL, NULL, NULL, NULL, NULL, NULL, split_mark, NULL}, split_equality, split_outerObject, split_innerObject, NULL, NULL, NULL, NULL, NULL }; static JSBool split_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { *bp = JS_FALSE; if (JSVAL_IS_PRIMITIVE(v)) return JS_TRUE; JSObject *obj2 = JSVAL_TO_OBJECT(v); if (JS_GET_CLASS(cx, obj2) != &split_global_class.base) return JS_TRUE; ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2); ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj); if( cpx == ourCpx ) { *bp = JS_TRUE; } else if ( cpx->inner ) { *bp = ( cpx->inner == obj && ourCpx->outer == obj2); } else { *bp = ( cpx->outer == obj && ourCpx->inner == obj2); } return JS_TRUE; } JSObject * split_create_outer(JSContext *cx) { ComplexObject *cpx; JSObject *obj; cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj); if (!cpx) return NULL; cpx->isInner = JS_FALSE; cpx->frozen = JS_TRUE; cpx->inner = NULL; cpx->outer = NULL; obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); if (!obj || !JS_SetParent(cx, obj, NULL)) { JS_free(cx, cpx); return NULL; } if (!JS_SetPrivate(cx, obj, cpx)) { JS_free(cx, cpx); return NULL; } return obj; } static JSObject * split_create_inner(JSContext *cx, JSObject *outer) { ComplexObject *cpx, *outercpx; JSObject *obj; JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base); cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx); if (!cpx) return NULL; cpx->isInner = JS_TRUE; cpx->frozen = JS_FALSE; cpx->inner = NULL; cpx->outer = outer; obj = JS_NewObject(cx, &split_global_class.base, NULL, NULL); if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) { JS_free(cx, cpx); return NULL; } outercpx = (ComplexObject *) JS_GetPrivate(cx, outer); outercpx->inner = obj; outercpx->frozen = JS_FALSE; return obj; } static ComplexObject * split_get_private(JSContext *cx, JSObject *obj) { do { if (JS_GET_CLASS(cx, obj) == &split_global_class.base) return (ComplexObject *) JS_GetPrivate(cx, obj); obj = JS_GetParent(cx, obj); } while (obj); return NULL; } JSObject* johnson_create_split_global_outer_object(JSContext* cx) { JSObject* outer = split_create_outer(cx); // fprintf(stderr,"outer %08x\n", outer); return outer; } JSObject* johnson_create_split_global_inner_object(JSContext* cx, JSObject* outer) { // JSObject* current = JS_GetGlobalObject(cx); // JS_SetGlobalObject(cx, outer); JSObject* inner = split_create_inner(cx, outer); JS_ClearScope(cx, outer); // fprintf(stderr,"inner %08x outer %08x\n", inner, outer); // JS_SetGlobalObject(cx, current); return inner; }