1 /*
2 * Copyright (c) 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 #include "ecmascript/serializer/value_serializer.h"
17
18 #include "ecmascript/base/array_helper.h"
19 #include "ecmascript/js_serializer.h"
20 #include "ecmascript/shared_mm/shared_mm.h"
21
22 namespace panda::ecmascript {
23
CheckObjectCanSerialize(TaggedObject * object,bool & findSharedObject)24 bool ValueSerializer::CheckObjectCanSerialize(TaggedObject *object, bool &findSharedObject)
25 {
26 JSType type = object->GetClass()->GetObjectType();
27 if (IsInternalJSType(type)) {
28 return true;
29 }
30 switch (type) {
31 case JSType::JS_ERROR:
32 case JSType::JS_EVAL_ERROR:
33 case JSType::JS_RANGE_ERROR:
34 case JSType::JS_REFERENCE_ERROR:
35 case JSType::JS_TYPE_ERROR:
36 case JSType::JS_AGGREGATE_ERROR:
37 case JSType::JS_URI_ERROR:
38 case JSType::JS_SYNTAX_ERROR:
39 case JSType::JS_OOM_ERROR:
40 case JSType::JS_TERMINATION_ERROR:
41 case JSType::JS_DATE:
42 case JSType::JS_ARRAY:
43 case JSType::JS_MAP:
44 case JSType::JS_SET:
45 case JSType::JS_REG_EXP:
46 case JSType::JS_INT8_ARRAY:
47 case JSType::JS_UINT8_ARRAY:
48 case JSType::JS_UINT8_CLAMPED_ARRAY:
49 case JSType::JS_INT16_ARRAY:
50 case JSType::JS_UINT16_ARRAY:
51 case JSType::JS_INT32_ARRAY:
52 case JSType::JS_UINT32_ARRAY:
53 case JSType::JS_FLOAT32_ARRAY:
54 case JSType::JS_FLOAT64_ARRAY:
55 case JSType::JS_BIGINT64_ARRAY:
56 case JSType::JS_BIGUINT64_ARRAY:
57 case JSType::JS_ARRAY_BUFFER:
58 case JSType::JS_SHARED_ARRAY_BUFFER:
59 case JSType::LINE_STRING:
60 case JSType::CONSTANT_STRING:
61 case JSType::TREE_STRING:
62 case JSType::SLICED_STRING:
63 case JSType::JS_OBJECT:
64 case JSType::JS_ASYNC_FUNCTION: // means CONCURRENT_FUNCTION
65 return true;
66 case JSType::JS_SHARED_OBJECT: {
67 if (serializeSharedEvent_ > 0) {
68 return true;
69 }
70 if (defaultCloneShared_ || cloneSharedSet_.find(ToUintPtr(object)) != cloneSharedSet_.end()) {
71 findSharedObject = true;
72 serializeSharedEvent_++;
73 return true;
74 }
75 break;
76 }
77 case JSType::JS_SHARED_FUNCTION: {
78 if (serializeSharedEvent_ > 0) {
79 return true;
80 }
81 break;
82 }
83 default:
84 break;
85 }
86 LOG_ECMA(ERROR) << "Unsupport serialize object type: " << JSHClass::DumpJSType(type);
87 return false;
88 }
89
InitTransferSet(CUnorderedSet<uintptr_t> transferDataSet)90 void ValueSerializer::InitTransferSet(CUnorderedSet<uintptr_t> transferDataSet)
91 {
92 transferDataSet_ = std::move(transferDataSet);
93 }
94
WriteValue(JSThread * thread,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & transfer,const JSHandle<JSTaggedValue> & cloneList)95 bool ValueSerializer::WriteValue(JSThread *thread,
96 const JSHandle<JSTaggedValue> &value,
97 const JSHandle<JSTaggedValue> &transfer,
98 const JSHandle<JSTaggedValue> &cloneList)
99 {
100 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "ValueSerializer::WriteValue");
101 ASSERT(!value->IsWeak());
102 if (!defaultTransfer_ && !PrepareTransfer(thread, transfer)) {
103 LOG_ECMA(ERROR) << "ValueSerialize: PrepareTransfer fail";
104 data_->SetIncompleteData(true);
105 return false;
106 }
107 if (!defaultCloneShared_ && !PrepareClone(thread, cloneList)) {
108 LOG_ECMA(ERROR) << "ValueSerialize: PrepareClone fail";
109 data_->SetIncompleteData(true);
110 return false;
111 }
112 if (value->IsHeapObject()) {
113 // Add fast path for string
114 if (value->IsString()) {
115 vm_->GetSnapshotEnv()->InitializeStringClass();
116 } else {
117 vm_->GetSnapshotEnv()->Initialize();
118 }
119 }
120 SerializeJSTaggedValue(value.GetTaggedValue());
121 if (value->IsHeapObject()) {
122 vm_->GetSnapshotEnv()->ClearEnvMap();
123 }
124 if (notSupport_) {
125 LOG_ECMA(ERROR) << "ValueSerialize: serialize data is incomplete";
126 data_->SetIncompleteData(true);
127 return false;
128 }
129 size_t maxSerializerSize = vm_->GetEcmaParamConfiguration().GetMaxJSSerializerSize();
130 if (data_->Size() > maxSerializerSize) {
131 LOG_ECMA(ERROR) << "The serialization data size has exceed limit Size, current size is: " << data_->Size()
132 << " max size is: " << maxSerializerSize;
133 return false;
134 }
135 return true;
136 }
137
SerializeObjectImpl(TaggedObject * object,bool isWeak)138 void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak)
139 {
140 if (notSupport_) {
141 return;
142 }
143 bool cloneSharedObject = false;
144 if (!CheckObjectCanSerialize(object, cloneSharedObject)) {
145 notSupport_ = true;
146 return;
147 }
148 if (isWeak) {
149 data_->WriteEncodeFlag(EncodeFlag::WEAK);
150 }
151 if (SerializeReference(object) || SerializeRootObject(object)) {
152 return;
153 }
154 if (object->GetClass()->IsNativeBindingObject()) {
155 SerializeNativeBindingObject(object);
156 return;
157 }
158 if (object->GetClass()->IsJSError()) {
159 SerializeJSError(object);
160 return;
161 }
162 bool arrayBufferDeferDetach = false;
163 JSTaggedValue trackInfo;
164 JSTaggedType hashfield = JSTaggedValue::VALUE_ZERO;
165 JSType type = object->GetClass()->GetObjectType();
166 // serialize prologue
167 switch (type) {
168 case JSType::JS_ARRAY_BUFFER:
169 arrayBufferDeferDetach = SerializeJSArrayBufferPrologue(object);
170 break;
171 case JSType::JS_SHARED_ARRAY_BUFFER:
172 SerializeJSSharedArrayBufferPrologue(object);
173 break;
174 case JSType::METHOD:
175 SerializeMethodPrologue(reinterpret_cast<Method *>(object));
176 break;
177 case JSType::JS_ARRAY: {
178 JSArray *array = reinterpret_cast<JSArray *>(object);
179 trackInfo = array->GetTrackInfo();
180 array->SetTrackInfo(thread_, JSTaggedValue::Undefined());
181 break;
182 }
183 case JSType::TREE_STRING:
184 case JSType::SLICED_STRING:
185 object = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(object));
186 break;
187 case JSType::JS_REG_EXP:
188 SerializeJSRegExpPrologue(reinterpret_cast<JSRegExp *>(object));
189 break;
190 case JSType::JS_SHARED_FUNCTION: {
191 if (serializeSharedEvent_ > 0) {
192 data_->WriteEncodeFlag(EncodeFlag::JS_FUNCTION_IN_SHARED);
193 }
194 break;
195 }
196 case JSType::JS_OBJECT:
197 hashfield = Barriers::GetValue<JSTaggedType>(object, JSObject::HASH_OFFSET);
198 Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, JSTaggedValue::VALUE_ZERO);
199 break;
200 default:
201 break;
202 }
203
204 // serialize object here
205 SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(object);
206
207 // serialize epilogue
208 if (type == JSType::JS_ARRAY) {
209 JSArray *array = reinterpret_cast<JSArray *>(object);
210 array->SetTrackInfo(thread_, trackInfo);
211 } else if (type == JSType::JS_OBJECT) {
212 if (JSTaggedValue(hashfield).IsHeapObject()) {
213 Barriers::SetObject<true>(thread_, object, JSObject::HASH_OFFSET, hashfield);
214 } else {
215 Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, hashfield);
216 }
217 }
218 if (cloneSharedObject) {
219 serializeSharedEvent_--;
220 }
221 if (arrayBufferDeferDetach) {
222 ASSERT(object->GetClass()->IsArrayBuffer());
223 JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
224 arrayBuffer->Detach(thread_, arrayBuffer->GetWithNativeAreaAllocator());
225 }
226 }
227
SerializeJSError(TaggedObject * object)228 void ValueSerializer::SerializeJSError(TaggedObject *object)
229 {
230 [[maybe_unused]] EcmaHandleScope scope(thread_);
231 data_->WriteEncodeFlag(EncodeFlag::JS_ERROR);
232 JSType type = object->GetClass()->GetObjectType();
233 ASSERT(type >= JSType::JS_ERROR_FIRST && type <= JSType::JS_ERROR_LAST);
234 data_->WriteUint8(static_cast<uint8_t>(type));
235 auto globalConst = thread_->GlobalConstants();
236 JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
237 JSHandle<JSTaggedValue> msg =
238 JSObject::GetProperty(thread_, JSHandle<JSTaggedValue>(thread_, object), handleMsg).GetValue();
239 if (msg->IsString()) {
240 EcmaString *str = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(msg->GetTaggedObject()));
241 data_->WriteUint8(1); // 1: msg is string
242 SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(str);
243 } else {
244 data_->WriteUint8(0); // 0: msg is undefined
245 }
246 }
247
SerializeNativeBindingObject(TaggedObject * object)248 void ValueSerializer::SerializeNativeBindingObject(TaggedObject *object)
249 {
250 [[maybe_unused]] EcmaHandleScope scope(thread_);
251 JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
252 JSHandle<JSTaggedValue> nativeBindingSymbol = env->GetNativeBindingSymbol();
253 JSHandle<JSTaggedValue> nativeBindingValue =
254 JSObject::GetProperty(thread_, JSHandle<JSObject>(thread_, object), nativeBindingSymbol).GetRawValue();
255 if (!nativeBindingValue->IsJSNativePointer()) {
256 LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject nativeBindingValue is not JSNativePointer";
257 notSupport_ = true;
258 return;
259 }
260 auto info = reinterpret_cast<panda::JSNApi::NativeBindingInfo *>(
261 JSNativePointer::Cast(nativeBindingValue->GetTaggedObject())->GetExternalPointer());
262 if (info == nullptr) {
263 LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject NativeBindingInfo is nullptr";
264 notSupport_ = true;
265 return;
266 }
267 void *enginePointer = info->env;
268 void *objPointer = info->nativeValue;
269 void *hint = info->hint;
270 void *detachData = info->detachData;
271 void *attachData = info->attachData;
272 DetachFunc detachNative = reinterpret_cast<DetachFunc>(info->detachFunc);
273 if (detachNative == nullptr) {
274 LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject detachNative == nullptr";
275 notSupport_ = true;
276 return;
277 }
278 void *buffer = detachNative(enginePointer, objPointer, hint, detachData);
279 AttachFunc attachNative = reinterpret_cast<AttachFunc>(info->attachFunc);
280 data_->WriteEncodeFlag(EncodeFlag::NATIVE_BINDING_OBJECT);
281 data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachNative));
282 data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(buffer));
283 data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(hint));
284 data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachData));
285 }
286
SerializeJSArrayBufferPrologue(TaggedObject * object)287 bool ValueSerializer::SerializeJSArrayBufferPrologue(TaggedObject *object)
288 {
289 ASSERT(object->GetClass()->IsArrayBuffer());
290 JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
291 if (arrayBuffer->IsDetach()) {
292 LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached array buffer";
293 notSupport_ = true;
294 return false;
295 }
296 bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
297 bool clone = cloneArrayBufferSet_.find(ToUintPtr(object)) != cloneArrayBufferSet_.end();
298 size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
299 if (arrayLength > 0) {
300 if (transfer) {
301 if (clone) {
302 notSupport_ = true;
303 LOG_ECMA(ERROR) << "ValueSerialize: can't put arraybuffer in both transfer list and clone list";
304 return false;
305 }
306 data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
307 return true;
308 } else if (clone || !defaultTransfer_) {
309 data_->WriteEncodeFlag(EncodeFlag::ARRAY_BUFFER);
310 data_->WriteUint32(arrayLength);
311 JSNativePointer *np =
312 reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
313 data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), arrayLength);
314 return false;
315 } else {
316 data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
317 return true;
318 }
319 }
320 return false;
321 }
322
SerializeJSSharedArrayBufferPrologue(TaggedObject * object)323 void ValueSerializer::SerializeJSSharedArrayBufferPrologue(TaggedObject *object)
324 {
325 ASSERT(object->GetClass()->IsSharedArrayBuffer());
326 JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
327 bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
328 if (arrayBuffer->IsDetach() || transfer) {
329 LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached or transfer shared array buffer";
330 notSupport_ = true;
331 return;
332 }
333 size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
334 if (arrayLength > 0) {
335 JSNativePointer *np = reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
336 void *buffer = np->GetExternalPointer();
337 if (JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength)) {
338 LOG_ECMA(ERROR) << "ValueSerialize: can't find buffer form shared memory pool";
339 notSupport_ = true;
340 return;
341 }
342 }
343 }
344
SerializeMethodPrologue(Method * method)345 void ValueSerializer::SerializeMethodPrologue(Method *method)
346 {
347 JSTaggedValue constPoolVal = method->GetConstantPool();
348 if (!constPoolVal.IsHeapObject()) {
349 return;
350 }
351 ConstantPool *constPool = reinterpret_cast<ConstantPool *>(constPoolVal.GetTaggedObject());
352 const JSPandaFile *jsPandaFile = constPool->GetJSPandaFile();
353 data_->WriteEncodeFlag(EncodeFlag::METHOD);
354 data_->WriteJSTaggedType(method->GetLiteralInfo());
355 data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(jsPandaFile));
356 }
357
SerializeJSRegExpPrologue(JSRegExp * jsRegExp)358 void ValueSerializer::SerializeJSRegExpPrologue(JSRegExp *jsRegExp)
359 {
360 uint32_t bufferSize = jsRegExp->GetLength();
361 if (bufferSize == 0) {
362 LOG_ECMA(ERROR) << "ValueSerialize: JSRegExp buffer size is 0";
363 notSupport_ = true;
364 return;
365 }
366
367 data_->WriteEncodeFlag(EncodeFlag::JS_REG_EXP);
368 data_->WriteUint32(bufferSize);
369 JSNativePointer *np =
370 reinterpret_cast<JSNativePointer *>(jsRegExp->GetByteCodeBuffer().GetTaggedObject());
371 data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), bufferSize);
372 }
373
PrepareTransfer(JSThread * thread,const JSHandle<JSTaggedValue> & transfer)374 bool ValueSerializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
375 {
376 if (transfer->IsUndefined()) {
377 return true;
378 }
379 if (!transfer->IsJSArray()) {
380 return false;
381 }
382 int len = base::ArrayHelper::GetArrayLength(thread, transfer);
383 int k = 0;
384 while (k < len) {
385 bool exists = JSTaggedValue::HasProperty(thread, transfer, k);
386 if (exists) {
387 JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, transfer, k);
388 if (!element->IsArrayBuffer()) {
389 return false;
390 }
391 transferDataSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
392 }
393 k++;
394 }
395 return true;
396 }
397
PrepareClone(JSThread * thread,const JSHandle<JSTaggedValue> & cloneList)398 bool ValueSerializer::PrepareClone(JSThread *thread, const JSHandle<JSTaggedValue> &cloneList)
399 {
400 if (cloneList->IsUndefined()) {
401 return true;
402 }
403 if (!cloneList->IsJSArray()) {
404 return false;
405 }
406 int len = base::ArrayHelper::GetArrayLength(thread, cloneList);
407 int index = 0;
408 while (index < len) {
409 bool exists = JSTaggedValue::HasProperty(thread, cloneList, index);
410 if (exists) {
411 JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, cloneList, index);
412 if (element->IsArrayBuffer()) {
413 cloneArrayBufferSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
414 } else if (element->IsJSSharedObject()) {
415 cloneSharedSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
416 } else {
417 return false;
418 }
419 }
420 index++;
421 }
422 return true;
423 }
424 } // namespace panda::ecmascript
425
426