1 /*
2 * Copyright (c) 2021-2022 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/jspandafile/literal_data_extractor.h"
17
18 #include "ecmascript/base/string_helper.h"
19 #include "ecmascript/compiler/aot_file/aot_file_manager.h"
20 #include "ecmascript/ecma_string.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/js_thread.h"
23 #include "ecmascript/module/js_module_manager.h"
24 #include "ecmascript/patch/quick_fix_manager.h"
25 #include "ecmascript/tagged_array-inl.h"
26
27 namespace panda::ecmascript {
28 using LiteralTag = panda_file::LiteralTag;
29 using StringData = panda_file::StringData;
30 using LiteralDataAccessor = panda_file::LiteralDataAccessor;
31 using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;
32
ExtractObjectDatas(JSThread * thread,const JSPandaFile * jsPandaFile,size_t index,JSMutableHandle<TaggedArray> elements,JSMutableHandle<TaggedArray> properties,JSHandle<ConstantPool> constpool,const CString & entryPoint)33 void LiteralDataExtractor::ExtractObjectDatas(JSThread *thread, const JSPandaFile *jsPandaFile, size_t index,
34 JSMutableHandle<TaggedArray> elements,
35 JSMutableHandle<TaggedArray> properties,
36 JSHandle<ConstantPool> constpool,
37 const CString &entryPoint)
38 {
39 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
40 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
41 uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half
42 elements.Update(factory->NewOldSpaceTaggedArray(num).GetTaggedValue());
43 properties.Update(factory->NewOldSpaceTaggedArray(num).GetTaggedValue());
44 uint32_t epos = 0;
45 uint32_t ppos = 0;
46 const uint8_t pairSize = 2;
47 uint32_t methodId = 0;
48 FunctionKind kind;
49 lda.EnumerateLiteralVals(
50 index, [elements, properties, &epos, &ppos, factory, thread, jsPandaFile,
51 &methodId, &kind, &constpool, &entryPoint](const LiteralValue &value, const LiteralTag &tag) {
52 JSTaggedValue jt = JSTaggedValue::Null();
53 bool flag = false;
54 switch (tag) {
55 case LiteralTag::INTEGER: {
56 jt = JSTaggedValue(std::get<uint32_t>(value));
57 break;
58 }
59 case LiteralTag::DOUBLE: {
60 jt = JSTaggedValue(std::get<double>(value));
61 break;
62 }
63 case LiteralTag::BOOL: {
64 jt = JSTaggedValue(std::get<bool>(value));
65 break;
66 }
67 case LiteralTag::STRING: {
68 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
69 EcmaString *str = factory->GetRawStringFromStringTable(sd, MemSpaceType::OLD_SPACE);
70 jt = JSTaggedValue(str);
71 uint32_t elementIndex = 0;
72 if (JSTaggedValue::ToElementIndex(jt, &elementIndex) && ppos % pairSize == 0) {
73 flag = true;
74 }
75 break;
76 }
77 case LiteralTag::METHOD: {
78 methodId = std::get<uint32_t>(value);
79 kind = FunctionKind::NORMAL_FUNCTION;
80 break;
81 }
82 case LiteralTag::GENERATORMETHOD: {
83 methodId = std::get<uint32_t>(value);
84 kind = FunctionKind::GENERATOR_FUNCTION;
85 break;
86 }
87 case LiteralTag::METHODAFFILIATE: {
88 uint16_t length = std::get<uint16_t>(value);
89 JSHandle<JSFunction> jsFunc =
90 DefineMethodInLiteral(thread, jsPandaFile, methodId, constpool, kind, length, entryPoint);
91 jt = jsFunc.GetTaggedValue();
92 break;
93 }
94 case LiteralTag::ACCESSOR: {
95 JSHandle<AccessorData> accessor = factory->NewAccessorData();
96 jt = accessor.GetTaggedValue();
97 break;
98 }
99 case LiteralTag::NULLVALUE: {
100 break;
101 }
102 default: {
103 LOG_ECMA(FATAL) << "this branch is unreachable";
104 UNREACHABLE();
105 break;
106 }
107 }
108 if (tag != LiteralTag::METHOD && tag != LiteralTag::GENERATORMETHOD) {
109 if (epos % pairSize == 0 && !flag) {
110 properties->Set(thread, ppos++, jt);
111 } else {
112 elements->Set(thread, epos++, jt);
113 }
114 }
115 });
116 }
117
GetDatasIgnoreTypeForClass(JSThread * thread,const JSPandaFile * jsPandaFile,size_t index,JSHandle<ConstantPool> constpool,const CString & entryPoint)118 JSHandle<TaggedArray> LiteralDataExtractor::GetDatasIgnoreTypeForClass(JSThread *thread,
119 const JSPandaFile *jsPandaFile, size_t index, JSHandle<ConstantPool> constpool, const CString &entryPoint)
120 {
121 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
122 uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half
123 // The num is 1, indicating that the current class has no member variable.
124 if (num == 1) {
125 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
126 return factory->EmptyArray();
127 }
128 return EnumerateLiteralVals(thread, lda, jsPandaFile, index, constpool, entryPoint);
129 }
130
GetDatasIgnoreType(JSThread * thread,const JSPandaFile * jsPandaFile,size_t index,JSHandle<ConstantPool> constpool,const CString & entryPoint)131 JSHandle<TaggedArray> LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread, const JSPandaFile *jsPandaFile,
132 size_t index, JSHandle<ConstantPool> constpool,
133 const CString &entryPoint)
134 {
135 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
136 return EnumerateLiteralVals(thread, lda, jsPandaFile, index, constpool, entryPoint);
137 }
138
EnumerateLiteralVals(JSThread * thread,LiteralDataAccessor & lda,const JSPandaFile * jsPandaFile,size_t index,JSHandle<ConstantPool> constpool,const CString & entryPoint)139 JSHandle<TaggedArray> LiteralDataExtractor::EnumerateLiteralVals(JSThread *thread, LiteralDataAccessor &lda,
140 const JSPandaFile *jsPandaFile, size_t index, JSHandle<ConstantPool> constpool, const CString &entryPoint)
141 {
142 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
143 uint32_t num = lda.GetLiteralValsNum(index) / 2; // 2: half
144 JSHandle<TaggedArray> literals = factory->NewOldSpaceTaggedArray(num);
145 uint32_t pos = 0;
146 uint32_t methodId = 0;
147 FunctionKind kind;
148 lda.EnumerateLiteralVals(
149 index, [literals, &pos, factory, thread, jsPandaFile, &methodId, &kind, &constpool, &entryPoint]
150 (const LiteralValue &value, const LiteralTag &tag) {
151 JSTaggedValue jt = JSTaggedValue::Null();
152 switch (tag) {
153 case LiteralTag::INTEGER: {
154 jt = JSTaggedValue(std::get<uint32_t>(value));
155 break;
156 }
157 case LiteralTag::DOUBLE: {
158 jt = JSTaggedValue(std::get<double>(value));
159 break;
160 }
161 case LiteralTag::BOOL: {
162 jt = JSTaggedValue(std::get<bool>(value));
163 break;
164 }
165 case LiteralTag::STRING: {
166 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
167 EcmaString *str = factory->GetRawStringFromStringTable(sd, MemSpaceType::OLD_SPACE);
168 jt = JSTaggedValue(str);
169 break;
170 }
171 case LiteralTag::METHOD: {
172 methodId = std::get<uint32_t>(value);
173 kind = FunctionKind::NORMAL_FUNCTION;
174 break;
175 }
176 case LiteralTag::GENERATORMETHOD: {
177 methodId = std::get<uint32_t>(value);
178 kind = FunctionKind::GENERATOR_FUNCTION;
179 break;
180 }
181 case LiteralTag::METHODAFFILIATE: {
182 uint16_t length = std::get<uint16_t>(value);
183 JSHandle<JSFunction> jsFunc =
184 DefineMethodInLiteral(thread, jsPandaFile, methodId, constpool, kind, length, entryPoint);
185 jt = jsFunc.GetTaggedValue();
186 break;
187 }
188 case LiteralTag::ACCESSOR: {
189 JSHandle<AccessorData> accessor = factory->NewAccessorData();
190 jt = accessor.GetTaggedValue();
191 break;
192 }
193 case LiteralTag::NULLVALUE: {
194 break;
195 }
196 default: {
197 LOG_ECMA(FATAL) << "this branch is unreachable";
198 UNREACHABLE();
199 break;
200 }
201 }
202 if (tag != LiteralTag::METHOD && tag != LiteralTag::GENERATORMETHOD) {
203 literals->Set(thread, pos++, jt);
204 } else {
205 uint32_t oldLength = literals->GetLength();
206 literals->Trim(thread, oldLength - 1);
207 }
208 });
209 return literals;
210 }
211
DefineMethodInLiteral(JSThread * thread,const JSPandaFile * jsPandaFile,uint32_t offset,JSHandle<ConstantPool> constpool,FunctionKind kind,uint16_t length,const CString & entryPoint,bool isLoadedAOT,uint32_t entryIndex)212 JSHandle<JSFunction> LiteralDataExtractor::DefineMethodInLiteral(JSThread *thread, const JSPandaFile *jsPandaFile,
213 uint32_t offset, JSHandle<ConstantPool> constpool,
214 FunctionKind kind, uint16_t length,
215 const CString &entryPoint,
216 bool isLoadedAOT, uint32_t entryIndex)
217 {
218 EcmaVM *vm = thread->GetEcmaVM();
219 ObjectFactory *factory = vm->GetFactory();
220
221 auto methodLiteral = jsPandaFile->FindMethodLiteral(offset);
222 ASSERT(methodLiteral != nullptr);
223 methodLiteral->SetFunctionKind(kind);
224 bool canFastCall = false;
225 JSHandle<Method> method = factory->NewMethod(
226 jsPandaFile, methodLiteral, constpool, entryIndex, isLoadedAOT, &canFastCall);
227 JSHandle<JSFunction> jsFunc = factory->NewJSFunction(method, kind, isLoadedAOT, canFastCall);
228 jsFunc->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(length));
229
230 CString moduleName = jsPandaFile->GetJSPandaFileDesc();
231 CString entry = JSPandaFile::ENTRY_FUNCTION_NAME;
232 if (!entryPoint.empty()) {
233 moduleName = entryPoint;
234 entry = entryPoint;
235 }
236 JSRecordInfo recordInfo;
237 bool hasRecord = jsPandaFile->CheckAndGetRecordInfo(entry, recordInfo);
238 if (!hasRecord) {
239 LOG_ECMA(FATAL) << "cannot find record '" + entry + "', please check the request path.";
240 }
241 if (jsPandaFile->IsModule(recordInfo)) {
242 JSHandle<SourceTextModule> module = thread->GetCurrentEcmaContext()->GetModuleManager()->HostGetImportedModule(
243 moduleName);
244 jsFunc->SetModule(thread, module.GetTaggedValue());
245 }
246 return jsFunc;
247 }
248
GetMethodOffsets(const JSPandaFile * jsPandaFile,size_t index,std::vector<uint32_t> & methodOffsets)249 void LiteralDataExtractor::GetMethodOffsets(const JSPandaFile *jsPandaFile, size_t index,
250 std::vector<uint32_t> &methodOffsets)
251 {
252 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
253 lda.EnumerateLiteralVals(index, [&methodOffsets](const LiteralValue &value, const LiteralTag &tag) {
254 switch (tag) {
255 case LiteralTag::METHOD:
256 case LiteralTag::GENERATORMETHOD: {
257 methodOffsets.emplace_back(std::get<uint32_t>(value));
258 break;
259 }
260 default: {
261 break;
262 }
263 }
264 });
265 }
266
GetMethodOffsets(const JSPandaFile * jsPandaFile,EntityId id,std::vector<uint32_t> & methodOffsets)267 void LiteralDataExtractor::GetMethodOffsets(const JSPandaFile *jsPandaFile, EntityId id,
268 std::vector<uint32_t> &methodOffsets)
269 {
270 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
271 lda.EnumerateLiteralVals(id, [&methodOffsets](const LiteralValue &value, const LiteralTag &tag) {
272 switch (tag) {
273 case LiteralTag::METHOD:
274 case LiteralTag::GENERATORMETHOD: {
275 methodOffsets.emplace_back(std::get<uint32_t>(value));
276 break;
277 }
278 default: {
279 break;
280 }
281 }
282 });
283 }
284
ExtractObjectDatas(JSThread * thread,const JSPandaFile * jsPandaFile,EntityId id,JSMutableHandle<TaggedArray> elements,JSMutableHandle<TaggedArray> properties,JSHandle<ConstantPool> constpool,const CString & entry,bool isLoadedAOT,JSHandle<AOTLiteralInfo> entryIndexes)285 void LiteralDataExtractor::ExtractObjectDatas(JSThread *thread, const JSPandaFile *jsPandaFile, EntityId id,
286 JSMutableHandle<TaggedArray> elements,
287 JSMutableHandle<TaggedArray> properties,
288 JSHandle<ConstantPool> constpool, const CString &entry,
289 bool isLoadedAOT, JSHandle<AOTLiteralInfo> entryIndexes)
290 {
291 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
292 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
293 uint32_t num = lda.GetLiteralValsNum(id) / 2; // 2: half
294 elements.Update(factory->NewOldSpaceTaggedArray(num).GetTaggedValue());
295 properties.Update(factory->NewOldSpaceTaggedArray(num).GetTaggedValue());
296 uint32_t epos = 0;
297 uint32_t ppos = 0;
298 const uint8_t pairSize = 2;
299 uint32_t methodId = 0;
300 int pos = 0;
301 FunctionKind kind;
302 lda.EnumerateLiteralVals(
303 id, [elements, properties, &entryIndexes, &pos, &epos, &ppos, factory, thread, jsPandaFile,
304 &methodId, &kind, &constpool, &entry, &isLoadedAOT](const LiteralValue &value, const LiteralTag &tag) {
305 JSTaggedValue jt = JSTaggedValue::Null();
306 bool flag = false;
307 switch (tag) {
308 case LiteralTag::INTEGER: {
309 jt = JSTaggedValue(std::get<uint32_t>(value));
310 break;
311 }
312 case LiteralTag::DOUBLE: {
313 jt = JSTaggedValue(std::get<double>(value));
314 break;
315 }
316 case LiteralTag::BOOL: {
317 jt = JSTaggedValue(std::get<bool>(value));
318 break;
319 }
320 case LiteralTag::STRING: {
321 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
322 EcmaString *str = factory->GetRawStringFromStringTable(sd, MemSpaceType::OLD_SPACE);
323 jt = JSTaggedValue(str);
324 uint32_t elementIndex = 0;
325 if (JSTaggedValue::ToElementIndex(jt, &elementIndex) && ppos % pairSize == 0) {
326 flag = true;
327 }
328 break;
329 }
330 case LiteralTag::METHOD: {
331 methodId = std::get<uint32_t>(value);
332 kind = FunctionKind::NORMAL_FUNCTION;
333 break;
334 }
335 case LiteralTag::GENERATORMETHOD: {
336 methodId = std::get<uint32_t>(value);
337 kind = FunctionKind::GENERATOR_FUNCTION;
338 break;
339 }
340 case LiteralTag::METHODAFFILIATE: {
341 uint16_t length = std::get<uint16_t>(value);
342 int entryIndex = 0;
343 bool needSetAotFlag = (isLoadedAOT && (epos % pairSize == 0) && !flag);
344 if (needSetAotFlag) {
345 entryIndex = entryIndexes->GetObjectFromCache(pos++).GetInt();
346 // -1 : this jsfunction is a large function
347 if (entryIndex == -1) {
348 needSetAotFlag = false;
349 }
350 }
351 JSHandle<JSFunction> jsFunc =
352 DefineMethodInLiteral(thread, jsPandaFile, methodId, constpool, kind,
353 length, entry, needSetAotFlag, entryIndex);
354 jt = jsFunc.GetTaggedValue();
355 break;
356 }
357 case LiteralTag::ACCESSOR: {
358 JSHandle<AccessorData> accessor = factory->NewAccessorData();
359 jt = accessor.GetTaggedValue();
360 break;
361 }
362 case LiteralTag::NULLVALUE: {
363 break;
364 }
365 default: {
366 LOG_ECMA(FATAL) << "this branch is unreachable";
367 UNREACHABLE();
368 break;
369 }
370 }
371 if (tag != LiteralTag::METHOD && tag != LiteralTag::GENERATORMETHOD) {
372 if ((epos % pairSize == 0) && !flag) {
373 properties->Set(thread, ppos++, jt);
374 } else {
375 elements->Set(thread, epos++, jt);
376 }
377 }
378 });
379 }
380
GetDatasIgnoreType(JSThread * thread,const JSPandaFile * jsPandaFile,EntityId id,JSHandle<ConstantPool> constpool,const CString & entryPoint,bool isLoadedAOT,JSHandle<AOTLiteralInfo> entryIndexes)381 JSHandle<TaggedArray> LiteralDataExtractor::GetDatasIgnoreType(JSThread *thread, const JSPandaFile *jsPandaFile,
382 EntityId id, JSHandle<ConstantPool> constpool,
383 const CString &entryPoint,
384 bool isLoadedAOT, JSHandle<AOTLiteralInfo> entryIndexes)
385 {
386 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
387 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
388 uint32_t num = lda.GetLiteralValsNum(id) / 2; // 2: half
389 JSHandle<TaggedArray> literals = JSHandle<TaggedArray>(factory->NewCOWTaggedArray(num));
390 uint32_t pos = 0;
391 uint32_t methodId = 0;
392 FunctionKind kind;
393 int index = 0;
394 lda.EnumerateLiteralVals(
395 id, [literals, &pos, factory, thread, jsPandaFile,
396 &methodId, &kind, &constpool, &entryPoint, &entryIndexes, &index, isLoadedAOT]
397 (const LiteralValue &value, const LiteralTag &tag) {
398 JSTaggedValue jt = JSTaggedValue::Null();
399 switch (tag) {
400 case LiteralTag::INTEGER: {
401 jt = JSTaggedValue(std::get<uint32_t>(value));
402 break;
403 }
404 case LiteralTag::DOUBLE: {
405 jt = JSTaggedValue(std::get<double>(value));
406 break;
407 }
408 case LiteralTag::BOOL: {
409 jt = JSTaggedValue(std::get<bool>(value));
410 break;
411 }
412 case LiteralTag::STRING: {
413 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
414 EcmaString *str = factory->GetRawStringFromStringTable(sd, MemSpaceType::OLD_SPACE);
415 jt = JSTaggedValue(str);
416 break;
417 }
418 case LiteralTag::METHOD: {
419 methodId = std::get<uint32_t>(value);
420 kind = FunctionKind::NORMAL_FUNCTION;
421 break;
422 }
423 case LiteralTag::GENERATORMETHOD: {
424 methodId = std::get<uint32_t>(value);
425 kind = FunctionKind::GENERATOR_FUNCTION;
426 break;
427 }
428 case LiteralTag::METHODAFFILIATE: {
429 uint16_t length = std::get<uint16_t>(value);
430 int entryIndex = 0;
431 bool needSetAotFlag = isLoadedAOT;
432 if (isLoadedAOT) {
433 entryIndex = entryIndexes->GetObjectFromCache(index++).GetInt();
434 if (entryIndex == -1) {
435 needSetAotFlag = false;
436 }
437 }
438 JSHandle<JSFunction> jsFunc =
439 DefineMethodInLiteral(thread, jsPandaFile, methodId, constpool,
440 kind, length, entryPoint, needSetAotFlag, entryIndex);
441 jt = jsFunc.GetTaggedValue();
442 break;
443 }
444 case LiteralTag::ACCESSOR: {
445 JSHandle<AccessorData> accessor = factory->NewAccessorData();
446 jt = accessor.GetTaggedValue();
447 break;
448 }
449 case LiteralTag::NULLVALUE: {
450 break;
451 }
452 default: {
453 LOG_ECMA(FATAL) << "this branch is unreachable";
454 UNREACHABLE();
455 break;
456 }
457 }
458 if (tag != LiteralTag::METHOD && tag != LiteralTag::GENERATORMETHOD) {
459 literals->Set(thread, pos++, jt);
460 } else {
461 uint32_t oldLength = literals->GetLength();
462 literals->Trim(thread, oldLength - 1);
463 }
464 });
465 return literals;
466 }
467 } // namespace panda::ecmascript
468