static JSObject * CloneObject(JSContext *cx, JSObject *selfHostedObject, CloneMemory &clonedObjects) { DependentAddPtr<CloneMemory> p(cx, clonedObjects, selfHostedObject); if (p) return p->value(); RootedObject clone(cx); if (selfHostedObject->is<JSFunction>()) { JSFunction *selfHostedFunction = &selfHostedObject->as<JSFunction>(); bool hasName = selfHostedFunction->atom() != nullptr; js::gc::AllocKind kind = hasName ? JSFunction::ExtendedFinalizeKind : selfHostedFunction->getAllocKind(); clone = CloneFunctionObject(cx, HandleFunction::fromMarkedLocation(&selfHostedFunction), cx->global(), kind, TenuredObject); // To be able to re-lazify the cloned function, its name in the // self-hosting compartment has to be stored on the clone. if (clone && hasName) clone->as<JSFunction>().setExtendedSlot(0, StringValue(selfHostedFunction->atom())); } else if (selfHostedObject->is<RegExpObject>()) { RegExpObject &reobj = selfHostedObject->as<RegExpObject>(); RootedAtom source(cx, reobj.getSource()); JS_ASSERT(source->isPermanentAtom()); clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), nullptr); } else if (selfHostedObject->is<DateObject>()) { clone = JS_NewDateObjectMsec(cx, selfHostedObject->as<DateObject>().UTCTime().toNumber()); } else if (selfHostedObject->is<BooleanObject>()) { clone = BooleanObject::create(cx, selfHostedObject->as<BooleanObject>().unbox()); } else if (selfHostedObject->is<NumberObject>()) { clone = NumberObject::create(cx, selfHostedObject->as<NumberObject>().unbox()); } else if (selfHostedObject->is<StringObject>()) { JSString *selfHostedString = selfHostedObject->as<StringObject>().unbox(); if (!selfHostedString->isFlat()) MOZ_CRASH(); RootedString str(cx, js_NewStringCopyN<CanGC>(cx, selfHostedString->asFlat().chars(), selfHostedString->asFlat().length())); if (!str) return nullptr; clone = StringObject::create(cx, str); } else if (selfHostedObject->is<ArrayObject>()) { clone = NewDenseEmptyArray(cx, nullptr, TenuredObject); } else { JS_ASSERT(selfHostedObject->isNative()); clone = NewObjectWithGivenProto(cx, selfHostedObject->getClass(), nullptr, cx->global(), selfHostedObject->tenuredGetAllocKind(), SingletonObject); } if (!clone) return nullptr; if (!p.add(cx, clonedObjects, selfHostedObject, clone)) return nullptr; if (!CloneProperties(cx, selfHostedObject, clone, clonedObjects)) { clonedObjects.remove(selfHostedObject); return nullptr; } return clone; }
static char * FormatFrame(JSContext *cx, const ScriptFrameIter &iter, char *buf, int num, JSBool showArgs, JSBool showLocals, JSBool showThisProps) { JSScript* script = iter.script(); jsbytecode* pc = iter.pc(); JSAutoCompartment ac(cx, iter.fp()->scopeChain()); const char *filename = script->filename; unsigned lineno = PCToLineNumber(script, pc); JSFunction *fun = iter.fp()->maybeFun(); JSString *funname = NULL; if (fun) funname = fun->atom(); JSObject *callObj = NULL; AutoPropertyDescArray callProps(cx); if (showArgs || showLocals) { callObj = JS_GetFrameCallObject(cx, Jsvalify(iter.fp())); if (callObj) callProps.fetch(callObj); } Value thisVal = UndefinedValue(); AutoPropertyDescArray thisProps(cx); if (ComputeThis(cx, iter.fp())) { thisVal = iter.fp()->thisValue(); if (showThisProps && !thisVal.isPrimitive()) thisProps.fetch(&thisVal.toObject()); } // print the frame number and function name if (funname) { JSAutoByteString funbytes; buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encode(cx, funname)); } else if (fun) { buf = JS_sprintf_append(buf, "%d anonymous(", num); } else { buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); } if (!buf) return buf; // print the function arguments if (showArgs && callObj) { uint32_t namedArgCount = 0; for (uint32_t i = 0; i < callProps->length; i++) { JSPropertyDesc* desc = &callProps->array[i]; JSAutoByteString nameBytes; const char *name = NULL; if (JSVAL_IS_STRING(desc->id)) name = FormatValue(cx, desc->id, nameBytes); JSAutoByteString valueBytes; const char *value = FormatValue(cx, desc->value, valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", namedArgCount ? ", " : "", name ? name :"", name ? " = " : "", desc->value.isString() ? "\"" : "", value ? value : "?unknown?", desc->value.isString() ? "\"" : ""); if (!buf) return buf; namedArgCount++; } // print any unnamed trailing args (found in 'arguments' object) Value val; if (JS_GetProperty(cx, callObj, "arguments", &val) && val.isObject()) { uint32_t argCount; JSObject* argsObj = &val.toObject(); if (JS_GetProperty(cx, argsObj, "length", &val) && ToUint32(cx, val, &argCount) && argCount > namedArgCount) { for (uint32_t k = namedArgCount; k < argCount; k++) { char number[8]; JS_snprintf(number, 8, "%d", (int) k); if (JS_GetProperty(cx, argsObj, number, &val)) { JSAutoByteString valueBytes; const char *value = FormatValue(cx, val, valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s", k ? ", " : "", val.isString() ? "\"" : "", value ? value : "?unknown?", val.isString() ? "\"" : ""); if (!buf) return buf; } } } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if (!buf) return buf; // print local variables if (showLocals && callProps->array) { for (uint32_t i = 0; i < callProps->length; i++) { JSPropertyDesc* desc = &callProps->array[i]; JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " %s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } } } // print the value of 'this' if (showLocals) { if (!thisVal.isUndefined()) { JSAutoByteString thisValBytes; if (JSString* thisValStr = ToString(cx, thisVal)) { if (const char *str = thisValBytes.encode(cx, thisValStr)) { buf = JS_sprintf_append(buf, " this = %s\n", str); if (!buf) return buf; } } } else { buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n"); } } // print the properties of 'this', if it is an object if (showThisProps && thisProps->array) { for (uint32_t i = 0; i < thisProps->length; i++) { JSPropertyDesc* desc = &thisProps->array[i]; if (desc->flags & JSPD_ENUMERATE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } } } } return buf; }