1 /**
2 * Copyright (c) 2023-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "debugger/object_repository.h"
17
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20
21 #include "assembly-emitter.h"
22 #include "assembly-parser.h"
23 #include "runtime.h"
24 #include "runtime_options.h"
25 #include "tooling/debugger.h"
26
27 #include "types/numeric_id.h"
28
29 #include "common.h"
30 #include "json_object_matcher.h"
31
32 // NOLINTBEGIN
33
34 namespace ark::tooling::inspector::test {
35
36 static constexpr const char *g_source = R"(
37 .record Test {}
38
39 .function i32 Test.foo() {
40 ldai 111
41 return
42 }
43 )";
44
45 class ObjectRepositoryTest : public testing::Test {
46 protected:
SetUpTestSuite()47 static void SetUpTestSuite()
48 {
49 RuntimeOptions options;
50 options.SetShouldInitializeIntrinsics(false);
51 options.SetShouldLoadBootPandaFiles(false);
52 Runtime::Create(options);
53 thread_ = ark::MTManagedThread::GetCurrent();
54 thread_->ManagedCodeBegin();
55
56 pandasm::Parser p;
57
58 auto res = p.Parse(g_source, "source.pa");
59 ASSERT_TRUE(res.HasValue());
60 auto pf = pandasm::AsmEmitter::Emit(res.Value());
61 ASSERT(pf);
62 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
63 classLinker->AddPandaFile(std::move(pf));
64
65 PandaString descriptor;
66 auto *ext = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
67 Class *klass = ext->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("Test"), &descriptor));
68 ASSERT_NE(klass, nullptr);
69
70 auto methods = klass->GetMethods();
71 ASSERT_EQ(methods.size(), 1);
72
73 clsObject = klass->GetManagedObject();
74 methodFoo = &methods[0];
75 }
76
TearDownTestSuite()77 static void TearDownTestSuite()
78 {
79 thread_->ManagedCodeEnd();
80 Runtime::Destroy();
81 }
82
83 static constexpr uint16_t U16_VALUE = 43602;
84 static constexpr int32_t I32_VALUE = -2345678;
85 static constexpr int64_t I64_VALUE = 200000000000002;
86 static constexpr double F64_VALUE = 6.547;
87
88 static ark::MTManagedThread *thread_;
89 static Method *methodFoo;
90 static ObjectHeader *clsObject;
91 };
92
93 ark::MTManagedThread *ObjectRepositoryTest::thread_ = nullptr;
94 Method *ObjectRepositoryTest::methodFoo = nullptr;
95 ObjectHeader *ObjectRepositoryTest::clsObject = nullptr;
96
97 template <typename ValueT, typename V>
GetPrimitiveProperties(const char * type,V value,const char * valueName="value")98 static auto GetPrimitiveProperties(const char *type, V value, const char *valueName = "value")
99 {
100 return JsonProperties(JsonProperty<JsonObject::StringT> {"type", type}, JsonProperty<ValueT> {valueName, value});
101 }
102
103 template <typename NameT, typename DescT>
GetObjectProperties(NameT className,DescT description,const char * objectId)104 static auto GetObjectProperties(NameT className, DescT description, const char *objectId)
105 {
106 return JsonProperties(JsonProperty<JsonObject::StringT> {"type", "object"},
107 JsonProperty<JsonObject::StringT> {"className", className},
108 JsonProperty<JsonObject::StringT> {"description", description},
109 JsonProperty<JsonObject::StringT> {"objectId", objectId});
110 }
111
112 template <typename NameT, typename V>
GetFrameObjectProperties(NameT name,V valueProperties)113 static auto GetFrameObjectProperties(NameT name, V valueProperties)
114 {
115 return JsonProperties(JsonProperty<JsonObject::StringT> {"name", name},
116 JsonProperty<JsonObject::JsonObjPointer> {"value", valueProperties},
117 JsonProperty<JsonObject::BoolT> {"writable", testing::_},
118 JsonProperty<JsonObject::BoolT> {"configurable", testing::_},
119 JsonProperty<JsonObject::BoolT> {"enumerable", testing::_});
120 }
121
TEST_F(ObjectRepositoryTest,S)122 TEST_F(ObjectRepositoryTest, S)
123 {
124 ObjectRepository obj;
125
126 auto clsObj = obj.CreateObject(TypedValue::Reference(clsObject));
127 ASSERT_EQ(clsObj.GetObjectId(), RemoteObjectId(1));
128 ASSERT_THAT(ToJson(clsObj), GetObjectProperties(testing::_, testing::_, "1"));
129
130 auto nullObj = obj.CreateObject(TypedValue::Reference(nullptr));
131 ASSERT_THAT(ToJson(nullObj), JsonProperties(JsonProperty<JsonObject::StringT> {"type", "object"},
132 JsonProperty<JsonObject::StringT> {"subtype", "null"},
133 JsonProperty<JsonObject::JsonObjPointer> {"value", testing::IsNull()}));
134
135 auto invObj = obj.CreateObject(TypedValue::Invalid());
136 ASSERT_THAT(ToJson(invObj), JsonProperties(JsonProperty<JsonObject::StringT> {"type", "undefined"}));
137
138 auto boolObj = obj.CreateObject(TypedValue::U1(true));
139 ASSERT_THAT(ToJson(boolObj), GetPrimitiveProperties<JsonObject::BoolT>("boolean", true));
140
141 auto numObj = obj.CreateObject(TypedValue::U16(U16_VALUE));
142 ASSERT_THAT(ToJson(numObj), GetPrimitiveProperties<JsonObject::NumT>("number", U16_VALUE));
143
144 auto negObj = obj.CreateObject(TypedValue::I32(I32_VALUE));
145 ASSERT_THAT(ToJson(negObj), GetPrimitiveProperties<JsonObject::NumT>("number", I32_VALUE));
146
147 auto hugeObj = obj.CreateObject(TypedValue::I64(I64_VALUE));
148 ASSERT_THAT(ToJson(hugeObj),
149 GetPrimitiveProperties<JsonObject::StringT>("bigint", "200000000000002", "unserializableValue"));
150
151 auto doubObj = obj.CreateObject(TypedValue::F64(F64_VALUE));
152 ASSERT_THAT(ToJson(doubObj), GetPrimitiveProperties<JsonObject::NumT>("number", testing::DoubleEq(F64_VALUE)));
153
154 auto globObj1 = obj.CreateGlobalObject();
155 ASSERT_THAT(ToJson(globObj1), GetObjectProperties("[Global]", "Global object", "0"));
156
157 auto globObj2 = obj.CreateGlobalObject();
158 ASSERT_THAT(ToJson(globObj2), GetObjectProperties("[Global]", "Global object", "0"));
159
160 PtDebugFrame frame(methodFoo, nullptr);
161 std::map<std::string, TypedValue> locals;
162 locals.emplace("a", TypedValue::U16(56U));
163 locals.emplace("ref", TypedValue::Reference(clsObject));
164 // "this" parameter for static languages. Note that ArkTS uses "=t" instead of "this".
165 locals.emplace("this", TypedValue::Reference(clsObject));
166
167 std::optional<RemoteObject> objThis;
168 auto frameObj = obj.CreateFrameObject(frame, locals, objThis);
169 ASSERT_EQ(frameObj.GetObjectId().value(), RemoteObjectId(2UL));
170
171 auto properties = obj.GetProperties(frameObj.GetObjectId().value(), false);
172 ASSERT_EQ(properties.size(), 2UL);
173 ASSERT_EQ(properties[0].GetName(), "a");
174
175 ASSERT_THAT(ToJson(frameObj), GetObjectProperties("", "Frame #0", "2"));
176
177 ASSERT_THAT(
178 ToJson(properties[0]),
179 GetFrameObjectProperties("a", testing::Pointee(GetPrimitiveProperties<JsonObject::NumT>("number", 56U))));
180
181 ASSERT_THAT(ToJson(properties[1]),
182 GetFrameObjectProperties("ref", testing::Pointee(GetObjectProperties(testing::_, testing::_, "1"))));
183
184 // Call to "CreateFrameObject" must find and fill "this" parameter.
185 ASSERT_TRUE(objThis.has_value());
186 auto idThis = objThis->GetObjectId();
187 ASSERT_TRUE(idThis.has_value());
188 ASSERT_EQ(idThis.value(), 1U);
189 }
190
TEST_F(ObjectRepositoryTest,TestFrameObjectNoThis)191 TEST_F(ObjectRepositoryTest, TestFrameObjectNoThis)
192 {
193 ObjectRepository obj;
194
195 PtDebugFrame frame(methodFoo, nullptr);
196 std::map<std::string, TypedValue> locals;
197 locals.emplace("a", TypedValue::U16(56U));
198 locals.emplace("ref", TypedValue::Reference(clsObject));
199
200 std::optional<RemoteObject> objThis;
201 auto frameObj = obj.CreateFrameObject(frame, locals, objThis);
202 ASSERT_EQ(frameObj.GetObjectId().value(), RemoteObjectId(2UL));
203
204 auto properties = obj.GetProperties(frameObj.GetObjectId().value(), true);
205 ASSERT_EQ(properties.size(), 2UL);
206
207 // No "this" parameter was provided.
208 ASSERT_FALSE(objThis.has_value());
209 }
210 } // namespace ark::tooling::inspector::test
211
212 // NOLINTEND
213