michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JavaScript Debugging support - Value and Property support michael@0: */ michael@0: michael@0: #include "jsd.h" michael@0: #include "jsapi.h" michael@0: #include "jsfriendapi.h" michael@0: #include "jswrapper.h" michael@0: #include "nsCxPusher.h" michael@0: michael@0: using mozilla::AutoSafeJSContext; michael@0: michael@0: #ifdef DEBUG michael@0: void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval) michael@0: { michael@0: MOZ_ASSERT(jsdval); michael@0: MOZ_ASSERT(jsdval->nref > 0); michael@0: if(!JS_CLIST_IS_EMPTY(&jsdval->props)) michael@0: { michael@0: MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)); michael@0: MOZ_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val)); michael@0: } michael@0: michael@0: if(jsdval->proto) michael@0: { michael@0: MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO)); michael@0: MOZ_ASSERT(jsdval->proto->nref > 0); michael@0: } michael@0: if(jsdval->parent) michael@0: { michael@0: MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT)); michael@0: MOZ_ASSERT(jsdval->parent->nref > 0); michael@0: } michael@0: if(jsdval->ctor) michael@0: { michael@0: MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR)); michael@0: MOZ_ASSERT(jsdval->ctor->nref > 0); michael@0: } michael@0: } michael@0: michael@0: void JSD_ASSERT_VALID_PROPERTY(JSDProperty* jsdprop) michael@0: { michael@0: MOZ_ASSERT(jsdprop); michael@0: MOZ_ASSERT(jsdprop->name); michael@0: MOZ_ASSERT(jsdprop->name->nref > 0); michael@0: MOZ_ASSERT(jsdprop->val); michael@0: MOZ_ASSERT(jsdprop->val->nref > 0); michael@0: if(jsdprop->alias) michael@0: MOZ_ASSERT(jsdprop->alias->nref > 0); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: bool michael@0: jsd_IsValueObject(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return !JSVAL_IS_PRIMITIVE(jsdval->val) || JSVAL_IS_NULL(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_NUMBER(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueInt(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_INT(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_DOUBLE(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueString(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_STRING(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_BOOLEAN(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueNull(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_NULL(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_VOID(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: return JSVAL_IS_PRIMITIVE(jsdval->val); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; // NB: Actually unused. michael@0: return !JSVAL_IS_PRIMITIVE(jsdval->val) && michael@0: JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(jsdval->val)); michael@0: } michael@0: michael@0: bool michael@0: jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedFunction fun(cx); michael@0: michael@0: if(jsd_IsValueFunction(jsdc, jsdval)) michael@0: { michael@0: JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(jsdval->val)); michael@0: AutoSaveExceptionState as(cx); michael@0: bool ok = false; michael@0: fun = JSD_GetValueFunction(jsdc, jsdval); michael@0: if(fun) michael@0: ok = JS_GetFunctionScript(cx, fun) ? false : true; michael@0: MOZ_ASSERT(fun); michael@0: return ok; michael@0: } michael@0: return !JSVAL_IS_PRIMITIVE(jsdval->val); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: bool michael@0: jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: jsval val = jsdval->val; michael@0: if(!JSVAL_IS_BOOLEAN(val)) michael@0: return false; michael@0: return JSVAL_TO_BOOLEAN(val); michael@0: } michael@0: michael@0: int32_t michael@0: jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: jsval val = jsdval->val; michael@0: if(!JSVAL_IS_INT(val)) michael@0: return 0; michael@0: return JSVAL_TO_INT(val); michael@0: } michael@0: michael@0: double michael@0: jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: if(!JSVAL_IS_DOUBLE(jsdval->val)) michael@0: return 0; michael@0: return JSVAL_TO_DOUBLE(jsdval->val); michael@0: } michael@0: michael@0: JSString* michael@0: jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedValue stringval(cx); michael@0: JS::RootedString string(cx); michael@0: JS::RootedObject scopeObj(cx); michael@0: michael@0: if(jsdval->string) michael@0: return jsdval->string; michael@0: michael@0: /* Reuse the string without copying or re-rooting it */ michael@0: if(JSVAL_IS_STRING(jsdval->val)) { michael@0: jsdval->string = JSVAL_TO_STRING(jsdval->val); michael@0: return jsdval->string; michael@0: } michael@0: michael@0: /* Objects call JS_ValueToString in their own compartment. */ michael@0: scopeObj = !JSVAL_IS_PRIMITIVE(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; michael@0: { michael@0: JSAutoCompartment ac(cx, scopeObj); michael@0: AutoSaveExceptionState as(cx); michael@0: JS::RootedValue v(cx, jsdval->val); michael@0: string = JS::ToString(cx, v); michael@0: } michael@0: michael@0: JSAutoCompartment ac2(cx, jsdc->glob); michael@0: if(string) { michael@0: stringval = STRING_TO_JSVAL(string); michael@0: } michael@0: if(!string || !JS_WrapValue(cx, &stringval)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: jsdval->string = JSVAL_TO_STRING(stringval); michael@0: if(!JS::AddNamedStringRoot(cx, &jsdval->string, "ValueString")) michael@0: jsdval->string = nullptr; michael@0: michael@0: return jsdval->string; michael@0: } michael@0: michael@0: JSString* michael@0: jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedFunction fun(cx); michael@0: michael@0: if(!jsdval->funName && jsd_IsValueFunction(jsdc, jsdval)) michael@0: { michael@0: JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(jsdval->val)); michael@0: AutoSaveExceptionState as(cx); michael@0: fun = JSD_GetValueFunction(jsdc, jsdval); michael@0: if(!fun) michael@0: return nullptr; michael@0: jsdval->funName = JS_GetFunctionId(fun); michael@0: michael@0: /* For compatibility we return "anonymous", not an empty string here. */ michael@0: if (!jsdval->funName) michael@0: jsdval->funName = JS_GetAnonymousString(jsdc->jsrt); michael@0: } michael@0: return jsdval->funName; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: /* michael@0: * Create a new JSD value referring to a jsval. Copy string values into the michael@0: * JSD compartment. Leave all other GCTHINGs in their native compartments michael@0: * and access them through cross-compartment calls. michael@0: */ michael@0: JSDValue* michael@0: jsd_NewValue(JSDContext* jsdc, jsval value) michael@0: { michael@0: JS::RootedValue val(jsdc->jsrt, value); michael@0: AutoSafeJSContext cx; michael@0: JSDValue* jsdval; michael@0: michael@0: if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue)))) michael@0: return nullptr; michael@0: michael@0: if(JSVAL_IS_GCTHING(val)) michael@0: { michael@0: bool ok; michael@0: JSAutoCompartment ac(cx, jsdc->glob); michael@0: michael@0: ok = JS::AddNamedValueRoot(cx, &jsdval->val, "JSDValue"); michael@0: if(ok && JSVAL_IS_STRING(val)) { michael@0: if(!JS_WrapValue(cx, &val)) { michael@0: ok = false; michael@0: } michael@0: } michael@0: michael@0: if(!ok) michael@0: { michael@0: free(jsdval); michael@0: return nullptr; michael@0: } michael@0: } michael@0: jsdval->val = val; michael@0: jsdval->nref = 1; michael@0: JS_INIT_CLIST(&jsdval->props); michael@0: michael@0: return jsdval; michael@0: } michael@0: michael@0: void michael@0: jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: MOZ_ASSERT(jsdval->nref > 0); michael@0: if(0 == --jsdval->nref) michael@0: { michael@0: jsd_RefreshValue(jsdc, jsdval); michael@0: if(JSVAL_IS_GCTHING(jsdval->val)) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, jsdc->glob); michael@0: JS::RemoveValueRoot(cx, &jsdval->val); michael@0: } michael@0: free(jsdval); michael@0: } michael@0: } michael@0: michael@0: jsval michael@0: jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedValue val(cx, jsdval->val); michael@0: if (!val.isPrimitive()) { michael@0: JS::RootedObject obj(cx, &val.toObject()); michael@0: JSAutoCompartment ac(cx, obj); michael@0: obj = JS_ObjectToOuterObject(cx, obj); michael@0: if (!obj) michael@0: { michael@0: JS_ClearPendingException(cx); michael@0: val = JSVAL_NULL; michael@0: } michael@0: else michael@0: val = JS::ObjectValue(*obj); michael@0: } michael@0: michael@0: return val; michael@0: } michael@0: michael@0: static JSDProperty* _newProperty(JSDContext* jsdc, JS::HandleValue propId, michael@0: JS::HandleValue propValue, JS::HandleValue propAlias, michael@0: uint8_t propFlags, unsigned additionalFlags) michael@0: { michael@0: JSDProperty* jsdprop; michael@0: michael@0: if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty)))) michael@0: return nullptr; michael@0: michael@0: JS_INIT_CLIST(&jsdprop->links); michael@0: jsdprop->nref = 1; michael@0: jsdprop->flags = propFlags | additionalFlags; michael@0: michael@0: if(!(jsdprop->name = jsd_NewValue(jsdc, propId))) michael@0: goto new_prop_fail; michael@0: michael@0: if(!(jsdprop->val = jsd_NewValue(jsdc, propValue))) michael@0: goto new_prop_fail; michael@0: michael@0: if((jsdprop->flags & JSDPD_ALIAS) && michael@0: !(jsdprop->alias = jsd_NewValue(jsdc, propAlias))) michael@0: goto new_prop_fail; michael@0: michael@0: return jsdprop; michael@0: new_prop_fail: michael@0: jsd_DropProperty(jsdc, jsdprop); michael@0: return nullptr; michael@0: } michael@0: michael@0: static void _freeProps(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: JSDProperty* jsdprop; michael@0: michael@0: while(jsdprop = (JSDProperty*)jsdval->props.next, michael@0: jsdprop != (JSDProperty*)&jsdval->props) michael@0: { michael@0: JS_REMOVE_AND_INIT_LINK(&jsdprop->links); michael@0: jsd_DropProperty(jsdc, jsdprop); michael@0: } michael@0: MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props)); michael@0: CLEAR_BIT_FLAG(jsdval->flags, GOT_PROPS); michael@0: } michael@0: michael@0: static bool _buildProps(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedObject obj(cx); michael@0: JSPropertyDescArray pda; michael@0: unsigned i; michael@0: michael@0: MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props)); michael@0: MOZ_ASSERT(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))); michael@0: MOZ_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val)); michael@0: michael@0: if(JSVAL_IS_PRIMITIVE(jsdval->val)) michael@0: return false; michael@0: michael@0: obj = JSVAL_TO_OBJECT(jsdval->val); michael@0: michael@0: JSAutoCompartment ac(cx, obj); michael@0: michael@0: if(!JS_GetPropertyDescArray(cx, obj, &pda)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: JS::RootedValue propId(cx); michael@0: JS::RootedValue propValue(cx); michael@0: JS::RootedValue propAlias(cx); michael@0: uint8_t propFlags; michael@0: for(i = 0; i < pda.length; i++) michael@0: { michael@0: propId = pda.array[i].id; michael@0: propValue = pda.array[i].value; michael@0: propAlias = pda.array[i].alias; michael@0: propFlags = pda.array[i].flags; michael@0: JSDProperty* prop = _newProperty(jsdc, propId, propValue, propAlias, propFlags, 0); michael@0: if(!prop) michael@0: { michael@0: _freeProps(jsdc, jsdval); michael@0: break; michael@0: } michael@0: JS_APPEND_LINK(&prop->links, &jsdval->props); michael@0: } michael@0: JS_PutPropertyDescArray(cx, &pda); michael@0: SET_BIT_FLAG(jsdval->flags, GOT_PROPS); michael@0: return !JS_CLIST_IS_EMPTY(&jsdval->props); michael@0: } michael@0: michael@0: #undef DROP_CLEAR_VALUE michael@0: #define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = nullptr;} michael@0: michael@0: void michael@0: jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: if(jsdval->string) michael@0: { michael@0: /* if the jsval is a string, then we didn't need to root the string */ michael@0: if(!JSVAL_IS_STRING(jsdval->val)) michael@0: { michael@0: JSAutoCompartment ac(cx, jsdc->glob); michael@0: JS::RemoveStringRoot(cx, &jsdval->string); michael@0: } michael@0: jsdval->string = nullptr; michael@0: } michael@0: michael@0: jsdval->funName = nullptr; michael@0: jsdval->className = nullptr; michael@0: DROP_CLEAR_VALUE(jsdc, jsdval->proto); michael@0: DROP_CLEAR_VALUE(jsdc, jsdval->parent); michael@0: DROP_CLEAR_VALUE(jsdc, jsdval->ctor); michael@0: _freeProps(jsdc, jsdval); michael@0: jsdval->flags = 0; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: unsigned michael@0: jsd_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: JSDProperty* jsdprop; michael@0: unsigned count = 0; michael@0: michael@0: if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))) michael@0: if(!_buildProps(jsdc, jsdval)) michael@0: return 0; michael@0: michael@0: for(jsdprop = (JSDProperty*)jsdval->props.next; michael@0: jsdprop != (JSDProperty*)&jsdval->props; michael@0: jsdprop = (JSDProperty*)jsdprop->links.next) michael@0: { michael@0: count++; michael@0: } michael@0: return count; michael@0: } michael@0: michael@0: JSDProperty* michael@0: jsd_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp) michael@0: { michael@0: JSDProperty* jsdprop = *iterp; michael@0: if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))) michael@0: { michael@0: MOZ_ASSERT(!jsdprop); michael@0: if(!_buildProps(jsdc, jsdval)) michael@0: return nullptr; michael@0: } michael@0: michael@0: if(!jsdprop) michael@0: jsdprop = (JSDProperty*)jsdval->props.next; michael@0: if(jsdprop == (JSDProperty*)&jsdval->props) michael@0: return nullptr; michael@0: *iterp = (JSDProperty*)jsdprop->links.next; michael@0: michael@0: MOZ_ASSERT(jsdprop); michael@0: jsdprop->nref++; michael@0: return jsdprop; michael@0: } michael@0: michael@0: JSDProperty* michael@0: jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* nameStr) michael@0: { michael@0: JS::RootedString name(jsdc->jsrt, nameStr); michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment acBase(cx, jsdc->glob); michael@0: JSDProperty* jsdprop; michael@0: JSDProperty* iter = nullptr; michael@0: JS::RootedObject obj(cx); michael@0: JS::RootedValue val(cx), nameval(cx); michael@0: JS::RootedId nameid(cx); michael@0: JS::RootedValue propId(cx); michael@0: JS::RootedValue propValue(cx); michael@0: JS::RootedValue propAlias(cx); michael@0: uint8_t propFlags; michael@0: michael@0: if(!jsd_IsValueObject(jsdc, jsdval)) michael@0: return nullptr; michael@0: michael@0: /* If we already have the prop, then return it */ michael@0: while(nullptr != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter))) michael@0: { michael@0: JSString* propName = jsd_GetValueString(jsdc, jsdprop->name); michael@0: if(propName) { michael@0: int result; michael@0: if (JS_CompareStrings(cx, propName, name, &result) && !result) michael@0: return jsdprop; michael@0: } michael@0: JSD_DropProperty(jsdc, jsdprop); michael@0: } michael@0: /* Not found in property list, look it up explicitly */ michael@0: michael@0: nameval = STRING_TO_JSVAL(name); michael@0: if(!JS_ValueToId(cx, nameval, &nameid)) michael@0: return nullptr; michael@0: michael@0: if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) michael@0: return nullptr; michael@0: michael@0: JS::Rooted desc(cx); michael@0: { michael@0: JSAutoCompartment ac(cx, obj); michael@0: JS::RootedId id(cx, nameid); michael@0: michael@0: if(!JS_WrapId(cx, &id)) michael@0: return nullptr; michael@0: if(!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) michael@0: return nullptr; michael@0: if(!desc.object()) michael@0: return nullptr; michael@0: michael@0: JS_ClearPendingException(cx); michael@0: michael@0: if(!JS_GetPropertyById(cx, obj, id, &val)) michael@0: { michael@0: if (JS_IsExceptionPending(cx)) michael@0: { michael@0: if (!JS_GetPendingException(cx, &propValue)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: propFlags = JSPD_EXCEPTION; michael@0: } michael@0: else michael@0: { michael@0: propFlags = JSPD_ERROR; michael@0: propValue = JSVAL_VOID; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: propValue = val; michael@0: } michael@0: } michael@0: michael@0: if (!JS_IdToValue(cx, nameid, &propId)) michael@0: return nullptr; michael@0: michael@0: propAlias = JSVAL_NULL; michael@0: propFlags |= desc.isEnumerable() ? JSPD_ENUMERATE : 0 michael@0: | desc.isReadonly() ? JSPD_READONLY : 0 michael@0: | desc.isPermanent() ? JSPD_PERMANENT : 0; michael@0: michael@0: return _newProperty(jsdc, propId, propValue, propAlias, propFlags, JSDPD_HINTED); michael@0: } michael@0: michael@0: /* michael@0: * Retrieve a JSFunction* from a JSDValue*. This differs from michael@0: * JS_ValueToFunction by fully unwrapping the object first. michael@0: */ michael@0: JSFunction* michael@0: jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: michael@0: JS::RootedObject obj(cx); michael@0: JS::RootedFunction fun(cx); michael@0: michael@0: if (JSVAL_IS_PRIMITIVE(jsdval->val)) michael@0: return nullptr; michael@0: michael@0: obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(jsdval->val)); michael@0: JSAutoCompartment ac(cx, obj); michael@0: JS::RootedValue funval(cx, JS::ObjectValue(*obj)); michael@0: fun = JS_ValueToFunction(cx, funval); michael@0: michael@0: return fun; michael@0: } michael@0: michael@0: JSDValue* michael@0: jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO))) michael@0: { michael@0: JS::RootedObject obj(cx); michael@0: JS::RootedObject proto(cx); michael@0: MOZ_ASSERT(!jsdval->proto); michael@0: SET_BIT_FLAG(jsdval->flags, GOT_PROTO); michael@0: if(JSVAL_IS_PRIMITIVE(jsdval->val)) michael@0: return nullptr; michael@0: obj = JSVAL_TO_OBJECT(jsdval->val); michael@0: if(!JS_GetPrototype(cx, obj, &proto)) michael@0: return nullptr; michael@0: if(!proto) michael@0: return nullptr; michael@0: jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto)); michael@0: } michael@0: if(jsdval->proto) michael@0: jsdval->proto->nref++; michael@0: return jsdval->proto; michael@0: } michael@0: michael@0: JSDValue* michael@0: jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT))) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedObject obj(cx); michael@0: JS::RootedObject parent(cx); michael@0: MOZ_ASSERT(!jsdval->parent); michael@0: SET_BIT_FLAG(jsdval->flags, GOT_PARENT); michael@0: if(JSVAL_IS_PRIMITIVE(jsdval->val)) michael@0: return nullptr; michael@0: obj = JSVAL_TO_OBJECT(jsdval->val); michael@0: { michael@0: JSAutoCompartment ac(cx, obj); michael@0: parent = JS_GetParentOrScopeChain(cx, obj); michael@0: } michael@0: if(!parent) michael@0: return nullptr; michael@0: jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent)); michael@0: } michael@0: if(jsdval->parent) michael@0: jsdval->parent->nref++; michael@0: return jsdval->parent; michael@0: } michael@0: michael@0: JSDValue* michael@0: jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR))) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedObject obj(cx); michael@0: JS::RootedObject proto(cx); michael@0: JS::RootedObject ctor(cx); michael@0: MOZ_ASSERT(!jsdval->ctor); michael@0: SET_BIT_FLAG(jsdval->flags, GOT_CTOR); michael@0: if(JSVAL_IS_PRIMITIVE(jsdval->val)) michael@0: return nullptr; michael@0: obj = JSVAL_TO_OBJECT(jsdval->val); michael@0: if(!JS_GetPrototype(cx, obj, &proto)) michael@0: return nullptr; michael@0: if(!proto) michael@0: return nullptr; michael@0: { michael@0: JSAutoCompartment ac(cx, obj); michael@0: ctor = JS_GetConstructor(cx, proto); michael@0: } michael@0: if(!ctor) michael@0: return nullptr; michael@0: jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor)); michael@0: } michael@0: if(jsdval->ctor) michael@0: jsdval->ctor->nref++; michael@0: return jsdval->ctor; michael@0: } michael@0: michael@0: const char* michael@0: jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: jsval val = jsdval->val; michael@0: if(!jsdval->className && !JSVAL_IS_PRIMITIVE(val)) michael@0: { michael@0: JS::RootedObject obj(jsdc->jsrt, JSVAL_TO_OBJECT(val)); michael@0: AutoSafeJSContext cx; michael@0: JSAutoCompartment ac(cx, obj); michael@0: jsdval->className = JS_GetDebugClassName(obj); michael@0: } michael@0: return jsdval->className; michael@0: } michael@0: michael@0: JSDScript* michael@0: jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::RootedValue val(cx, jsdval->val); michael@0: JS::RootedScript script(cx); michael@0: JSDScript* jsdscript; michael@0: michael@0: if (!jsd_IsValueFunction(jsdc, jsdval)) michael@0: return nullptr; michael@0: michael@0: { michael@0: JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(val)); michael@0: AutoSaveExceptionState as(cx); michael@0: JS::RootedFunction fun(cx, JSD_GetValueFunction(jsdc, jsdval)); michael@0: if (fun) michael@0: script = JS_GetFunctionScript(cx, fun); michael@0: } michael@0: michael@0: if (!script) michael@0: return nullptr; michael@0: michael@0: JSD_LOCK_SCRIPTS(jsdc); michael@0: jsdscript = jsd_FindJSDScript(jsdc, script); michael@0: JSD_UNLOCK_SCRIPTS(jsdc); michael@0: return jsdscript; michael@0: } michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: /***************************************************************************/ michael@0: michael@0: JSDValue* michael@0: jsd_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop) michael@0: { michael@0: jsdprop->name->nref++; michael@0: return jsdprop->name; michael@0: } michael@0: michael@0: JSDValue* michael@0: jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop) michael@0: { michael@0: jsdprop->val->nref++; michael@0: return jsdprop->val; michael@0: } michael@0: michael@0: JSDValue* michael@0: jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop) michael@0: { michael@0: if(jsdprop->alias) michael@0: jsdprop->alias->nref++; michael@0: return jsdprop->alias; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop) michael@0: { michael@0: return jsdprop->flags; michael@0: } michael@0: michael@0: void michael@0: jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop) michael@0: { michael@0: MOZ_ASSERT(jsdprop->nref > 0); michael@0: if(0 == --jsdprop->nref) michael@0: { michael@0: MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdprop->links)); michael@0: DROP_CLEAR_VALUE(jsdc, jsdprop->val); michael@0: DROP_CLEAR_VALUE(jsdc, jsdprop->name); michael@0: DROP_CLEAR_VALUE(jsdc, jsdprop->alias); michael@0: free(jsdprop); michael@0: } michael@0: }