зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1689413 part 6 - Move Realm* from ObjectGroup to BaseShape. r=jonco
Differential Revision: https://phabricator.services.mozilla.com/D106976
This commit is contained in:
Родитель
dbd6f24a18
Коммит
2204818bf0
|
@ -56,7 +56,7 @@ inline const JSClass* GetClass(const JSObject* obj) {
|
|||
* compartment of this realm.
|
||||
*/
|
||||
static MOZ_ALWAYS_INLINE Compartment* GetCompartment(JSObject* obj) {
|
||||
Realm* realm = reinterpret_cast<shadow::Object*>(obj)->group->realm;
|
||||
Realm* realm = reinterpret_cast<shadow::Object*>(obj)->shape->base->realm;
|
||||
return GetCompartmentForRealm(realm);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace shadow {
|
|||
struct ObjectGroup {
|
||||
int* unused;
|
||||
JSObject* proto;
|
||||
JS::Realm* realm;
|
||||
};
|
||||
|
||||
} // namespace shadow
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace shadow {
|
|||
|
||||
struct BaseShape {
|
||||
const JSClass* clasp;
|
||||
JS::Realm* realm;
|
||||
};
|
||||
|
||||
class Shape {
|
||||
|
|
|
@ -3817,9 +3817,9 @@ static JSObject* CreateArrayPrototype(JSContext* cx, JSProtoKey key) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_,
|
||||
TaggedProto(proto),
|
||||
gc::AllocKind::OBJECT0));
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(
|
||||
cx, &ArrayObject::class_, cx->realm(),
|
||||
TaggedProto(proto), gc::AllocKind::OBJECT0));
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -3972,9 +3972,9 @@ static MOZ_ALWAYS_INLINE ArrayObject* NewArray(JSContext* cx, uint32_t length,
|
|||
* Get a shape with zero fixed slots, regardless of the size class.
|
||||
* See JSObject::createArray.
|
||||
*/
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &ArrayObject::class_,
|
||||
TaggedProto(proto),
|
||||
gc::AllocKind::OBJECT0));
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(
|
||||
cx, &ArrayObject::class_, cx->realm(),
|
||||
TaggedProto(proto), gc::AllocKind::OBJECT0));
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -4089,14 +4089,14 @@ ArrayObject* js::NewDenseFullyAllocatedArrayWithTemplate(
|
|||
}
|
||||
|
||||
// TODO(no-TI): clean up.
|
||||
ArrayObject* js::NewArrayWithGroup(JSContext* cx, uint32_t length,
|
||||
HandleObjectGroup group) {
|
||||
// Ion can call this with a group from a different realm when calling
|
||||
ArrayObject* js::NewArrayWithShape(JSContext* cx, uint32_t length,
|
||||
HandleShape shape) {
|
||||
// Ion can call this with a shape from a different realm when calling
|
||||
// another realm's Array constructor.
|
||||
Maybe<AutoRealm> ar;
|
||||
if (cx->realm() != group->realm()) {
|
||||
MOZ_ASSERT(cx->compartment() == group->compartment());
|
||||
ar.emplace(cx, group);
|
||||
if (cx->realm() != shape->realm()) {
|
||||
MOZ_ASSERT(cx->compartment() == shape->compartment());
|
||||
ar.emplace(cx, shape);
|
||||
}
|
||||
|
||||
return NewDenseFullyAllocatedArray(cx, length);
|
||||
|
|
|
@ -85,8 +85,8 @@ extern ArrayObject* NewDenseCopiedArray(JSContext* cx, uint32_t length,
|
|||
extern ArrayObject* NewDenseFullyAllocatedArrayWithTemplate(
|
||||
JSContext* cx, uint32_t length, ArrayObject* templateObject);
|
||||
|
||||
extern ArrayObject* NewArrayWithGroup(JSContext* cx, uint32_t length,
|
||||
HandleObjectGroup group);
|
||||
extern ArrayObject* NewArrayWithShape(JSContext* cx, uint32_t length,
|
||||
HandleShape shape);
|
||||
|
||||
extern bool ToLength(JSContext* cx, HandleValue v, uint64_t* out);
|
||||
|
||||
|
|
|
@ -927,9 +927,10 @@ static bool InstantiateFunctions(JSContext* cx, CompilationInput& input,
|
|||
if (!group) {
|
||||
return false;
|
||||
}
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(
|
||||
cx, &JSFunction::class_, TaggedProto(proto),
|
||||
/* nfixed = */ 0, ObjectFlags()));
|
||||
RootedShape shape(
|
||||
cx, EmptyShape::getInitialShape(cx, &JSFunction::class_, cx->realm(),
|
||||
TaggedProto(proto),
|
||||
/* nfixed = */ 0, ObjectFlags()));
|
||||
if (!shape) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8054,8 +8054,11 @@ void GCRuntime::mergeRealms(Realm* source, Realm* target) {
|
|||
group->setProtoUnchecked(TaggedProto(targetProto));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group->realm_ = target;
|
||||
for (auto baseShape = source->zone()->cellIterUnsafe<BaseShape>();
|
||||
!baseShape.done(); baseShape.next()) {
|
||||
baseShape->setRealmForMergeRealms(target);
|
||||
}
|
||||
|
||||
// Fixup zone pointers in source's zone to refer to target's zone.
|
||||
|
|
|
@ -1541,21 +1541,18 @@ void js::ObjectGroup::traceChildren(JSTracer* trc) {
|
|||
if (proto().isObject()) {
|
||||
TraceEdge(trc, &proto(), "group_proto");
|
||||
}
|
||||
|
||||
// Note: the realm's global can be nullptr if we GC while creating the global.
|
||||
if (JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal()) {
|
||||
TraceManuallyBarrieredEdge(trc, &global, "group_global");
|
||||
}
|
||||
}
|
||||
|
||||
void js::GCMarker::lazilyMarkChildren(ObjectGroup* group) {
|
||||
if (group->proto().isObject()) {
|
||||
markAndTraverseEdge(group, group->proto().toObject());
|
||||
}
|
||||
}
|
||||
|
||||
void BaseShape::traceChildren(JSTracer* trc) {
|
||||
// Note: the realm's global can be nullptr if we GC while creating the global.
|
||||
if (GlobalObject* global = group->realm()->unsafeUnbarrieredMaybeGlobal()) {
|
||||
markAndTraverseEdge(group, static_cast<JSObject*>(global));
|
||||
if (JSObject* global = realm()->unsafeUnbarrieredMaybeGlobal()) {
|
||||
TraceManuallyBarrieredEdge(trc, &global, "baseshape_global");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
// Test that objects in different compartments can have the same shape if they
|
||||
// have a null proto.
|
||||
|
||||
var g1 = newGlobal();
|
||||
var g2 = newGlobal({sameZoneAs: g1});
|
||||
|
||||
g1.eval("x1 = {foo: 1}");
|
||||
g2.eval("x2 = {foo: 2}");
|
||||
assertEq(unwrappedObjectsHaveSameShape(g1.x1, g2.x2), false);
|
||||
|
||||
g1.eval("x1 = Object.create(null); x1.foo = 1;");
|
||||
g2.eval("x2 = Object.create(null); x2.foo = 2;");
|
||||
assertEq(unwrappedObjectsHaveSameShape(g1.x1, g2.x2), true);
|
|
@ -35,8 +35,9 @@ Match.Pattern([{node: gc, edge: "fun_environment"},
|
|||
.assert(findPath(gc, o));
|
||||
print(JSON.stringify(findPath(gc, o)));
|
||||
|
||||
Match.Pattern([{node: {}, edge: "group"},
|
||||
{node: Match.Pattern.ANY, edge: "group_global"},
|
||||
Match.Pattern([{node: {}, edge: "shape"},
|
||||
{node: Match.Pattern.ANY, edge: "base"},
|
||||
{node: Match.Pattern.ANY, edge: "baseshape_global"},
|
||||
{node: {}, edge: "o"}])
|
||||
.assert(findPath(o, o));
|
||||
print(findPath(o, o).map((e) => e.edge).toString());
|
||||
|
|
|
@ -6623,11 +6623,11 @@ void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) {
|
|||
JSObject* templateObject = lir->mir()->templateObject();
|
||||
|
||||
if (templateObject) {
|
||||
pushArg(ImmGCPtr(templateObject->group()));
|
||||
pushArg(ImmGCPtr(templateObject->shape()));
|
||||
pushArg(Imm32(lir->mir()->length()));
|
||||
|
||||
using Fn = ArrayObject* (*)(JSContext*, uint32_t, HandleObjectGroup);
|
||||
callVM<Fn, NewArrayWithGroup>(lir);
|
||||
using Fn = ArrayObject* (*)(JSContext*, uint32_t, HandleShape);
|
||||
callVM<Fn, NewArrayWithShape>(lir);
|
||||
} else {
|
||||
pushArg(Imm32(GenericObject));
|
||||
pushArg(Imm32(lir->mir()->length()));
|
||||
|
|
|
@ -1644,8 +1644,9 @@ void MacroAssembler::switchToRealm(const void* realm, Register scratch) {
|
|||
}
|
||||
|
||||
void MacroAssembler::switchToObjectRealm(Register obj, Register scratch) {
|
||||
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
|
||||
loadPtr(Address(scratch, ObjectGroup::offsetOfRealm()), scratch);
|
||||
loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
|
||||
loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
|
||||
loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
|
||||
switchToRealm(scratch);
|
||||
}
|
||||
|
||||
|
@ -1685,8 +1686,9 @@ void MacroAssembler::setIsCrossRealmArrayConstructor(Register obj,
|
|||
|
||||
// The object's realm must not be cx->realm.
|
||||
Label isFalse, done;
|
||||
loadPtr(Address(obj, JSObject::offsetOfGroup()), output);
|
||||
loadPtr(Address(output, ObjectGroup::offsetOfRealm()), output);
|
||||
loadPtr(Address(obj, JSObject::offsetOfShape()), output);
|
||||
loadPtr(Address(output, Shape::offsetOfBaseShape()), output);
|
||||
loadPtr(Address(output, BaseShape::offsetOfRealm()), output);
|
||||
branchPtr(Assembler::Equal, AbsoluteAddress(ContextRealmPtr()), output,
|
||||
&isFalse);
|
||||
|
||||
|
@ -3595,8 +3597,9 @@ void MacroAssembler::branchTestObjCompartment(Condition cond, Register obj,
|
|||
const Address& compartment,
|
||||
Register scratch, Label* label) {
|
||||
MOZ_ASSERT(obj != scratch);
|
||||
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
|
||||
loadPtr(Address(scratch, ObjectGroup::offsetOfRealm()), scratch);
|
||||
loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
|
||||
loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
|
||||
loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
|
||||
loadPtr(Address(scratch, Realm::offsetOfCompartment()), scratch);
|
||||
branchPtr(cond, compartment, scratch, label);
|
||||
}
|
||||
|
@ -3605,8 +3608,9 @@ void MacroAssembler::branchTestObjCompartment(
|
|||
Condition cond, Register obj, const JS::Compartment* compartment,
|
||||
Register scratch, Label* label) {
|
||||
MOZ_ASSERT(obj != scratch);
|
||||
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
|
||||
loadPtr(Address(scratch, ObjectGroup::offsetOfRealm()), scratch);
|
||||
loadPtr(Address(obj, JSObject::offsetOfShape()), scratch);
|
||||
loadPtr(Address(scratch, Shape::offsetOfBaseShape()), scratch);
|
||||
loadPtr(Address(scratch, BaseShape::offsetOfRealm()), scratch);
|
||||
loadPtr(Address(scratch, Realm::offsetOfCompartment()), scratch);
|
||||
branchPtr(cond, scratch, ImmPtr(compartment), label);
|
||||
}
|
||||
|
|
|
@ -1571,9 +1571,9 @@ RNewArray::RNewArray(CompactBufferReader& reader) {
|
|||
bool RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const {
|
||||
RootedObject templateObject(cx, &iter.read().toObject());
|
||||
RootedValue result(cx);
|
||||
RootedObjectGroup group(cx, templateObject->group());
|
||||
RootedShape shape(cx, templateObject->shape());
|
||||
|
||||
ArrayObject* resultObject = NewArrayWithGroup(cx, count_, group);
|
||||
ArrayObject* resultObject = NewArrayWithShape(cx, count_, shape);
|
||||
if (!resultObject) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ namespace jit {
|
|||
_(NewArgumentsObject, js::jit::NewArgumentsObject) \
|
||||
_(NewArrayIterator, js::NewArrayIterator) \
|
||||
_(NewArrayOperation, js::NewArrayOperation) \
|
||||
_(NewArrayWithGroup, js::NewArrayWithGroup) \
|
||||
_(NewArrayWithShape, js::NewArrayWithShape) \
|
||||
_(NewCallObject, js::jit::NewCallObject) \
|
||||
_(NewObjectOperation, js::NewObjectOperation) \
|
||||
_(NewObjectOperationWithTemplate, js::NewObjectOperationWithTemplate) \
|
||||
|
|
|
@ -364,7 +364,7 @@ JS_FRIEND_API bool UninlinedIsCrossCompartmentWrapper(const JSObject* obj);
|
|||
// getting a wrapper's realm usually doesn't make sense.
|
||||
static MOZ_ALWAYS_INLINE JS::Realm* GetNonCCWObjectRealm(JSObject* obj) {
|
||||
MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(obj));
|
||||
return reinterpret_cast<JS::shadow::Object*>(obj)->group->realm;
|
||||
return reinterpret_cast<JS::shadow::Object*>(obj)->shape->base->realm;
|
||||
}
|
||||
|
||||
JS_FRIEND_API void AssertSameCompartment(JSContext* cx, JSObject* obj);
|
||||
|
|
|
@ -4653,21 +4653,6 @@ static bool GroupOf(JSContext* cx, unsigned argc, JS::Value* vp) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool UnwrappedObjectsHaveSameShape(JSContext* cx, unsigned argc,
|
||||
JS::Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (!args.get(0).isObject() || !args.get(1).isObject()) {
|
||||
JS_ReportErrorASCII(cx, "2 objects expected");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
|
||||
RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
|
||||
|
||||
args.rval().setBoolean(obj1->shape() == obj2->shape());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Sleep_fn(JSContext* cx, unsigned argc, Value* vp) {
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
@ -9045,12 +9030,6 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
|||
"groupOf(obj)",
|
||||
" Get the group of obj (an implementation detail)."),
|
||||
|
||||
JS_FN_HELP("unwrappedObjectsHaveSameShape", UnwrappedObjectsHaveSameShape, 2, 0,
|
||||
"unwrappedObjectsHaveSameShape(obj1, obj2)",
|
||||
" Returns true iff obj1 and obj2 have the same shape, false otherwise. Both\n"
|
||||
" objects are unwrapped first, so this can be used on objects from different\n"
|
||||
" globals."),
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_FN_HELP("arrayInfo", ArrayInfo, 1, 0,
|
||||
"arrayInfo(a1, a2, ...)",
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
// |reftest| skip-if(!xulRuntime.shell) -- requires evalcx
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 466905;
|
||||
var summary = 'Sandbox shapes';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test()
|
||||
{
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
if (typeof evalcx != 'function')
|
||||
{
|
||||
expect = actual = 'Test skipped: requires evalcx support';
|
||||
}
|
||||
else if (typeof shapeOf != 'function')
|
||||
{
|
||||
expect = actual = 'Test skipped: requires shapeOf support';
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var s1 = evalcx('lazy');
|
||||
var s2 = evalcx('lazy');
|
||||
|
||||
expect = shapeOf(s1);
|
||||
actual = shapeOf(s2);
|
||||
}
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
}
|
|
@ -278,9 +278,9 @@ ArgumentsObject* ArgumentsObject::createTemplateObject(JSContext* cx,
|
|||
}
|
||||
|
||||
constexpr ObjectFlags objectFlags = {ObjectFlag::Indexed};
|
||||
RootedShape shape(cx,
|
||||
EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
|
||||
FINALIZE_KIND, objectFlags));
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(
|
||||
cx, clasp, cx->realm(), TaggedProto(proto),
|
||||
FINALIZE_KIND, objectFlags));
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -50,11 +50,9 @@ inline NativeObject* NewObjectCache::newObjectFromHit(JSContext* cx,
|
|||
NativeObject* templateObj =
|
||||
reinterpret_cast<NativeObject*>(&entry->templateObject);
|
||||
|
||||
ObjectGroup* group = templateObj->group();
|
||||
|
||||
// If we did the lookup based on the proto we might have a group/object from a
|
||||
// If we did the lookup based on the proto we might have a shape/object from a
|
||||
// different (same-compartment) realm, so we have to do a realm check.
|
||||
if (group->realm() != cx->realm()) {
|
||||
if (templateObj->shape()->realm() != cx->realm()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -591,9 +591,9 @@ static PropertyIteratorObject* NewPropertyIteratorObject(JSContext* cx) {
|
|||
}
|
||||
|
||||
const JSClass* clasp = &PropertyIteratorObject::class_;
|
||||
RootedShape shape(cx,
|
||||
EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr),
|
||||
ITERATOR_FINALIZE_KIND));
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, cx->realm(),
|
||||
TaggedProto(nullptr),
|
||||
ITERATOR_FINALIZE_KIND));
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -374,7 +374,7 @@ inline void JSContext::enterRealmOf(JSScript* target) {
|
|||
enterRealm(target->realm());
|
||||
}
|
||||
|
||||
inline void JSContext::enterRealmOf(js::ObjectGroup* target) {
|
||||
inline void JSContext::enterRealmOf(js::Shape* target) {
|
||||
JS::AssertCellIsNotGray(target);
|
||||
enterRealm(target->realm());
|
||||
}
|
||||
|
|
|
@ -329,7 +329,7 @@ struct JS_PUBLIC_API JSContext : public JS::RootingContext,
|
|||
public:
|
||||
inline void enterRealmOf(JSObject* target);
|
||||
inline void enterRealmOf(JSScript* target);
|
||||
inline void enterRealmOf(js::ObjectGroup* target);
|
||||
inline void enterRealmOf(js::Shape* target);
|
||||
inline void enterNullRealm();
|
||||
|
||||
inline void setRealmForJitExceptionHandler(JS::Realm* realm);
|
||||
|
|
|
@ -132,7 +132,6 @@ js::NativeObject::updateDictionaryListPointerAfterMinorGC(NativeObject* old) {
|
|||
|
||||
inline void JSObject::setGroup(js::ObjectGroup* group) {
|
||||
MOZ_RELEASE_ASSERT(group);
|
||||
MOZ_ASSERT(maybeCCWRealm() == group->realm());
|
||||
setGroupRaw(group);
|
||||
}
|
||||
|
||||
|
|
|
@ -524,7 +524,7 @@ bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj,
|
|||
// dictionary mode.
|
||||
RootedShape last(
|
||||
cx, EmptyShape::getInitialShape(
|
||||
cx, nobj->getClass(), nobj->taggedProto(),
|
||||
cx, nobj->getClass(), nobj->realm(), nobj->taggedProto(),
|
||||
nobj->numFixedSlots(), nobj->lastProperty()->objectFlags()));
|
||||
if (!last) {
|
||||
return false;
|
||||
|
@ -771,8 +771,9 @@ static inline JSObject* NewObject(JSContext* cx, HandleObjectGroup group,
|
|||
? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
|
||||
: GetGCKindSlots(kind, clasp);
|
||||
|
||||
RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(),
|
||||
nfixed, objectFlags));
|
||||
RootedShape shape(
|
||||
cx, EmptyShape::getInitialShape(cx, clasp, cx->realm(), group->proto(),
|
||||
nfixed, objectFlags));
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1194,7 +1195,8 @@ static bool InitializePropertiesFromCompatibleNativeObject(
|
|||
// We need to generate a new shape for dst that has dst's proto but all
|
||||
// the property information from src. Note that we asserted above that
|
||||
// dst's object flags are empty.
|
||||
shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->taggedProto(),
|
||||
shape = EmptyShape::getInitialShape(cx, dst->getClass(), dst->realm(),
|
||||
dst->taggedProto(),
|
||||
dst->numFixedSlots(), ObjectFlags());
|
||||
if (!shape) {
|
||||
return false;
|
||||
|
@ -1806,11 +1808,9 @@ static bool SetProto(JSContext* cx, HandleObject obj,
|
|||
}
|
||||
}
|
||||
|
||||
RootedObjectGroup oldGroup(cx, obj->group());
|
||||
|
||||
ObjectGroup* newGroup;
|
||||
{
|
||||
AutoRealm ar(cx, oldGroup);
|
||||
AutoRealm ar(cx, obj->shape());
|
||||
newGroup = ObjectGroup::defaultNewGroup(cx, obj->getClass(), proto);
|
||||
if (!newGroup) {
|
||||
return false;
|
||||
|
@ -3813,7 +3813,7 @@ void JSObject::debugCheckNewObject(ObjectGroup* group, Shape* shape,
|
|||
CanNurseryAllocateFinalizedClass(clasp) ||
|
||||
clasp->isProxyObject());
|
||||
|
||||
MOZ_ASSERT(!group->realm()->hasObjectPendingMetadata());
|
||||
MOZ_ASSERT(!shape->realm()->hasObjectPendingMetadata());
|
||||
|
||||
// Non-native classes manage their own data and slots, so numFixedSlots and
|
||||
// slotSpan are always 0. Note that proxy classes can have reserved slots
|
||||
|
|
|
@ -138,7 +138,7 @@ class JSObject
|
|||
|
||||
void initGroup(js::ObjectGroup* group) { initHeaderPtr(group); }
|
||||
|
||||
JS::Compartment* compartment() const { return group()->compartment(); }
|
||||
JS::Compartment* compartment() const { return shape()->compartment(); }
|
||||
JS::Compartment* maybeCompartment() const { return compartment(); }
|
||||
|
||||
void initShape(js::Shape* shape) {
|
||||
|
@ -148,7 +148,7 @@ class JSObject
|
|||
shape_.init(shape);
|
||||
}
|
||||
void setShape(js::Shape* shape) {
|
||||
MOZ_ASSERT(zone() == shape->zone());
|
||||
MOZ_ASSERT(maybeCCWRealm() == shape->realm());
|
||||
shape_ = shape;
|
||||
}
|
||||
js::Shape* shape() const { return shape_; }
|
||||
|
@ -363,14 +363,14 @@ class JSObject
|
|||
|
||||
JS::Realm* nonCCWRealm() const {
|
||||
MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this));
|
||||
return group()->realm();
|
||||
return shape()->realm();
|
||||
}
|
||||
bool hasSameRealmAs(JSContext* cx) const;
|
||||
|
||||
// Returns the object's realm even if the object is a CCW (be careful, in
|
||||
// this case the realm is not very meaningful because wrappers are shared by
|
||||
// all realms in the compartment).
|
||||
JS::Realm* maybeCCWRealm() const { return group()->realm(); }
|
||||
JS::Realm* maybeCCWRealm() const { return shape()->realm(); }
|
||||
|
||||
/*
|
||||
* ES5 meta-object properties and operations.
|
||||
|
|
|
@ -47,22 +47,15 @@ static ObjectGroup* MakeGroup(JSContext* cx, Handle<TaggedProto> proto) {
|
|||
if (!group) {
|
||||
return nullptr;
|
||||
}
|
||||
new (group) ObjectGroup(proto, cx->realm());
|
||||
new (group) ObjectGroup(proto);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
ObjectGroup::ObjectGroup(TaggedProto proto, JS::Realm* realm)
|
||||
: TenuredCellWithNonGCPointer(nullptr), proto_(proto), realm_(realm) {
|
||||
ObjectGroup::ObjectGroup(TaggedProto proto)
|
||||
: TenuredCellWithNonGCPointer(nullptr), proto_(proto) {
|
||||
/* Windows may not appear on prototype chains. */
|
||||
MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject()));
|
||||
|
||||
#ifdef DEBUG
|
||||
GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal();
|
||||
if (global) {
|
||||
AssertTargetIsNotGray(global);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ObjectGroup::setProtoUnchecked(TaggedProto proto) {
|
||||
|
|
|
@ -49,8 +49,10 @@ class ObjectGroup : public gc::TenuredCellWithNonGCPointer<int> {
|
|||
/* Prototype shared by objects in this group. */
|
||||
GCPtr<TaggedProto> proto_; // set by constructor
|
||||
|
||||
/* Realm shared by objects in this group. */
|
||||
JS::Realm* realm_; // set by constructor
|
||||
#ifndef JS_64BIT
|
||||
// Temporary padding to respect MinCellSize.
|
||||
uint64_t padding_ = 0;
|
||||
#endif
|
||||
|
||||
// END OF PROPERTIES
|
||||
|
||||
|
@ -59,17 +61,13 @@ class ObjectGroup : public gc::TenuredCellWithNonGCPointer<int> {
|
|||
return offsetof(ObjectGroup, proto_);
|
||||
}
|
||||
|
||||
static inline uint32_t offsetOfRealm() {
|
||||
return offsetof(ObjectGroup, realm_);
|
||||
}
|
||||
|
||||
friend class gc::GCRuntime;
|
||||
|
||||
// See JSObject::offsetOfGroup() comment.
|
||||
friend class js::jit::MacroAssembler;
|
||||
|
||||
public:
|
||||
inline ObjectGroup(TaggedProto proto, JS::Realm* realm);
|
||||
inline explicit ObjectGroup(TaggedProto proto);
|
||||
|
||||
const GCPtr<TaggedProto>& proto() const { return proto_; }
|
||||
|
||||
|
@ -77,12 +75,6 @@ class ObjectGroup : public gc::TenuredCellWithNonGCPointer<int> {
|
|||
|
||||
void setProtoUnchecked(TaggedProto proto);
|
||||
|
||||
JS::Compartment* compartment() const {
|
||||
return JS::GetCompartmentForRealm(realm_);
|
||||
}
|
||||
JS::Compartment* maybeCompartment() const { return compartment(); }
|
||||
JS::Realm* realm() const { return realm_; }
|
||||
|
||||
/* Helpers */
|
||||
|
||||
void traceChildren(JSTracer* trc);
|
||||
|
|
|
@ -101,7 +101,8 @@ ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
shape = EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0);
|
||||
shape =
|
||||
EmptyShape::getInitialShape(cx, clasp, realm, proto, /* nfixed = */ 0);
|
||||
if (!shape) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -109,8 +110,7 @@ ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler,
|
|||
realm->newProxyCache.add(group, shape);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(group->realm() == realm);
|
||||
MOZ_ASSERT(shape->zone() == cx->zone());
|
||||
MOZ_ASSERT(shape->realm() == realm);
|
||||
MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(group.address()));
|
||||
MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape.address()));
|
||||
|
||||
|
|
|
@ -96,8 +96,8 @@ Shape* js::EmptyEnvironmentShape(JSContext* cx, const JSClass* cls,
|
|||
uint32_t numSlots, ObjectFlags objectFlags) {
|
||||
// Put as many slots into the object header as possible.
|
||||
uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots));
|
||||
return EmptyShape::getInitialShape(cx, cls, TaggedProto(nullptr), numFixed,
|
||||
objectFlags);
|
||||
return EmptyShape::getInitialShape(cx, cls, cx->realm(), TaggedProto(nullptr),
|
||||
numFixed, objectFlags);
|
||||
}
|
||||
|
||||
static Shape* NextEnvironmentShape(JSContext* cx, HandleAtom name,
|
||||
|
@ -542,9 +542,9 @@ uint32_t Scope::environmentChainLength() const {
|
|||
}
|
||||
|
||||
Shape* Scope::maybeCloneEnvironmentShape(JSContext* cx) {
|
||||
// Clone the environment shape if cloning into a different zone.
|
||||
// Clone the environment shape if cloning into a different realm.
|
||||
Shape* shape = environmentShape();
|
||||
if (shape && shape->zoneFromAnyThread() != cx->zone()) {
|
||||
if (shape && shape->realm() != cx->realm()) {
|
||||
BindingIter bi(this);
|
||||
return CreateEnvironmentShape(cx, bi, shape->getObjectClass(),
|
||||
shape->slotSpan(), shape->objectFlags());
|
||||
|
|
|
@ -29,7 +29,8 @@ inline AutoKeepShapeCaches::~AutoKeepShapeCaches() {
|
|||
cx_->zone()->setKeepShapeCaches(prev_);
|
||||
}
|
||||
|
||||
inline StackBaseShape::StackBaseShape(const JSClass* clasp) : clasp(clasp) {}
|
||||
inline StackBaseShape::StackBaseShape(const JSClass* clasp, JS::Realm* realm)
|
||||
: clasp(clasp), realm(realm) {}
|
||||
|
||||
MOZ_ALWAYS_INLINE Shape* Shape::search(JSContext* cx, jsid id) {
|
||||
return search(cx, this, id);
|
||||
|
|
|
@ -329,8 +329,8 @@ Shape* Shape::replaceLastProperty(JSContext* cx, ObjectFlags objectFlags,
|
|||
if (!shape->parent) {
|
||||
/* Treat as resetting the initial property of the shape hierarchy. */
|
||||
gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
|
||||
return EmptyShape::getInitialShape(cx, shape->getObjectClass(), proto, kind,
|
||||
objectFlags);
|
||||
return EmptyShape::getInitialShape(
|
||||
cx, shape->getObjectClass(), shape->realm(), proto, kind, objectFlags);
|
||||
}
|
||||
|
||||
Rooted<StackShape> child(cx, StackShape(shape));
|
||||
|
@ -1408,8 +1408,14 @@ Shape* Shape::setObjectFlag(JSContext* cx, ObjectFlag flag, TaggedProto proto,
|
|||
}
|
||||
|
||||
inline BaseShape::BaseShape(const StackBaseShape& base)
|
||||
: TenuredCellWithNonGCPointer(base.clasp) {
|
||||
: TenuredCellWithNonGCPointer(base.clasp), realm_(base.realm) {
|
||||
MOZ_ASSERT(JS::StringIsASCII(clasp()->name));
|
||||
|
||||
#ifdef DEBUG
|
||||
if (GlobalObject* global = realm()->unsafeUnbarrieredMaybeGlobal()) {
|
||||
AssertTargetIsNotGray(global);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -1435,8 +1441,6 @@ BaseShape* BaseShape::get(JSContext* cx, StackBaseShape& base) {
|
|||
return nbase;
|
||||
}
|
||||
|
||||
void BaseShape::traceChildren(JSTracer* trc) {}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool Shape::canSkipMarkingShapeCache() {
|
||||
// Check that every shape in the shape table will be marked by marking
|
||||
|
@ -1501,8 +1505,8 @@ void Zone::checkInitialShapesTableAfterMovingGC() {
|
|||
}
|
||||
|
||||
using Lookup = InitialShapeEntry::Lookup;
|
||||
Lookup lookup(shape->getObjectClass(), proto, shape->numFixedSlots(),
|
||||
shape->objectFlags());
|
||||
Lookup lookup(shape->getObjectClass(), shape->realm(), proto,
|
||||
shape->numFixedSlots(), shape->objectFlags());
|
||||
InitialShapeSet::Ptr ptr = initialShapes().lookup(lookup);
|
||||
MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
|
||||
}
|
||||
|
@ -1918,22 +1922,23 @@ void Shape::dumpSubtree(int level, js::GenericPrinter& out) const {
|
|||
|
||||
/* static */
|
||||
Shape* EmptyShape::getInitialShape(JSContext* cx, const JSClass* clasp,
|
||||
TaggedProto proto, size_t nfixed,
|
||||
ObjectFlags objectFlags) {
|
||||
JS::Realm* realm, TaggedProto proto,
|
||||
size_t nfixed, ObjectFlags objectFlags) {
|
||||
MOZ_ASSERT(cx->compartment() == realm->compartment());
|
||||
MOZ_ASSERT_IF(proto.isObject(),
|
||||
cx->isInsideCurrentCompartment(proto.toObject()));
|
||||
|
||||
auto& table = cx->zone()->initialShapes();
|
||||
auto& table = realm->zone()->initialShapes();
|
||||
|
||||
using Lookup = InitialShapeEntry::Lookup;
|
||||
auto protoPointer =
|
||||
MakeDependentAddPtr(cx, table, Lookup(clasp, proto, nfixed, objectFlags));
|
||||
auto protoPointer = MakeDependentAddPtr(
|
||||
cx, table, Lookup(clasp, realm, proto, nfixed, objectFlags));
|
||||
if (protoPointer) {
|
||||
return protoPointer->shape;
|
||||
}
|
||||
|
||||
Rooted<TaggedProto> protoRoot(cx, proto);
|
||||
StackBaseShape base(clasp);
|
||||
StackBaseShape base(clasp, realm);
|
||||
Rooted<BaseShape*> nbase(cx, BaseShape::get(cx, base));
|
||||
if (!nbase) {
|
||||
return nullptr;
|
||||
|
@ -1944,7 +1949,7 @@ Shape* EmptyShape::getInitialShape(JSContext* cx, const JSClass* clasp,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Lookup lookup(clasp, protoRoot, nfixed, objectFlags);
|
||||
Lookup lookup(clasp, realm, protoRoot, nfixed, objectFlags);
|
||||
if (!protoPointer.add(cx, table, lookup,
|
||||
InitialShapeEntry(shape, protoRoot))) {
|
||||
return nullptr;
|
||||
|
@ -1955,9 +1960,10 @@ Shape* EmptyShape::getInitialShape(JSContext* cx, const JSClass* clasp,
|
|||
|
||||
/* static */
|
||||
Shape* EmptyShape::getInitialShape(JSContext* cx, const JSClass* clasp,
|
||||
TaggedProto proto, gc::AllocKind kind,
|
||||
JS::Realm* realm, TaggedProto proto,
|
||||
gc::AllocKind kind,
|
||||
ObjectFlags objectFlags) {
|
||||
return getInitialShape(cx, clasp, proto, GetGCKindSlots(kind, clasp),
|
||||
return getInitialShape(cx, clasp, realm, proto, GetGCKindSlots(kind, clasp),
|
||||
objectFlags);
|
||||
}
|
||||
|
||||
|
@ -1986,7 +1992,7 @@ void NewObjectCache::invalidateEntriesForShape(Shape* shape, JSObject* proto) {
|
|||
void EmptyShape::insertInitialShape(JSContext* cx, HandleShape shape,
|
||||
HandleObject proto) {
|
||||
using Lookup = InitialShapeEntry::Lookup;
|
||||
Lookup lookup(shape->getObjectClass(), TaggedProto(proto),
|
||||
Lookup lookup(shape->getObjectClass(), shape->realm(), TaggedProto(proto),
|
||||
shape->numFixedSlots(), shape->objectFlags());
|
||||
|
||||
InitialShapeSet::Ptr p = cx->zone()->initialShapes().lookup(lookup);
|
||||
|
@ -2043,8 +2049,8 @@ void Zone::fixupInitialShapeTable() {
|
|||
if (proto.isObject() && IsForwarded(proto.toObject())) {
|
||||
entry.proto = TaggedProto(Forwarded(proto.toObject()));
|
||||
using Lookup = InitialShapeEntry::Lookup;
|
||||
Lookup relookup(shape->getObjectClass(), proto, shape->numFixedSlots(),
|
||||
shape->objectFlags());
|
||||
Lookup relookup(shape->getObjectClass(), shape->realm(), proto,
|
||||
shape->numFixedSlots(), shape->objectFlags());
|
||||
e.rekeyFront(relookup, entry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -676,8 +676,11 @@ class BaseShape : public gc::TenuredCellWithNonGCPointer<const JSClass> {
|
|||
const JSClass* clasp() const { return headerPtr(); }
|
||||
|
||||
private:
|
||||
JS::Realm* realm_;
|
||||
#ifndef JS_64BIT
|
||||
// Temporary padding to respect MinCellSize.
|
||||
uint64_t padding_ = 0;
|
||||
#endif
|
||||
|
||||
BaseShape(const BaseShape& base) = delete;
|
||||
BaseShape& operator=(const BaseShape& other) = delete;
|
||||
|
@ -690,6 +693,14 @@ class BaseShape : public gc::TenuredCellWithNonGCPointer<const JSClass> {
|
|||
/* Not defined: BaseShapes must not be stack allocated. */
|
||||
~BaseShape() = delete;
|
||||
|
||||
JS::Realm* realm() const { return realm_; }
|
||||
JS::Compartment* compartment() const {
|
||||
return JS::GetCompartmentForRealm(realm());
|
||||
}
|
||||
JS::Compartment* maybeCompartment() const { return compartment(); }
|
||||
|
||||
void setRealmForMergeRealms(JS::Realm* realm) { realm_ = realm; }
|
||||
|
||||
/*
|
||||
* Lookup base shapes from the zone's baseShapes table, adding if not
|
||||
* already found.
|
||||
|
@ -702,10 +713,14 @@ class BaseShape : public gc::TenuredCellWithNonGCPointer<const JSClass> {
|
|||
|
||||
static constexpr size_t offsetOfClasp() { return offsetOfHeaderPtr(); }
|
||||
|
||||
static constexpr size_t offsetOfRealm() {
|
||||
return offsetof(BaseShape, realm_);
|
||||
}
|
||||
|
||||
private:
|
||||
static void staticAsserts() {
|
||||
static_assert(offsetOfHeaderPtr() ==
|
||||
offsetof(JS::shadow::BaseShape, clasp));
|
||||
static_assert(offsetOfClasp() == offsetof(JS::shadow::BaseShape, clasp));
|
||||
static_assert(offsetOfRealm() == offsetof(JS::shadow::BaseShape, realm));
|
||||
static_assert(sizeof(BaseShape) % gc::CellAlignBytes == 0,
|
||||
"Things inheriting from gc::Cell must have a size that's "
|
||||
"a multiple of gc::CellAlignBytes");
|
||||
|
@ -715,26 +730,28 @@ class BaseShape : public gc::TenuredCellWithNonGCPointer<const JSClass> {
|
|||
/* Entries for the per-zone baseShapes set. */
|
||||
struct StackBaseShape : public DefaultHasher<WeakHeapPtr<BaseShape*>> {
|
||||
const JSClass* clasp;
|
||||
JS::Realm* realm;
|
||||
|
||||
explicit inline StackBaseShape(const JSClass* clasp);
|
||||
inline StackBaseShape(const JSClass* clasp, JS::Realm* realm);
|
||||
|
||||
struct Lookup {
|
||||
const JSClass* clasp;
|
||||
JS::Realm* realm;
|
||||
|
||||
MOZ_IMPLICIT Lookup(const StackBaseShape& base) : clasp(base.clasp) {}
|
||||
MOZ_IMPLICIT Lookup(const StackBaseShape& base)
|
||||
: clasp(base.clasp), realm(base.realm) {}
|
||||
|
||||
MOZ_IMPLICIT Lookup(BaseShape* base) : clasp(base->clasp()) {}
|
||||
|
||||
explicit Lookup(const WeakHeapPtr<BaseShape*>& base)
|
||||
: clasp(base.unbarrieredGet()->clasp()) {}
|
||||
MOZ_IMPLICIT Lookup(BaseShape* base)
|
||||
: clasp(base->clasp()), realm(base->realm()) {}
|
||||
};
|
||||
|
||||
static HashNumber hash(const Lookup& lookup) {
|
||||
return mozilla::HashGeneric(lookup.clasp);
|
||||
return mozilla::HashGeneric(lookup.clasp, lookup.realm);
|
||||
}
|
||||
static inline bool match(const WeakHeapPtr<BaseShape*>& key,
|
||||
const Lookup& lookup) {
|
||||
return key.unbarrieredGet()->clasp() == lookup.clasp;
|
||||
return key.unbarrieredGet()->clasp() == lookup.clasp &&
|
||||
key.unbarrieredGet()->realm() == lookup.realm;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1014,6 +1031,12 @@ class Shape : public gc::CellWithTenuredGCPointer<gc::TenuredCell, BaseShape> {
|
|||
};
|
||||
|
||||
const JSClass* getObjectClass() const { return base()->clasp(); }
|
||||
JS::Realm* realm() const { return base()->realm(); }
|
||||
|
||||
JS::Compartment* compartment() const { return base()->compartment(); }
|
||||
JS::Compartment* maybeCompartment() const {
|
||||
return base()->maybeCompartment();
|
||||
}
|
||||
|
||||
static Shape* setObjectFlag(JSContext* cx, ObjectFlag flag, TaggedProto proto,
|
||||
Shape* last);
|
||||
|
@ -1358,10 +1381,11 @@ struct EmptyShape : public js::Shape {
|
|||
* shape if none was found.
|
||||
*/
|
||||
static Shape* getInitialShape(JSContext* cx, const JSClass* clasp,
|
||||
TaggedProto proto, size_t nfixed,
|
||||
ObjectFlags objectFlags = {});
|
||||
JS::Realm* realm, TaggedProto proto,
|
||||
size_t nfixed, ObjectFlags objectFlags = {});
|
||||
static Shape* getInitialShape(JSContext* cx, const JSClass* clasp,
|
||||
TaggedProto proto, gc::AllocKind kind,
|
||||
JS::Realm* realm, TaggedProto proto,
|
||||
gc::AllocKind kind,
|
||||
ObjectFlags objectFlags = {});
|
||||
|
||||
/*
|
||||
|
@ -1407,13 +1431,15 @@ struct InitialShapeEntry {
|
|||
/* State used to determine a match on an initial shape. */
|
||||
struct Lookup {
|
||||
const JSClass* clasp;
|
||||
JS::Realm* realm;
|
||||
TaggedProto proto;
|
||||
uint32_t nfixed;
|
||||
ObjectFlags objectFlags;
|
||||
|
||||
Lookup(const JSClass* clasp, const TaggedProto& proto, uint32_t nfixed,
|
||||
ObjectFlags objectFlags)
|
||||
Lookup(const JSClass* clasp, JS::Realm* realm, const TaggedProto& proto,
|
||||
uint32_t nfixed, ObjectFlags objectFlags)
|
||||
: clasp(clasp),
|
||||
realm(realm),
|
||||
proto(proto),
|
||||
nfixed(nfixed),
|
||||
objectFlags(objectFlags) {}
|
||||
|
@ -1424,13 +1450,14 @@ struct InitialShapeEntry {
|
|||
|
||||
static HashNumber hash(const Lookup& lookup) {
|
||||
HashNumber hash = MovableCellHasher<TaggedProto>::hash(lookup.proto);
|
||||
return mozilla::AddToHash(hash,
|
||||
mozilla::HashGeneric(lookup.clasp, lookup.nfixed,
|
||||
lookup.objectFlags.toRaw()));
|
||||
return mozilla::AddToHash(
|
||||
hash, mozilla::HashGeneric(lookup.clasp, lookup.realm, lookup.nfixed,
|
||||
lookup.objectFlags.toRaw()));
|
||||
}
|
||||
static inline bool match(const InitialShapeEntry& key, const Lookup& lookup) {
|
||||
const Shape* shape = key.shape.unbarrieredGet();
|
||||
return lookup.clasp == shape->getObjectClass() &&
|
||||
lookup.realm == shape->realm() &&
|
||||
lookup.nfixed == shape->numFixedSlots() &&
|
||||
lookup.objectFlags == shape->objectFlags() &&
|
||||
key.proto.unbarrieredGet() == lookup.proto;
|
||||
|
|
Загрузка…
Ссылка в новой задаче