/* * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "precompiled.hpp" #include "memory/allocation.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" #include "oops/array.hpp" #include "oops/metadata.hpp" #include "runtime/javaThread.hpp" #include "unittest.hpp" class MyMetaData : public MetaspaceObj { public: MyMetaData* _a; MyMetaData* _b; MyMetaData() : _a(nullptr), _b(nullptr) {} MetaspaceObj::Type type() const { return MetaspaceObj::SymbolType; // Just lie. It doesn't matter in this test } const char* internal_name() const { return "MyMetaData"; } int size() const { return align_up((int)sizeof(MyMetaData), wordSize) / wordSize; }; static bool is_read_only_by_default() { return true; } void metaspace_pointers_do(MetaspaceClosure* it) { it->push(&_a); it->push(&_b); } }; class MyUniqueMetaspaceClosure : public MetaspaceClosure { static constexpr int SIZE = 10; MyMetaData* _visited[SIZE]; int _count; public: MyUniqueMetaspaceClosure() { for (int i = 0; i < SIZE; i++) { _visited[i] = nullptr; } _count = 0; } virtual bool do_ref(Ref* ref, bool read_only) { MyMetaData* ptr = (MyMetaData*)ref->obj(); assert(_count < SIZE, "out of bounds"); _visited[_count++] = ptr; return true; // recurse } bool has_visited(MyMetaData* p) { for (int i = 0; i < SIZE; i++) { if (_visited[i] == p) { return true; } } return false; } }; // iterate an Array TEST_VM(MetaspaceClosure, MSOPointerArrayRef) { JavaThread* THREAD = JavaThread::current(); ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data(); Array* array = MetadataFactory::new_array(cld, 4, THREAD); for (int i = 0; i < array->length(); i++) { EXPECT_TRUE(array->at(i) == nullptr) << "should be initialized to null"; } MyMetaData x; MyMetaData y; MyMetaData z; array->at_put(0, &x); array->at_put(2, &y); y._a = &z; MyUniqueMetaspaceClosure closure; closure.push(&array); EXPECT_TRUE(closure.has_visited(&x)) << "must be"; EXPECT_TRUE(closure.has_visited(&y)) << "must be"; EXPECT_TRUE(closure.has_visited(&z)) << "must be"; } // iterate an Array TEST_VM(MetaspaceClosure, MSOArrayRef) { JavaThread* THREAD = JavaThread::current(); ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data(); Array* array = MetadataFactory::new_array(cld, 4, THREAD); for (int i = 0; i < array->length(); i++) { EXPECT_TRUE(array->at(i)._a == nullptr) << "should be initialized to null"; EXPECT_TRUE(array->at(i)._b == nullptr) << "should be initialized to null"; } MyMetaData x; MyMetaData y; MyMetaData z; array->adr_at(0)->_a = &x; array->adr_at(2)->_b = &y; y._a = &z; MyUniqueMetaspaceClosure closure; closure.push(&array); EXPECT_TRUE(closure.has_visited(&x)) << "must be"; EXPECT_TRUE(closure.has_visited(&y)) << "must be"; EXPECT_TRUE(closure.has_visited(&z)) << "must be"; }