1 /*
2 * Copyright (c) 2021-2023 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 #ifndef ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H
17 #define ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H
18
19 #include "ecmascript/base/config.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/global_dictionary-inl.h"
22 #include "ecmascript/ic/ic_runtime_stub.h"
23 #include "ecmascript/ic/ic_handler.h"
24 #include "ecmascript/ic/ic_runtime.h"
25 #include "ecmascript/interpreter/fast_runtime_stub-inl.h"
26 #include "ecmascript/ic/proto_change_details.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/js_array.h"
29 #include "ecmascript/js_hclass-inl.h"
30 #include "ecmascript/js_function.h"
31 #include "ecmascript/js_proxy.h"
32 #include "ecmascript/js_handle.h"
33 #include "ecmascript/js_tagged_value.h"
34 #include "ecmascript/object_factory-inl.h"
35 #include "ecmascript/runtime_call_id.h"
36 #include "ecmascript/js_primitive_ref.h"
37
38 namespace panda::ecmascript {
LoadGlobalICByName(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue globalValue,JSTaggedValue key,uint32_t slotId,bool tryLoad)39 JSTaggedValue ICRuntimeStub::LoadGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
40 JSTaggedValue globalValue, JSTaggedValue key, uint32_t slotId,
41 bool tryLoad)
42 {
43 INTERPRETER_TRACE(thread, LoadGlobalICByName);
44 JSTaggedValue handler = profileTypeInfo->Get(thread, slotId);
45 if (handler.IsHeapObject()) {
46 auto result = LoadGlobal(thread, handler);
47 if (!result.IsHole()) {
48 return result;
49 }
50 }
51 ICKind kind = tryLoad ? ICKind::NamedGlobalTryLoadIC : ICKind::NamedGlobalLoadIC;
52 return LoadMiss(thread, profileTypeInfo, globalValue, key, slotId, kind);
53 }
54
StoreGlobalICByName(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue globalValue,JSTaggedValue key,JSTaggedValue value,uint32_t slotId,bool tryStore)55 JSTaggedValue ICRuntimeStub::StoreGlobalICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
56 JSTaggedValue globalValue, JSTaggedValue key,
57 JSTaggedValue value, uint32_t slotId, bool tryStore)
58 {
59 INTERPRETER_TRACE(thread, StoreGlobalICByName);
60 JSTaggedValue handler = profileTypeInfo->Get(thread, slotId);
61 if (handler.IsHeapObject()) {
62 auto result = StoreGlobal(thread, value, handler);
63 if (!result.IsHole()) {
64 return result;
65 }
66 }
67 ICKind kind = tryStore ? ICKind::NamedGlobalTryStoreIC : ICKind::NamedGlobalStoreIC;
68 return StoreMiss(thread, profileTypeInfo, globalValue, key, value, slotId, kind);
69 }
70
CheckPolyHClass(const JSThread * thread,JSTaggedValue cachedValue,JSHClass * hclass)71 JSTaggedValue ICRuntimeStub::CheckPolyHClass(const JSThread *thread, JSTaggedValue cachedValue, JSHClass *hclass)
72 {
73 if (!cachedValue.IsWeak()) {
74 ASSERT(cachedValue.IsTaggedArray());
75 TaggedArray *array = TaggedArray::Cast(cachedValue.GetTaggedObject());
76 uint32_t length = array->GetLength();
77 for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot
78 auto result = array->Get(thread, i);
79 if (!result.IsUndefined() && result.GetWeakReferent() == hclass) {
80 return array->Get(thread, i + 1);
81 }
82 }
83 }
84 return JSTaggedValue::Hole();
85 }
86
TryLoadICByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue firstValue,JSTaggedValue secondValue)87 ARK_INLINE JSTaggedValue ICRuntimeStub::TryLoadICByName(JSThread *thread, JSTaggedValue receiver,
88 JSTaggedValue firstValue, JSTaggedValue secondValue)
89 {
90 INTERPRETER_TRACE(thread, TryLoadICByName);
91 if (LIKELY(receiver.IsHeapObject())) {
92 auto hclass = receiver.GetTaggedObject()->GetClass();
93 if (LIKELY(firstValue.GetWeakReferentUnChecked() == hclass)) {
94 return LoadICWithHandler(thread, receiver, receiver, secondValue);
95 }
96 JSTaggedValue cachedHandler = CheckPolyHClass(thread, firstValue, hclass);
97 if (!cachedHandler.IsHole()) {
98 return LoadICWithHandler(thread, receiver, receiver, cachedHandler);
99 }
100 } else if (receiver.IsNumber()) {
101 JSHandle<JSFunction> function(thread->GetEcmaVM()->GetGlobalEnv()->GetNumberFunction());
102 auto hclass = reinterpret_cast<JSHClass *>(function->GetProtoOrHClass(thread).GetTaggedObject());
103 if (firstValue.GetWeakReferentUnChecked() == hclass) {
104 return LoadICWithHandler(thread, receiver, receiver, secondValue);
105 }
106 }
107 return JSTaggedValue::Hole();
108 }
109
LoadICByName(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,uint32_t slotId)110 ARK_NOINLINE JSTaggedValue ICRuntimeStub::LoadICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
111 JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId)
112 {
113 INTERPRETER_TRACE(thread, LoadICByName);
114 return LoadMiss(thread, profileTypeInfo, receiver, key, slotId, ICKind::NamedLoadIC);
115 }
116
TryLoadICByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue firstValue,JSTaggedValue secondValue)117 ARK_INLINE JSTaggedValue ICRuntimeStub::TryLoadICByValue(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key,
118 JSTaggedValue firstValue, JSTaggedValue secondValue)
119 {
120 INTERPRETER_TRACE(thread, TryLoadICByValue);
121 if (receiver.IsHeapObject()) {
122 auto hclass = receiver.GetTaggedObject()->GetClass();
123 if (firstValue.GetWeakReferentUnChecked() == hclass) {
124 if (HandlerBase::IsNormalElement(secondValue.GetNumber())) {
125 return LoadElement(thread, JSObject::Cast(receiver.GetTaggedObject()), key);
126 } else if (HandlerBase::IsTypedArrayElement(secondValue.GetNumber())) {
127 return LoadTypedArrayElement(thread, receiver, key);
128 }
129 ASSERT(HandlerBase::IsStringElement(secondValue.GetNumber()));
130 return LoadStringElement(thread, receiver, key);
131 }
132 // check ploy
133 if (secondValue.IsHole() && !firstValue.IsHole()) {
134 JSTaggedValue cachedHandler = CheckPolyHClass(thread, firstValue, hclass);
135 return LoadICWithElementHandler(thread, receiver, cachedHandler, key);
136 }
137 // Check key
138 if (firstValue == key) {
139 JSTaggedValue cachedHandler = CheckPolyHClass(thread, secondValue, hclass);
140 if (!cachedHandler.IsHole()) {
141 return LoadICWithHandler(thread, receiver, receiver, cachedHandler);
142 }
143 }
144 }
145 return JSTaggedValue::Hole();
146 }
147
LoadICByValue(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,uint32_t slotId)148 ARK_NOINLINE JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
149 JSTaggedValue receiver, JSTaggedValue key, uint32_t slotId)
150 {
151 INTERPRETER_TRACE(thread, LoadICByValue);
152 return LoadValueMiss(thread, profileTypeInfo, receiver, key, slotId, ICKind::LoadIC);
153 }
154
TryStoreICByValue(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue firstValue,JSTaggedValue secondValue,JSTaggedValue value)155 ARK_INLINE JSTaggedValue ICRuntimeStub::TryStoreICByValue(JSThread *thread, JSTaggedValue receiver,
156 JSTaggedValue key, JSTaggedValue firstValue,
157 JSTaggedValue secondValue, JSTaggedValue value)
158 {
159 INTERPRETER_TRACE(thread, TryStoreICByValue);
160 if (receiver.IsHeapObject()) {
161 auto hclass = receiver.GetTaggedObject()->GetClass();
162 if (firstValue.GetWeakReferentUnChecked() == hclass) {
163 return StoreElement(thread, JSObject::Cast(receiver.GetTaggedObject()), key, value, secondValue);
164 }
165 // Check key
166 if (firstValue == key) {
167 JSTaggedValue cachedHandler = CheckPolyHClass(thread, secondValue, hclass);
168 if (!cachedHandler.IsHole()) {
169 return StoreICWithHandler(thread, receiver, receiver, value, cachedHandler);
170 }
171 }
172 }
173
174 return JSTaggedValue::Hole();
175 }
176
StoreOwnICByValue(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,uint32_t slotId)177 ARK_NOINLINE JSTaggedValue ICRuntimeStub::StoreOwnICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
178 JSTaggedValue receiver, JSTaggedValue key,
179 JSTaggedValue value, uint32_t slotId)
180 {
181 INTERPRETER_TRACE(thread, StoreOwnICByValue);
182 return StoreMiss(thread, profileTypeInfo, receiver, key, value, slotId, ICKind::StoreIC, true);
183 }
184
StoreICByValue(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,uint32_t slotId)185 ARK_NOINLINE JSTaggedValue ICRuntimeStub::StoreICByValue(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
186 JSTaggedValue receiver, JSTaggedValue key,
187 JSTaggedValue value, uint32_t slotId)
188 {
189 INTERPRETER_TRACE(thread, StoreICByValue);
190 return StoreMiss(thread, profileTypeInfo, receiver, key, value, slotId, ICKind::StoreIC);
191 }
192
TryStoreICByName(JSThread * thread,JSTaggedValue receiver,JSTaggedValue firstValue,JSTaggedValue secondValue,JSTaggedValue value)193 ARK_INLINE JSTaggedValue ICRuntimeStub::TryStoreICByName(JSThread *thread, JSTaggedValue receiver,
194 JSTaggedValue firstValue, JSTaggedValue secondValue,
195 JSTaggedValue value)
196 {
197 INTERPRETER_TRACE(thread, TryStoreICByName);
198 if (receiver.IsHeapObject()) {
199 auto hclass = receiver.GetTaggedObject()->GetClass();
200 if (firstValue.GetWeakReferentUnChecked() == hclass) {
201 return StoreICWithHandler(thread, receiver, receiver, value, secondValue);
202 }
203 JSTaggedValue cachedHandler = CheckPolyHClass(thread, firstValue, hclass);
204 if (!cachedHandler.IsHole()) {
205 return StoreICWithHandler(thread, receiver, receiver, value, cachedHandler);
206 }
207 }
208 return JSTaggedValue::Hole();
209 }
210
StoreICByName(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,uint32_t slotId)211 ARK_NOINLINE JSTaggedValue ICRuntimeStub::StoreICByName(JSThread *thread, ProfileTypeInfo *profileTypeInfo,
212 JSTaggedValue receiver, JSTaggedValue key,
213 JSTaggedValue value, uint32_t slotId)
214 {
215 INTERPRETER_TRACE(thread, StoreICByName);
216 return StoreMiss(thread, profileTypeInfo, receiver, key, value, slotId, ICKind::NamedStoreIC);
217 }
218
StoreICWithHandler(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue value,JSTaggedValue handler)219 ARK_INLINE JSTaggedValue ICRuntimeStub::StoreICWithHandler(JSThread *thread, JSTaggedValue receiver,
220 JSTaggedValue holder,
221 JSTaggedValue value, JSTaggedValue handler)
222 {
223 INTERPRETER_TRACE(thread, StoreICWithHandler);
224 if (handler.IsInt()) {
225 auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler);
226 HandlerBase::PrintStoreHandler(handlerInfo, std::cout);
227 if (HandlerBase::IsNonSharedStoreField(handlerInfo)) {
228 StoreField(thread, JSObject::Cast(receiver.GetTaggedObject()), value, handlerInfo);
229 return JSTaggedValue::Undefined();
230 }
231 bool isShared = HandlerBase::IsStoreShared(handlerInfo);
232 if (isShared) {
233 SharedFieldType fieldType { HandlerBase::GetFieldType(handlerInfo) };
234 bool hasAccessor = HandlerBase::IsAccessor(handlerInfo);
235 if (!hasAccessor) {
236 if (!ClassHelper::MatchFieldType(fieldType, value)) {
237 [[maybe_unused]] EcmaHandleScope handleScope(thread);
238 THROW_TYPE_ERROR_AND_RETURN((thread), GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty),
239 JSTaggedValue::Exception());
240 }
241 if (value.IsTreeString()) {
242 [[maybe_unused]] EcmaHandleScope handleScope(thread);
243 JSHandle<JSTaggedValue> objHandle(thread, receiver);
244 JSHandle<JSTaggedValue> holderHandle(thread, holder);
245 JSHandle<JSTaggedValue> valueHandle(thread, value);
246 value = JSTaggedValue::PublishSharedValue(thread, valueHandle).GetTaggedValue();
247 receiver = objHandle.GetTaggedValue();
248 holder = holderHandle.GetTaggedValue();
249 }
250 }
251 HandlerBase::ClearSharedStoreKind(handlerInfo);
252 return StoreICWithHandler(thread, receiver, holder, value,
253 JSTaggedValue::WrapUint64(handlerInfo));
254 }
255 ASSERT(HandlerBase::IsAccessor(handlerInfo));
256 auto accessor = LoadFromField(thread, JSObject::Cast(holder.GetTaggedObject()), handlerInfo);
257 return FastRuntimeStub::CallSetter(thread, JSTaggedValue(receiver), value, accessor);
258 }
259 if (handler.IsTransitionHandler()) {
260 StoreWithTransition(thread, JSObject::Cast(receiver.GetTaggedObject()), value, handler);
261 return JSTaggedValue::Undefined();
262 }
263 if (handler.IsTransWithProtoHandler()) {
264 return StoreTransWithProto(thread, JSObject::Cast(receiver.GetTaggedObject()), value, handler);
265 }
266 if (handler.IsPrototypeHandler()) {
267 return StorePrototype(thread, receiver, value, handler);
268 }
269 if (handler.IsPropertyBox()) {
270 return StoreGlobal(thread, value, handler);
271 }
272 if (handler.IsStoreAOTHandler()) {
273 return StoreWithTS(thread, receiver, value, handler);
274 }
275 return JSTaggedValue::Undefined();
276 }
277
StorePrototype(JSThread * thread,JSTaggedValue receiver,JSTaggedValue value,JSTaggedValue handler)278 JSTaggedValue ICRuntimeStub::StorePrototype(JSThread *thread, JSTaggedValue receiver,
279 JSTaggedValue value, JSTaggedValue handler)
280 {
281 INTERPRETER_TRACE(thread, StorePrototype);
282 ASSERT(handler.IsPrototypeHandler());
283 PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject());
284 if (!receiver.IsJSShared()) {
285 auto cellValue = prototypeHandler->GetProtoCell(thread);
286 if (cellValue.IsNull()) {
287 return JSTaggedValue::Hole();
288 }
289 ASSERT(cellValue.IsProtoChangeMarker());
290 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
291 if (cell->GetHasChanged()) {
292 return JSTaggedValue::Hole();
293 }
294 }
295 auto holder = prototypeHandler->GetHolder(thread);
296 JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(thread);
297 return StoreICWithHandler(thread, receiver, holder, value, handlerInfo);
298 }
299
StoreWithTS(JSThread * thread,JSTaggedValue receiver,JSTaggedValue value,JSTaggedValue handler)300 JSTaggedValue ICRuntimeStub::StoreWithTS(JSThread *thread, JSTaggedValue receiver,
301 JSTaggedValue value, JSTaggedValue handler)
302 {
303 INTERPRETER_TRACE(thread, StoreWithAOT);
304 ASSERT(handler.IsStoreAOTHandler());
305 StoreAOTHandler *storeAOTHandler = StoreAOTHandler::Cast(handler.GetTaggedObject());
306 auto cellValue = storeAOTHandler->GetProtoCell(thread);
307 if (cellValue == JSTaggedValue::Undefined()) {
308 return JSTaggedValue::Hole();
309 }
310 ASSERT(cellValue.IsProtoChangeMarker());
311 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
312 if (cell->GetHasChanged()) {
313 return JSTaggedValue::Hole();
314 }
315 auto holder = storeAOTHandler->GetHolder(thread);
316 JSTaggedValue handlerInfo = storeAOTHandler->GetHandlerInfo(thread);
317 auto handlerInfoInt = JSTaggedValue::UnwrapToUint64(handlerInfo);
318 if (HandlerBase::IsField(handlerInfoInt)) {
319 StoreField(thread, JSObject::Cast(receiver.GetTaggedObject()), value, handlerInfoInt);
320 return JSTaggedValue::Undefined();
321 }
322 ASSERT(HandlerBase::IsAccessor(handlerInfoInt));
323 auto accessor = LoadFromField(thread, JSObject::Cast(holder.GetTaggedObject()), handlerInfoInt);
324 return FastRuntimeStub::CallSetter(thread, JSTaggedValue(receiver), value, accessor);
325 }
326
StoreWithTransition(JSThread * thread,JSObject * receiver,JSTaggedValue value,JSTaggedValue handler,bool withPrototype)327 void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value,
328 JSTaggedValue handler, bool withPrototype)
329 {
330 INTERPRETER_TRACE(thread, StoreWithTransition);
331
332 JSHClass *newHClass = nullptr;
333 uint64_t handlerInfo = 0;
334
335 if (withPrototype) {
336 TransWithProtoHandler *transWithProtoHandler = TransWithProtoHandler::Cast(handler.GetTaggedObject());
337 newHClass = JSHClass::Cast(transWithProtoHandler->GetTransitionHClass(thread).GetTaggedObject());
338 handlerInfo = JSTaggedValue::UnwrapToUint64(transWithProtoHandler->GetHandlerInfo(thread));
339 } else {
340 TransitionHandler *transitionHandler = TransitionHandler::Cast(handler.GetTaggedObject());
341 newHClass = JSHClass::Cast(transitionHandler->GetTransitionHClass(thread).GetTaggedObject());
342 handlerInfo = JSTaggedValue::UnwrapToUint64(transitionHandler->GetHandlerInfo(thread));
343 }
344 JSHandle<JSHClass> newHClassHandle(thread, newHClass);
345 JSHandle<JSHClass> oldHClassHandle(thread, receiver->GetJSHClass());
346 if (newHClassHandle->IsPrototype()) {
347 newHClassHandle->SetProtoChangeDetails(thread, oldHClassHandle->GetProtoChangeDetails(thread));
348 }
349 JSHandle<JSObject> objHandle(thread, receiver);
350 ElementsKind oldKind = receiver->GetJSHClass()->GetElementsKind();
351 JSHClass::RestoreElementsKindToGeneric(newHClass);
352 objHandle->SynchronizedTransitionClass(thread, newHClass);
353 JSObject::TryMigrateToGenericKindForJSObject(thread, objHandle, oldKind);
354
355 ASSERT(HandlerBase::IsField(handlerInfo));
356
357 if (!HandlerBase::IsInlinedProps(handlerInfo)) {
358 TaggedArray *array = TaggedArray::Cast(objHandle->GetProperties(thread).GetTaggedObject());
359 int capacity = static_cast<int>(array->GetLength());
360 int index = HandlerBase::GetOffset(handlerInfo);
361 if (index >= capacity) {
362 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
363 [[maybe_unused]] EcmaHandleScope handleScope(thread);
364 JSHandle<TaggedArray> properties;
365 JSHandle<JSTaggedValue> valueHandle(thread, value);
366 if (capacity == 0) {
367 capacity = JSObject::MIN_PROPERTIES_LENGTH;
368 properties = factory->NewTaggedArray(capacity);
369 } else {
370 auto arrayHandle = JSHandle<TaggedArray>(thread, array);
371 uint32_t maxNonInlinedFastPropsCapacity = objHandle->GetNonInlinedFastPropsCapacity();
372 uint32_t newLen = JSObject::ComputeNonInlinedFastPropsCapacity(thread, capacity,
373 maxNonInlinedFastPropsCapacity);
374 properties = factory->CopyArray(arrayHandle, capacity, newLen);
375 }
376 properties->Set(thread, index, valueHandle);
377 objHandle->SetProperties(thread, properties);
378 return;
379 }
380 array->Set(thread, index, value);
381 return;
382 }
383 StoreField(thread, *objHandle, value, handlerInfo);
384 }
385
StoreTransWithProto(JSThread * thread,JSObject * receiver,JSTaggedValue value,JSTaggedValue handler)386 JSTaggedValue ICRuntimeStub::StoreTransWithProto(JSThread *thread, JSObject *receiver, JSTaggedValue value,
387 JSTaggedValue handler)
388 {
389 INTERPRETER_TRACE(thread, StoreTransWithProto);
390 ASSERT(handler.IsTransWithProtoHandler());
391 ASSERT(!receiver->GetClass()->IsJSShared());
392 TransWithProtoHandler *transWithProtoHandler = TransWithProtoHandler::Cast(handler.GetTaggedObject());
393 auto cellValue = transWithProtoHandler->GetProtoCell(thread);
394 if (cellValue == JSTaggedValue::Undefined()) {
395 return JSTaggedValue::Hole();
396 }
397 ASSERT(cellValue.IsProtoChangeMarker());
398 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
399 if (cell->GetHasChanged()) {
400 return JSTaggedValue::Hole();
401 }
402
403 StoreWithTransition(thread, receiver, value, handler, true);
404 return JSTaggedValue::Undefined();
405 }
406
StoreField(JSThread * thread,JSObject * receiver,JSTaggedValue value,uint64_t handler)407 ARK_INLINE void ICRuntimeStub::StoreField(JSThread *thread, JSObject *receiver, JSTaggedValue value, uint64_t handler)
408 {
409 INTERPRETER_TRACE(thread, StoreField);
410 int index = HandlerBase::GetOffset(handler);
411 if (HandlerBase::IsInlinedProps(handler)) {
412 SET_VALUE_WITH_BARRIER(thread, receiver, static_cast<uint32_t>(index) * JSTaggedValue::TaggedTypeSize(), value);
413 return;
414 }
415 TaggedArray *array = TaggedArray::Cast(receiver->GetProperties(thread).GetTaggedObject());
416 ASSERT(index < static_cast<int>(array->GetLength()));
417 array->Set(thread, index, value);
418 }
419
LoadFromField(const JSThread * thread,JSObject * receiver,uint64_t handlerInfo)420 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadFromField(const JSThread *thread, JSObject *receiver, uint64_t handlerInfo)
421 {
422 int index = HandlerBase::GetOffset(handlerInfo);
423 if (HandlerBase::IsInlinedProps(handlerInfo)) {
424 return JSTaggedValue(GET_VALUE(thread, receiver, static_cast<size_t>(index) * JSTaggedValue::TaggedTypeSize()));
425 }
426 return TaggedArray::Cast(receiver->GetProperties(thread).GetTaggedObject())->Get(thread, index);
427 }
428
LoadGlobal(const JSThread * thread,JSTaggedValue handler)429 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadGlobal(const JSThread *thread, JSTaggedValue handler)
430 {
431 ASSERT(handler.IsPropertyBox());
432 PropertyBox *cell = PropertyBox::Cast(handler.GetTaggedObject());
433 if (cell->IsInvalid(thread) || cell->GetValue(thread).IsAccessorData()) {
434 return JSTaggedValue::Hole();
435 }
436 JSTaggedValue ret = cell->GetValue(thread);
437 ASSERT(!ret.IsAccessorData());
438 return ret;
439 }
440
StoreGlobal(JSThread * thread,JSTaggedValue value,JSTaggedValue handler)441 ARK_INLINE JSTaggedValue ICRuntimeStub::StoreGlobal(JSThread *thread, JSTaggedValue value, JSTaggedValue handler)
442 {
443 INTERPRETER_TRACE(thread, StoreGlobal);
444 ASSERT(handler.IsPropertyBox());
445 PropertyBox *cell = PropertyBox::Cast(handler.GetTaggedObject());
446 if (cell->IsInvalid(thread) || cell->GetValue(thread).IsAccessorData()) {
447 return JSTaggedValue::Hole();
448 }
449 ASSERT(!cell->GetValue(thread).IsAccessorData());
450 cell->SetValue(thread, value);
451 return JSTaggedValue::Undefined();
452 }
453
LoadPrototype(JSThread * thread,JSTaggedValue receiver,JSTaggedValue handler)454 JSTaggedValue ICRuntimeStub::LoadPrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler)
455 {
456 INTERPRETER_TRACE(thread, LoadPrototype);
457 ASSERT(handler.IsPrototypeHandler());
458 PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject());
459 auto holder = prototypeHandler->GetHolder(thread);
460 if (!receiver.IsJSShared()) {
461 auto cellValue = prototypeHandler->GetProtoCell(thread);
462 if (cellValue == JSTaggedValue::Undefined()) {
463 return JSTaggedValue::Hole();
464 }
465 ASSERT(cellValue.IsProtoChangeMarker());
466 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
467 if (cell->GetHasChanged()) {
468 return JSTaggedValue::Hole();
469 }
470
471 // For "Not Found" case (holder equals Undefined()),
472 // we should ensure that both GetNotFoundHasChanged() and GetHasChanged() return false.
473 if (holder == JSTaggedValue::Undefined()) {
474 if (cell->GetNotFoundHasChanged()) {
475 return JSTaggedValue::Hole();
476 }
477 return JSTaggedValue::Undefined();
478 }
479 }
480
481 JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo(thread);
482 return LoadICWithHandler(thread, receiver, holder, handlerInfo);
483 }
484
LoadICWithHandler(JSThread * thread,JSTaggedValue receiver,JSTaggedValue holder,JSTaggedValue handler)485 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTaggedValue receiver,
486 JSTaggedValue holder, JSTaggedValue handler)
487 {
488 INTERPRETER_TRACE(thread, LoadICWithHandler);
489 if (LIKELY(handler.IsInt())) {
490 auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler);
491 HandlerBase::PrintLoadHandler(handlerInfo, std::cout);
492 if (LIKELY(HandlerBase::IsField(handlerInfo))) {
493 return LoadFromField(thread, JSObject::Cast(holder.GetTaggedObject()), handlerInfo);
494 }
495 if (HandlerBase::IsSupportedPrimitiveTypeICHandler(handlerInfo)) {
496 return LoadFromField(thread, JSObject::Cast(holder.GetTaggedObject()), handlerInfo);
497 }
498
499 // For the special "Not Found" case we may generate ic by "LoadHandler::LoadProperty".
500 // In this situation, you can trust ic without ChangeMarker.
501 if (HandlerBase::IsNonExist(handlerInfo)) {
502 return JSTaggedValue::Undefined();
503 }
504
505 if (HandlerBase::IsStringLength(handlerInfo)) {
506 return JSTaggedNumber((EcmaStringAccessor(EcmaString::Cast(holder)).GetLength()));
507 }
508 ASSERT(HandlerBase::IsAccessor(handlerInfo));
509 auto accessor = LoadFromField(thread, JSObject::Cast(holder.GetTaggedObject()), handlerInfo);
510 return FastRuntimeStub::CallGetter(thread, receiver, holder, accessor);
511 }
512
513 if (handler.IsPrototypeHandler()) {
514 return LoadPrototype(thread, receiver, handler);
515 }
516
517 return LoadGlobal(thread, handler);
518 }
519
LoadICWithElementHandler(JSThread * thread,JSTaggedValue receiver,JSTaggedValue handler,JSTaggedValue key)520 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithElementHandler(JSThread *thread, JSTaggedValue receiver,
521 JSTaggedValue handler, JSTaggedValue key)
522 {
523 if (LIKELY(handler.IsInt())) {
524 auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler);
525 HandlerBase::PrintLoadHandler(handlerInfo, std::cout);
526 if (HandlerBase::IsNormalElement(handlerInfo)) {
527 return LoadElement(thread, JSObject::Cast(receiver.GetTaggedObject()), key);
528 } else if (HandlerBase::IsTypedArrayElement(handlerInfo)) {
529 return LoadTypedArrayElement(thread, receiver, key);
530 }
531 ASSERT(HandlerBase::IsStringElement(handlerInfo));
532 return LoadStringElement(thread, receiver, key);
533 }
534 return JSTaggedValue::Hole();
535 }
536
LoadElement(JSThread * thread,JSObject * receiver,JSTaggedValue key)537 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadElement(JSThread *thread, JSObject *receiver, JSTaggedValue key)
538 {
539 auto index = TryToElementsIndex(thread, key);
540 if (index < 0) {
541 return JSTaggedValue::Hole();
542 }
543 uint32_t elementIndex = static_cast<uint32_t>(index);
544 if (ElementAccessor::GetElementsLength(thread, receiver) <= elementIndex) {
545 return JSTaggedValue::Hole();
546 }
547
548 JSTaggedValue value = ElementAccessor::Get(thread, receiver, elementIndex);
549 // TaggedArray elements
550 return value;
551 }
552
LoadStringElement(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)553 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadStringElement(JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
554 {
555 auto index = TryToElementsIndex(thread, key);
556 if (index < 0) {
557 return JSTaggedValue::Hole();
558 }
559 uint32_t elementIndex = static_cast<uint32_t>(index);
560 uint16_t tmpChar = 0;
561 {
562 JSHandle<EcmaString> strHandle(thread, receiver);
563 JSHandle<EcmaString> strFlat(thread, EcmaStringAccessor::Flatten(thread->GetEcmaVM(), strHandle));
564 if (EcmaStringAccessor(strFlat).GetLength() <= elementIndex) {
565 return JSTaggedValue::Hole();
566 }
567 tmpChar = EcmaStringAccessor(strFlat).Get(thread, elementIndex);
568 }
569 auto factory = thread->GetEcmaVM()->GetFactory();
570 JSHandle<JSTaggedValue> value(factory->NewFromUtf16(&tmpChar, 1));
571 return value.GetTaggedValue();
572 }
573
LoadTypedArrayElement(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key)574 ARK_INLINE JSTaggedValue ICRuntimeStub::LoadTypedArrayElement(JSThread *thread, JSTaggedValue receiver,
575 JSTaggedValue key)
576 {
577 auto index = TryToElementsIndex(thread, key);
578 if (index < 0) {
579 return JSTaggedValue::Hole();
580 }
581 auto typedarrayObj = JSTypedArray::Cast(receiver.GetTaggedObject());
582 uint32_t arrLen = typedarrayObj->GetArrayLength();
583 if (index >= arrLen) {
584 return JSTaggedValue::Hole();
585 }
586 JSType type = typedarrayObj->GetJSHClass()->GetObjectType();
587 return JSTypedArray::FastGetPropertyByIndex(thread, receiver, index, type);
588 }
589
590 template <typename T>
StoreElementWithProtoHandler(JSThread * thread,JSObject * receiver,JSTaggedValue key,JSTaggedValue value,JSTaggedValue handler)591 ARK_INLINE JSTaggedValue ICRuntimeStub::StoreElementWithProtoHandler(JSThread *thread,
592 JSObject *receiver,
593 JSTaggedValue key,
594 JSTaggedValue value,
595 JSTaggedValue handler)
596 {
597 if (receiver->GetClass()->IsJSShared()) {
598 THROW_TYPE_ERROR_AND_RETURN(thread,
599 GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), JSTaggedValue::Exception());
600 }
601 T *protoHandler = T::Cast(handler.GetTaggedObject());
602 auto cellValue = protoHandler->GetProtoCell(thread);
603 if (cellValue == JSTaggedValue::Undefined() || cellValue == JSTaggedValue::Null()) {
604 return JSTaggedValue::Hole();
605 }
606 ASSERT(cellValue.IsProtoChangeMarker());
607 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
608 if (cell->GetHasChanged()) {
609 return JSTaggedValue::Hole();
610 }
611 JSTaggedValue handlerInfo = protoHandler->GetHandlerInfo(thread);
612 return StoreElement(thread, receiver, key, value, handlerInfo);
613 }
614
StoreElement(JSThread * thread,JSObject * receiver,JSTaggedValue key,JSTaggedValue value,JSTaggedValue handler)615 JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key,
616 JSTaggedValue value, JSTaggedValue handler)
617 {
618 INTERPRETER_TRACE(thread, StoreElement);
619 auto index = TryToElementsIndex(thread, key);
620 if (index < 0) {
621 return JSTaggedValue::Hole();
622 }
623 uint32_t elementIndex = static_cast<uint32_t>(index);
624 if (handler.IsInt()) {
625 auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler);
626 HandlerBase::PrintStoreHandler(handlerInfo, std::cout);
627 [[maybe_unused]] EcmaHandleScope handleScope(thread);
628 JSHandle<JSObject> receiverHandle(thread, receiver);
629 JSHandle<JSTaggedValue> valueHandle(thread, value);
630 if (HandlerBase::IsTypedArrayElement(handlerInfo)) {
631 return StoreTypedArrayElement(thread, JSTaggedValue::Cast(receiver), key, value);
632 } else if (HandlerBase::IsJSArray(handlerInfo)) {
633 JSTaggedValue receiveValue = receiverHandle.GetTaggedValue();
634 if (receiveValue.IsJSCOWArray(thread)) {
635 // Copy on write array.
636 JSArray::CheckAndCopyArray(thread, JSHandle<JSArray>::Cast(receiverHandle));
637 }
638 JSArray *arr = JSArray::Cast(*receiverHandle);
639 uint32_t oldLength = arr->GetArrayLength();
640 if (elementIndex >= oldLength) {
641 arr->SetArrayLength(thread, elementIndex + 1);
642 }
643 }
644 TaggedArray *elements = TaggedArray::Cast(receiverHandle->GetElements(thread).GetTaggedObject());
645 uint32_t capacity = elements->GetLength();
646 if (elementIndex >= capacity) {
647 if (JSObject::ShouldTransToDict(capacity, elementIndex)) {
648 return JSTaggedValue::Hole();
649 }
650 elements = *JSObject::GrowElementsCapacity(thread, receiverHandle, elementIndex + 1);
651 receiverHandle->SetElements(thread, JSTaggedValue(elements));
652 elements->Set(thread, elementIndex, valueHandle);
653 return JSTaggedValue::Undefined();
654 }
655 elements->Set(thread, elementIndex, valueHandle);
656 } else {
657 if (handler.IsPrototypeHandler()) {
658 return StoreElementWithProtoHandler<PrototypeHandler>(thread, receiver, key, value, handler);
659 } else if (handler.IsTransWithProtoHandler()) {
660 return StoreElementWithProtoHandler<TransWithProtoHandler>(thread, receiver, key, value, handler);
661 } else {
662 ASSERT(handler.IsTransitionHandler());
663 if (receiver->GetClass()->IsJSShared()) {
664 THROW_TYPE_ERROR_AND_RETURN(thread,
665 GET_MESSAGE_STRING(SetTypeMismatchedSharedProperty), JSTaggedValue::Exception());
666 }
667 TransitionHandler *transitionHandler = TransitionHandler::Cast(handler.GetTaggedObject());
668 JSTaggedValue handlerInfo = transitionHandler->GetHandlerInfo(thread);
669 return StoreElement(thread, receiver, key, value, handlerInfo);
670 }
671 }
672 return JSTaggedValue::Undefined();
673 }
674
StoreTypedArrayElement(JSThread * thread,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value)675 ARK_INLINE JSTaggedValue ICRuntimeStub::StoreTypedArrayElement(JSThread *thread, JSTaggedValue receiver,
676 JSTaggedValue key, JSTaggedValue value)
677 {
678 auto index = TryToElementsIndex(thread, key);
679 if (index < 0) {
680 return JSTaggedValue::Hole();
681 }
682 auto typedarrayObj = JSTypedArray::Cast(receiver.GetTaggedObject());
683 uint32_t arrLen = typedarrayObj->GetArrayLength();
684 if (index >= arrLen) {
685 return JSTaggedValue::Hole();
686 }
687 JSType type = typedarrayObj->GetJSHClass()->GetObjectType();
688 return JSTypedArray::FastSetPropertyByIndex(thread, receiver, index, value, type);
689 }
690
TryToElementsIndex(JSThread * thread,JSTaggedValue key)691 ARK_INLINE int64_t ICRuntimeStub::TryToElementsIndex(JSThread *thread, JSTaggedValue key)
692 {
693 if (LIKELY(key.IsInt())) {
694 return key.GetInt();
695 }
696
697 if (key.IsString()) {
698 uint32_t index = 0;
699 if (JSTaggedValue::StringToElementIndex(thread, key, &index)) {
700 return static_cast<int64_t>(index);
701 }
702 }
703
704 if (key.IsDouble()) {
705 double number = key.GetDouble();
706 auto integer = static_cast<int32_t>(number);
707 if (number == integer) {
708 return integer;
709 }
710 }
711
712 return -1;
713 }
714
LoadMiss(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,uint32_t slotId,ICKind kind)715 JSTaggedValue ICRuntimeStub::LoadMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver,
716 JSTaggedValue key, uint32_t slotId, ICKind kind)
717 {
718 [[maybe_unused]] EcmaHandleScope handleScope(thread);
719 auto keyHandle = JSHandle<JSTaggedValue>(thread, key);
720 auto receiverHandle = JSHandle<JSTaggedValue>(thread, receiver);
721 auto profileInfoHandle = JSHandle<JSTaggedValue>(thread, profileTypeInfo);
722 LoadICRuntime icRuntime(thread, JSHandle<ProfileTypeInfo>::Cast(profileInfoHandle), slotId, kind);
723 return icRuntime.LoadMiss(receiverHandle, keyHandle);
724 }
725
LoadValueMiss(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,uint32_t slotId,ICKind kind)726 JSTaggedValue ICRuntimeStub::LoadValueMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver,
727 JSTaggedValue key, uint32_t slotId, ICKind kind)
728 {
729 [[maybe_unused]] EcmaHandleScope handleScope(thread);
730 auto keyHandle = JSHandle<JSTaggedValue>(thread, key);
731 auto receiverHandle = JSHandle<JSTaggedValue>(thread, receiver);
732 auto profileInfoHandle = JSHandle<JSTaggedValue>(thread, profileTypeInfo);
733 LoadICRuntime icRuntime(thread, JSHandle<ProfileTypeInfo>::Cast(profileInfoHandle), slotId, kind);
734 return icRuntime.LoadValueMiss(receiverHandle, keyHandle);
735 }
736
StoreMiss(JSThread * thread,ProfileTypeInfo * profileTypeInfo,JSTaggedValue receiver,JSTaggedValue key,JSTaggedValue value,uint32_t slotId,ICKind kind,bool isOwn)737 JSTaggedValue ICRuntimeStub::StoreMiss(JSThread *thread, ProfileTypeInfo *profileTypeInfo, JSTaggedValue receiver,
738 JSTaggedValue key, JSTaggedValue value, uint32_t slotId, ICKind kind, bool isOwn)
739 {
740 [[maybe_unused]] EcmaHandleScope handleScope(thread);
741 auto keyHandle = JSHandle<JSTaggedValue>(thread, key);
742 auto receiverHandle = JSHandle<JSTaggedValue>(thread, receiver);
743 auto valueHandle = JSHandle<JSTaggedValue>(thread, value);
744 auto profileInfoHandle = JSHandle<JSTaggedValue>(thread, profileTypeInfo);
745 StoreICRuntime icRuntime(thread, JSHandle<ProfileTypeInfo>::Cast(profileInfoHandle), slotId, kind);
746 return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle, isOwn);
747 }
748 } // namespace panda::ecmascript
749
750 #endif // ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H
751