• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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