зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1083359
- Part 1 - Add the asyncCause and asyncParent properties to the native SavedFrame object. r=jimb
This commit is contained in:
Родитель
77e821b69f
Коммит
14addff448
|
@ -59,10 +59,27 @@ as the content compartment sees it, waive the xray wrapper with
|
|||
: Either SpiderMonkey's inferred name for this stack frame's function, or
|
||||
`null`.
|
||||
|
||||
`parent`
|
||||
: Either this stack frame's parent stack frame (the next older frame), or
|
||||
`null` if this is the oldest frame in the captured stack.
|
||||
`asyncCause`
|
||||
: If this stack frame is the `asyncParent` of other stack frames, then this is
|
||||
a string representing the type of asynchronous call by which this frame
|
||||
invoked its children. For example, if this frame's children are calls to
|
||||
handlers for a promise this frame created, this frame's `asyncCause` would
|
||||
be `"Promise"`. If the asynchronous call was started in a descendant frame
|
||||
to which the requester of the property does not have access, this will be
|
||||
the generic string `"Async"`. If this is not an asynchronous call point,
|
||||
this will be `null`.
|
||||
|
||||
`asyncParent`
|
||||
: If this stack frame was called as a result of an asynchronous operation, for
|
||||
example if the function referenced by this frame is a promise handler, this
|
||||
property points to the stack frame responsible for the asynchronous call,
|
||||
for example where the promise was created. If the frame responsible for the
|
||||
call is not accessible to the caller, this points to the youngest accessible
|
||||
ancestor of the real frame, if any. In all other cases, this is `null`.
|
||||
|
||||
`parent`
|
||||
: This stack frame's caller, or `null` if this is the oldest frame on the
|
||||
stack. In this case, there might be an `asyncParent` instead.
|
||||
|
||||
## Function Properties of the `SavedFrame.prototype` Object
|
||||
|
||||
|
|
|
@ -5105,6 +5105,20 @@ GetSavedFrameColumn(JSContext *cx, HandleObject savedFrame, uint32_t *columnp);
|
|||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, MutableHandleString namep);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncCause(JSContext *cx, HandleObject savedFrame, MutableHandleString asyncCausep);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
|
||||
* if there is no asyncParent. The `asyncParentp` out parameter is _NOT_
|
||||
* guaranteed to be in the cx's compartment. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject asyncParentp);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
|
||||
* it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
|
||||
macro(ArrayType, ArrayType, "ArrayType") \
|
||||
macro(ArrayValues, ArrayValues, "ArrayValues") \
|
||||
macro(Async, Async, "Async") \
|
||||
macro(buffer, buffer, "buffer") \
|
||||
macro(builder, builder, "builder") \
|
||||
macro(byteLength, byteLength, "byteLength") \
|
||||
|
|
|
@ -39,12 +39,14 @@ using mozilla::Maybe;
|
|||
namespace js {
|
||||
|
||||
struct SavedFrame::Lookup {
|
||||
Lookup(JSAtom *source, uint32_t line, uint32_t column, JSAtom *functionDisplayName,
|
||||
SavedFrame *parent, JSPrincipals *principals)
|
||||
Lookup(JSAtom *source, uint32_t line, uint32_t column,
|
||||
JSAtom *functionDisplayName, JSAtom *asyncCause, SavedFrame *parent,
|
||||
JSPrincipals *principals)
|
||||
: source(source),
|
||||
line(line),
|
||||
column(column),
|
||||
functionDisplayName(functionDisplayName),
|
||||
asyncCause(asyncCause),
|
||||
parent(parent),
|
||||
principals(principals)
|
||||
{
|
||||
|
@ -55,6 +57,7 @@ struct SavedFrame::Lookup {
|
|||
uint32_t line;
|
||||
uint32_t column;
|
||||
JSAtom *functionDisplayName;
|
||||
JSAtom *asyncCause;
|
||||
SavedFrame *parent;
|
||||
JSPrincipals *principals;
|
||||
|
||||
|
@ -64,6 +67,10 @@ struct SavedFrame::Lookup {
|
|||
gc::MarkStringUnbarriered(trc, &functionDisplayName,
|
||||
"SavedFrame::Lookup::functionDisplayName");
|
||||
}
|
||||
if (asyncCause) {
|
||||
gc::MarkStringUnbarriered(trc, &asyncCause,
|
||||
"SavedFrame::Lookup::asyncCause");
|
||||
}
|
||||
if (parent) {
|
||||
gc::MarkObjectUnbarriered(trc, &parent,
|
||||
"SavedFrame::Lookup::parent");
|
||||
|
@ -102,6 +109,7 @@ SavedFrame::HashPolicy::hash(const Lookup &lookup)
|
|||
lookup.column,
|
||||
lookup.source,
|
||||
lookup.functionDisplayName,
|
||||
lookup.asyncCause,
|
||||
SavedFramePtrHasher::hash(lookup.parent),
|
||||
JSPrincipalsPtrHasher::hash(lookup.principals));
|
||||
}
|
||||
|
@ -129,6 +137,10 @@ SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup)
|
|||
if (functionDisplayName != lookup.functionDisplayName)
|
||||
return false;
|
||||
|
||||
JSAtom *asyncCause = existing->getAsyncCause();
|
||||
if (asyncCause != lookup.asyncCause)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -197,6 +209,8 @@ SavedFrame::protoAccessors[] = {
|
|||
JS_PSG("line", SavedFrame::lineProperty, 0),
|
||||
JS_PSG("column", SavedFrame::columnProperty, 0),
|
||||
JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
|
||||
JS_PSG("asyncCause", SavedFrame::asyncCauseProperty, 0),
|
||||
JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
|
||||
JS_PSG("parent", SavedFrame::parentProperty, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
@ -243,6 +257,16 @@ SavedFrame::getFunctionDisplayName()
|
|||
return &s->asAtom();
|
||||
}
|
||||
|
||||
JSAtom *
|
||||
SavedFrame::getAsyncCause()
|
||||
{
|
||||
const Value &v = getReservedSlot(JSSLOT_ASYNCCAUSE);
|
||||
if (v.isNull())
|
||||
return nullptr;
|
||||
JSString *s = v.toString();
|
||||
return &s->asAtom();
|
||||
}
|
||||
|
||||
SavedFrame *
|
||||
SavedFrame::getParent()
|
||||
{
|
||||
|
@ -272,6 +296,10 @@ SavedFrame::initFromLookup(SavedFrame::HandleLookup lookup)
|
|||
lookup->functionDisplayName
|
||||
? StringValue(lookup->functionDisplayName)
|
||||
: NullValue());
|
||||
setReservedSlot(JSSLOT_ASYNCCAUSE,
|
||||
lookup->asyncCause
|
||||
? StringValue(lookup->asyncCause)
|
||||
: NullValue());
|
||||
setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup->parent));
|
||||
setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(lookup->parent));
|
||||
|
||||
|
@ -312,10 +340,14 @@ SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
// Return the first SavedFrame in the chain that starts with |frame| whose
|
||||
// principals are subsumed by |principals|, according to |subsumes|. If there is
|
||||
// no such frame, return nullptr.
|
||||
// no such frame, return nullptr. |skippedAsync| is set to true if any of the
|
||||
// skipped frames had the |asyncCause| property set, otherwise it is explicitly
|
||||
// set to false.
|
||||
static SavedFrame *
|
||||
GetFirstSubsumedFrame(JSContext *cx, HandleSavedFrame frame)
|
||||
GetFirstSubsumedFrame(JSContext *cx, HandleSavedFrame frame, bool &skippedAsync)
|
||||
{
|
||||
skippedAsync = false;
|
||||
|
||||
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
if (!subsumes)
|
||||
return frame;
|
||||
|
@ -323,8 +355,11 @@ GetFirstSubsumedFrame(JSContext *cx, HandleSavedFrame frame)
|
|||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
|
||||
RootedSavedFrame rootedFrame(cx, frame);
|
||||
while (rootedFrame && !subsumes(principals, rootedFrame->getPrincipals()))
|
||||
while (rootedFrame && !subsumes(principals, rootedFrame->getPrincipals())) {
|
||||
if (rootedFrame->getAsyncCause())
|
||||
skippedAsync = true;
|
||||
rootedFrame = rootedFrame->getParent();
|
||||
}
|
||||
|
||||
return rootedFrame;
|
||||
}
|
||||
|
@ -334,8 +369,9 @@ GetFirstSubsumedSavedFrame(JSContext *cx, HandleObject savedFrame)
|
|||
{
|
||||
if (!savedFrame)
|
||||
return nullptr;
|
||||
bool skippedAsync;
|
||||
RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
|
||||
return GetFirstSubsumedFrame(cx, frame);
|
||||
return GetFirstSubsumedFrame(cx, frame, skippedAsync);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
@ -437,7 +473,7 @@ public:
|
|||
} // anonymous namespace
|
||||
|
||||
static inline js::SavedFrame *
|
||||
UnwrapSavedFrame(JSContext *cx, HandleObject obj)
|
||||
UnwrapSavedFrame(JSContext *cx, HandleObject obj, bool &skippedAsync)
|
||||
{
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
@ -445,14 +481,15 @@ UnwrapSavedFrame(JSContext *cx, HandleObject obj)
|
|||
MOZ_ASSERT(savedFrameObj);
|
||||
MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
|
||||
js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
|
||||
return GetFirstSubsumedFrame(cx, frame);
|
||||
return GetFirstSubsumedFrame(cx, frame, skippedAsync);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameSource(JSContext *cx, HandleObject savedFrame, MutableHandleString sourcep)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
sourcep.set(cx->runtime()->emptyString);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -466,7 +503,8 @@ GetSavedFrameLine(JSContext *cx, HandleObject savedFrame, uint32_t *linep)
|
|||
{
|
||||
MOZ_ASSERT(linep);
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
*linep = 0;
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -480,7 +518,8 @@ GetSavedFrameColumn(JSContext *cx, HandleObject savedFrame, uint32_t *columnp)
|
|||
{
|
||||
MOZ_ASSERT(columnp);
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
*columnp = 0;
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -493,7 +532,8 @@ JS_PUBLIC_API(SavedFrameResult)
|
|||
GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, MutableHandleString namep)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
namep.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
|
@ -502,17 +542,73 @@ GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, Mutable
|
|||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncCause(JSContext *cx, HandleObject savedFrame, MutableHandleString asyncCausep)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
asyncCausep.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
asyncCausep.set(frame->getAsyncCause());
|
||||
if (!asyncCausep && skippedAsync)
|
||||
asyncCausep.set(cx->names().Async);
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameAsyncParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject asyncParentp)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
asyncParentp.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
js::RootedSavedFrame parent(cx, frame->getParent());
|
||||
|
||||
// The current value of |skippedAsync| is not interesting, because we are
|
||||
// interested in whether we would cross any async parents to get from here
|
||||
// to the first subsumed parent frame instead.
|
||||
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
|
||||
|
||||
// Even if |parent| is not subsumed, we still want to return a pointer to it
|
||||
// rather than |subsumedParent| so it can pick up any |asyncCause| from the
|
||||
// inaccessible part of the chain.
|
||||
if (subsumedParent && (subsumedParent->getAsyncCause() || skippedAsync))
|
||||
asyncParentp.set(parent);
|
||||
else
|
||||
asyncParentp.set(nullptr);
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject parentp)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
|
||||
if (!frame) {
|
||||
parentp.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
js::RootedSavedFrame parent(cx, frame->getParent());
|
||||
parentp.set(js::GetFirstSubsumedFrame(cx, parent));
|
||||
|
||||
// The current value of |skippedAsync| is not interesting, because we are
|
||||
// interested in whether we would cross any async parents to get from here
|
||||
// to the first subsumed parent frame instead.
|
||||
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
|
||||
|
||||
// Even if |parent| is not subsumed, we still want to return a pointer to it
|
||||
// rather than |subsumedParent| so it can pick up any |asyncCause| from the
|
||||
// inaccessible part of the chain.
|
||||
if (subsumedParent && !(subsumedParent->getAsyncCause() || skippedAsync))
|
||||
parentp.set(parent);
|
||||
else
|
||||
parentp.set(nullptr);
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
|
@ -520,7 +616,8 @@ JS_PUBLIC_API(bool)
|
|||
StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString stringp)
|
||||
{
|
||||
AutoMaybeEnterFrameCompartment ac(cx, stack);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack));
|
||||
bool skippedAsync;
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, skippedAsync));
|
||||
if (!frame) {
|
||||
stringp.set(cx->runtime()->emptyString);
|
||||
return true;
|
||||
|
@ -535,8 +632,13 @@ StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString
|
|||
MOZ_ASSERT_IF(subsumes, (*subsumes)(principals, frame->getPrincipals()));
|
||||
|
||||
if (!frame->isSelfHosted()) {
|
||||
RootedString asyncCause(cx, frame->getAsyncCause());
|
||||
if (!asyncCause && skippedAsync) {
|
||||
asyncCause.set(cx->names().Async);
|
||||
}
|
||||
js::RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if ((name && !sb.append(name))
|
||||
if ((asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
|
||||
|| (name && !sb.append(name))
|
||||
|| !sb.append('@')
|
||||
|| !sb.append(frame->getSource())
|
||||
|| !sb.append(':')
|
||||
|
@ -550,7 +652,7 @@ StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString
|
|||
}
|
||||
|
||||
parent = frame->getParent();
|
||||
frame = js::GetFirstSubsumedFrame(cx, parent);
|
||||
frame = js::GetFirstSubsumedFrame(cx, parent, skippedAsync);
|
||||
} while (frame);
|
||||
|
||||
JSString *str = sb.finishString();
|
||||
|
@ -613,6 +715,29 @@ SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::asyncCauseProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get asyncCause)", args, frame);
|
||||
RootedString asyncCause(cx);
|
||||
JS::SavedFrameResult result = JS::GetSavedFrameAsyncCause(cx, frame, &asyncCause);
|
||||
if (result == JS::SavedFrameResult::Ok && asyncCause)
|
||||
args.rval().setString(asyncCause);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::asyncParentProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get asyncParent)", args, frame);
|
||||
RootedObject asyncParent(cx);
|
||||
(void) JS::GetSavedFrameAsyncParent(cx, frame, &asyncParent);
|
||||
args.rval().setObjectOrNull(asyncParent);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -676,6 +801,7 @@ SavedStacks::sweep(JSRuntime *rt)
|
|||
frame->getLine(),
|
||||
frame->getColumn(),
|
||||
frame->getFunctionDisplayName(),
|
||||
frame->getAsyncCause(),
|
||||
frame->getParent(),
|
||||
frame->getPrincipals()),
|
||||
ReadBarriered<SavedFrame *>(frame));
|
||||
|
@ -759,6 +885,7 @@ SavedStacks::insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFram
|
|||
location->column,
|
||||
iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
iter.compartment()->principals
|
||||
);
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ class SavedFrame : public NativeObject {
|
|||
static bool lineProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool columnProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool asyncCauseProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool asyncParentProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool parentProperty(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool toStringMethod(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
|
@ -43,6 +45,7 @@ class SavedFrame : public NativeObject {
|
|||
uint32_t getLine();
|
||||
uint32_t getColumn();
|
||||
JSAtom *getFunctionDisplayName();
|
||||
JSAtom *getAsyncCause();
|
||||
SavedFrame *getParent();
|
||||
JSPrincipals *getPrincipals();
|
||||
|
||||
|
@ -86,6 +89,7 @@ class SavedFrame : public NativeObject {
|
|||
JSSLOT_LINE,
|
||||
JSSLOT_COLUMN,
|
||||
JSSLOT_FUNCTIONDISPLAYNAME,
|
||||
JSSLOT_ASYNCCAUSE,
|
||||
JSSLOT_PARENT,
|
||||
JSSLOT_PRINCIPALS,
|
||||
JSSLOT_PRIVATE_PARENT,
|
||||
|
|
Загрузка…
Ссылка в новой задаче