1 /*
2 * Copyright (c) 2021 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 "ecmascript/ic/ic_runtime.h"
17
18 #include "ecmascript/ecma_macros.h"
19 #include "ecmascript/global_dictionary-inl.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/ic/ic_handler.h"
22 #include "ecmascript/ic/profile_type_info.h"
23 #include "ecmascript/interpreter/slow_runtime_stub.h"
24 #include "ecmascript/js_function.h"
25 #include "ecmascript/js_hclass-inl.h"
26 #include "ecmascript/js_proxy.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/js_typed_array.h"
29 #include "ecmascript/object_factory-inl.h"
30 #include "ecmascript/tagged_dictionary.h"
31 #include "ecmascript/message_string.h"
32
33 namespace panda::ecmascript {
34 #define TRACE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
35
UpdateLoadHandler(const ObjectOperator & op,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> receiver)36 void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle<JSTaggedValue> key,
37 JSHandle<JSTaggedValue> receiver)
38 {
39 if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) {
40 return;
41 }
42 if (IsNamedIC(GetICKind())) {
43 key = JSHandle<JSTaggedValue>();
44 }
45 JSHandle<JSTaggedValue> handlerValue;
46 if (receiver->IsNumber()) {
47 receiver = thread_->GetEcmaVM()->GetGlobalEnv()->GetNumberFunction();
48 }
49 JSHandle<JSHClass> hclass(GetThread(), receiver->GetTaggedObject()->GetClass());
50 // When a transition occurs without the shadow property, AOT does not trigger the
51 // notifyprototypechange behavior, so for the case where the property does not
52 // exist and the Hclass is AOT, IC needs to be abandoned.
53 if (hclass->IsTS() && !op.IsFound()) {
54 return;
55 }
56 if (op.IsElement()) {
57 if (!op.IsFound() && hclass->IsDictionaryElement()) {
58 return;
59 }
60 handlerValue = LoadHandler::LoadElement(thread_, op);
61 } else {
62 if (!op.IsFound()) {
63 JSTaggedValue proto = hclass->GetPrototype();
64 if (!proto.IsECMAObject()) {
65 handlerValue = LoadHandler::LoadProperty(thread_, op);
66 } else {
67 handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass);
68 }
69 } else if (!op.IsOnPrototype()) {
70 handlerValue = LoadHandler::LoadProperty(thread_, op);
71 } else {
72 // do not support global prototype ic
73 if (IsGlobalLoadIC(GetICKind()) || receiver->IsString()) {
74 return;
75 }
76 handlerValue = PrototypeHandler::LoadPrototype(thread_, op, hclass);
77 }
78 }
79
80 if (key.IsEmpty()) {
81 icAccessor_.AddHandlerWithoutKey(JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
82 } else if (op.IsElement()) {
83 // do not support global element ic
84 if (IsGlobalLoadIC(GetICKind())) {
85 return;
86 }
87 icAccessor_.AddElementHandler(JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
88 } else {
89 icAccessor_.AddHandlerWithKey(key, JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
90 }
91 }
92
UpdateLoadStringHandler(JSHandle<JSTaggedValue> receiver)93 void ICRuntime::UpdateLoadStringHandler(JSHandle<JSTaggedValue> receiver)
94 {
95 if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) {
96 return;
97 }
98 JSHandle<JSTaggedValue> handlerValue = LoadHandler::LoadStringElement(thread_);
99 JSHandle<JSHClass> hclass(GetThread(), receiver->GetTaggedObject()->GetClass());
100 icAccessor_.AddElementHandler(JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
101 }
102
UpdateTypedArrayHandler(JSHandle<JSTaggedValue> receiver)103 void ICRuntime::UpdateTypedArrayHandler(JSHandle<JSTaggedValue> receiver)
104 {
105 if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) {
106 return;
107 }
108 JSHandle<JSTaggedValue> handlerValue =
109 LoadHandler::LoadTypedArrayElement(thread_, JSHandle<JSTypedArray>(receiver));
110 JSHandle<JSHClass> hclass(GetThread(), receiver->GetTaggedObject()->GetClass());
111 icAccessor_.AddElementHandler(JSHandle<JSTaggedValue>::Cast(hclass), handlerValue);
112 }
113
UpdateStoreHandler(const ObjectOperator & op,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> receiver)114 void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle<JSTaggedValue> key,
115 JSHandle<JSTaggedValue> receiver)
116 {
117 if (icAccessor_.GetICState() == ProfileTypeAccessor::ICState::MEGA) {
118 return;
119 }
120 if (IsNamedIC(GetICKind())) {
121 key = JSHandle<JSTaggedValue>();
122 }
123 JSHandle<JSTaggedValue> handlerValue;
124 ASSERT(op.IsFound());
125
126 if (op.IsTransition()) {
127 if (op.IsOnPrototype()) {
128 JSHandle<JSHClass> hclass(thread_, JSHandle<JSObject>::Cast(receiver)->GetClass());
129 handlerValue = TransWithProtoHandler::StoreTransition(thread_, op, hclass);
130 } else {
131 handlerValue = TransitionHandler::StoreTransition(thread_, op);
132 }
133 } else if (op.IsOnPrototype()) {
134 // do not support global prototype ic
135 if (IsGlobalStoreIC(GetICKind())) {
136 return;
137 }
138 JSHandle<JSHClass> hclass(thread_, JSHandle<JSObject>::Cast(receiver)->GetClass());
139 handlerValue = PrototypeHandler::StorePrototype(thread_, op, hclass);
140 } else {
141 handlerValue = StoreHandler::StoreProperty(thread_, op);
142 }
143
144 if (key.IsEmpty()) {
145 icAccessor_.AddHandlerWithoutKey(receiverHClass_, handlerValue);
146 } else if (op.IsElement()) {
147 // do not support global element ic
148 if (IsGlobalStoreIC(GetICKind())) {
149 return;
150 }
151 icAccessor_.AddElementHandler(receiverHClass_, handlerValue);
152 } else {
153 icAccessor_.AddHandlerWithKey(key, receiverHClass_, handlerValue);
154 }
155 }
156
TraceIC(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key) const157 void ICRuntime::TraceIC([[maybe_unused]] JSHandle<JSTaggedValue> receiver,
158 [[maybe_unused]] JSHandle<JSTaggedValue> key) const
159 {
160 #if TRACE_IC
161 auto kind = ICKindToString(GetICKind());
162 auto state = ProfileTypeAccessor::ICStateToString(icAccessor_.GetICState());
163 if (key->IsString()) {
164 auto keyStrHandle = JSHandle<EcmaString>::Cast(key);
165 LOG_ECMA(ERROR) << kind << " miss key is: " << EcmaStringAccessor(keyStrHandle).ToCString()
166 << ", receiver is " << receiver->GetTaggedObject()->GetClass()->IsDictionaryMode()
167 << ", state is " << state;
168 } else {
169 LOG_ECMA(ERROR) << kind << " miss " << ", state is "
170 << ", receiver is " << receiver->GetTaggedObject()->GetClass()->IsDictionaryMode()
171 << state;
172 }
173 #endif
174 }
175
LoadValueMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key)176 JSTaggedValue LoadICRuntime::LoadValueMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
177 {
178 JSTaggedValue::RequireObjectCoercible(thread_, receiver, "Cannot load property of null or undefined");
179 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
180
181 if ((!receiver->IsJSObject() || receiver->HasOrdinaryGet()) && !receiver->IsString()) {
182 icAccessor_.SetAsMega();
183 JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread_, key);
184 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
185 return JSTaggedValue::GetProperty(thread_, receiver, propKey).GetValue().GetTaggedValue();
186 }
187 if (receiver->IsTypedArray()) {
188 return LoadTypedArrayValueMiss(receiver, key);
189 }
190 ObjectOperator op(GetThread(), receiver, key);
191 auto result = JSHandle<JSTaggedValue>(thread_, JSObject::GetProperty(GetThread(), &op));
192
193 if (receiver->IsString()) {
194 // do not cache element
195 if (!op.IsFastMode()) {
196 icAccessor_.SetAsMega();
197 return result.GetTaggedValue();
198 }
199 UpdateLoadStringHandler(receiver);
200 } else {
201 if (op.GetValue().IsInternalAccessor()) {
202 op = ObjectOperator(GetThread(), receiver, key);
203 }
204 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
205 // ic-switch
206 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
207 icAccessor_.SetAsMega();
208 return result.GetTaggedValue();
209 }
210 TraceIC(receiver, key);
211 // do not cache element
212 if (!op.IsFastMode()) {
213 icAccessor_.SetAsMega();
214 return result.GetTaggedValue();
215 }
216 UpdateLoadHandler(op, key, receiver);
217 }
218
219 return result.GetTaggedValue();
220 }
221
LoadMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key)222 JSTaggedValue LoadICRuntime::LoadMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
223 {
224 if ((!receiver->IsJSObject() || receiver->HasOrdinaryGet()) &&
225 !receiver->IsString() && !receiver->IsNumber()) {
226 icAccessor_.SetAsMega();
227 JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread_, key);
228 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
229 return JSTaggedValue::GetProperty(thread_, receiver, propKey).GetValue().GetTaggedValue();
230 }
231
232 ICKind kind = GetICKind();
233 // global variable find from global record firstly
234 if (kind == ICKind::NamedGlobalLoadIC || kind == ICKind::NamedGlobalTryLoadIC) {
235 JSTaggedValue box = SlowRuntimeStub::LdGlobalRecord(thread_, key.GetTaggedValue());
236 if (!box.IsUndefined()) {
237 ASSERT(box.IsPropertyBox());
238 if (icAccessor_.GetICState() != ProfileTypeAccessor::ICState::MEGA) {
239 icAccessor_.AddGlobalRecordHandler(JSHandle<JSTaggedValue>(thread_, box));
240 }
241 return PropertyBox::Cast(box.GetTaggedObject())->GetValue();
242 }
243 }
244
245 ObjectOperator op(GetThread(), receiver, key);
246 auto result = JSHandle<JSTaggedValue>(thread_, JSObject::GetProperty(GetThread(), &op));
247 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
248 if (op.GetValue().IsAccessor()) {
249 op = ObjectOperator(GetThread(), receiver, key);
250 }
251 if (!op.IsFound() && kind == ICKind::NamedGlobalTryLoadIC) {
252 return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined");
253 }
254 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
255 // ic-switch
256 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
257 icAccessor_.SetAsMega();
258 return result.GetTaggedValue();
259 }
260 TraceIC(receiver, key);
261 // do not cache element
262 if (!op.IsFastMode()) {
263 icAccessor_.SetAsMega();
264 return result.GetTaggedValue();
265 }
266
267 UpdateLoadHandler(op, key, receiver);
268 return result.GetTaggedValue();
269 }
270
LoadTypedArrayValueMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key)271 JSTaggedValue LoadICRuntime::LoadTypedArrayValueMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
272 {
273 JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(GetThread(), key);
274 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
275 JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(GetThread(), propKey);
276 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
277 if (!numericIndex.IsUndefined()) {
278 if (!JSTypedArray::IsValidIntegerIndex(receiver, numericIndex) || !GetThread()->GetEcmaVM()->ICEnabled()) {
279 icAccessor_.SetAsMega();
280 return JSTaggedValue::GetProperty(GetThread(), receiver, propKey).GetValue().GetTaggedValue();
281 }
282 UpdateTypedArrayHandler(receiver);
283 JSHandle<JSTaggedValue> indexHandle(GetThread(), numericIndex);
284 uint32_t index = static_cast<uint32_t>(JSTaggedValue::ToInteger(GetThread(), indexHandle).ToInt32());
285 JSType type = receiver->GetTaggedObject()->GetClass()->GetObjectType();
286 return JSTypedArray::FastGetPropertyByIndex(GetThread(), receiver.GetTaggedValue(), index, type);
287 } else {
288 ObjectOperator op(GetThread(), receiver, key);
289 auto result = JSHandle<JSTaggedValue>(GetThread(), JSObject::GetProperty(GetThread(), &op));
290 if (op.GetValue().IsInternalAccessor()) {
291 op = ObjectOperator(GetThread(), receiver, key);
292 }
293 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
294 // ic-switch
295 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
296 icAccessor_.SetAsMega();
297 return result.GetTaggedValue();
298 }
299 TraceIC(receiver, key);
300 // do not cache element
301 if (!op.IsFastMode()) {
302 icAccessor_.SetAsMega();
303 return result.GetTaggedValue();
304 }
305 UpdateLoadHandler(op, key, receiver);
306 return result.GetTaggedValue();
307 }
308 }
309
StoreMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value,bool isOwn)310 JSTaggedValue StoreICRuntime::StoreMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key,
311 JSHandle<JSTaggedValue> value, bool isOwn)
312 {
313 if (!receiver->IsJSObject() || receiver->HasOrdinaryGet()) {
314 icAccessor_.SetAsMega();
315 bool success = JSTaggedValue::SetProperty(GetThread(), receiver, key, value, true);
316 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
317 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
318 }
319 if (receiver->IsTypedArray()) {
320 return StoreTypedArrayValueMiss(receiver, key, value);
321 }
322 ICKind kind = GetICKind();
323 // global variable find from global record firstly
324 if (kind == ICKind::NamedGlobalStoreIC || kind == ICKind::NamedGlobalTryStoreIC) {
325 JSTaggedValue box = SlowRuntimeStub::LdGlobalRecord(thread_, key.GetTaggedValue());
326 if (!box.IsUndefined()) {
327 ASSERT(box.IsPropertyBox());
328 SlowRuntimeStub::TryUpdateGlobalRecord(thread_, key.GetTaggedValue(), value.GetTaggedValue());
329 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
330 if (icAccessor_.GetICState() != ProfileTypeAccessor::ICState::MEGA) {
331 icAccessor_.AddGlobalRecordHandler(JSHandle<JSTaggedValue>(thread_, box));
332 }
333 return JSTaggedValue::Undefined();
334 }
335 }
336 UpdateReceiverHClass(JSHandle<JSTaggedValue>(GetThread(), JSHandle<JSObject>::Cast(receiver)->GetClass()));
337 ObjectOperator op = ConstructOp(receiver, key, value, isOwn);
338 if (!op.IsFound()) {
339 if (kind == ICKind::NamedGlobalStoreIC) {
340 PropertyAttributes attr = PropertyAttributes::Default(true, true, false);
341 op.SetAttr(attr);
342 } else if (kind == ICKind::NamedGlobalTryStoreIC) {
343 return SlowRuntimeStub::ThrowReferenceError(GetThread(), key.GetTaggedValue(), " is not defined");
344 }
345 }
346 bool success = JSObject::SetProperty(&op, value, true);
347 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_);
348 // ic-switch
349 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
350 icAccessor_.SetAsMega();
351 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
352 }
353 TraceIC(receiver, key);
354 // do not cache element
355 if (!op.IsFastMode()) {
356 icAccessor_.SetAsMega();
357 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
358 }
359 if (success) {
360 UpdateStoreHandler(op, key, receiver);
361 return JSTaggedValue::Undefined();
362 }
363 return JSTaggedValue::Exception();
364 }
365
StoreTypedArrayValueMiss(JSHandle<JSTaggedValue> receiver,JSHandle<JSTaggedValue> key,JSHandle<JSTaggedValue> value)366 JSTaggedValue StoreICRuntime::StoreTypedArrayValueMiss(JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key,
367 JSHandle<JSTaggedValue> value)
368 {
369 JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(GetThread(), key);
370 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
371 JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(GetThread(), propKey);
372 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
373 if (!numericIndex.IsUndefined()) {
374 if (!JSTypedArray::IsValidIntegerIndex(receiver, numericIndex) || value->IsECMAObject() ||
375 !GetThread()->GetEcmaVM()->ICEnabled()) {
376 icAccessor_.SetAsMega();
377 bool success = JSTaggedValue::SetProperty(GetThread(), receiver, propKey, value, true);
378 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
379 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
380 }
381 UpdateTypedArrayHandler(receiver);
382 JSHandle<JSTaggedValue> indexHandle(GetThread(), numericIndex);
383 uint32_t index = static_cast<uint32_t>(JSTaggedValue::ToInteger(GetThread(), indexHandle).ToInt32());
384 JSType type = receiver->GetTaggedObject()->GetClass()->GetObjectType();
385 return JSTypedArray::FastSetPropertyByIndex(GetThread(), receiver.GetTaggedValue(), index,
386 value.GetTaggedValue(), type);
387 } else {
388 UpdateReceiverHClass(JSHandle<JSTaggedValue>(GetThread(), JSHandle<JSObject>::Cast(receiver)->GetClass()));
389 ObjectOperator op(GetThread(), receiver, key);
390 bool success = JSObject::SetProperty(&op, value, true);
391 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(GetThread());
392 // ic-switch
393 if (!GetThread()->GetEcmaVM()->ICEnabled()) {
394 icAccessor_.SetAsMega();
395 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
396 }
397 TraceIC(receiver, key);
398 // do not cache element
399 if (!op.IsFastMode()) {
400 icAccessor_.SetAsMega();
401 return success ? JSTaggedValue::Undefined() : JSTaggedValue::Exception();
402 }
403 if (success) {
404 UpdateStoreHandler(op, key, receiver);
405 return JSTaggedValue::Undefined();
406 }
407 return JSTaggedValue::Exception();
408 }
409 }
410 } // namespace panda::ecmascript
411