1 /*
2 * Copyright (c) 2025 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/compiler/builtins/containers_stub_builder.h"
17
18 #include "ecmascript/compiler/call_stub_builder.h"
19
20 namespace panda::ecmascript::kungfu {
21 // common IR for containers apis that use function call
ContainersCommonFuncCall(GateRef glue,GateRef thisValue,GateRef numArgs,Variable * result,Label * exit,Label * slowPath,ContainersType type)22 void ContainersCommonStubBuilder::ContainersCommonFuncCall(GateRef glue, GateRef thisValue,
23 GateRef numArgs, Variable* result, Label *exit, Label *slowPath, ContainersType type)
24 {
25 auto env = GetEnvironment();
26 DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
27 DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
28 DEFVARIABLE(key, VariableType::INT64(), Int64(0));
29 DEFVARIABLE(kValue, VariableType::JS_ANY(), Undefined());
30 DEFVARIABLE(length, VariableType::INT32(), Int32(0));
31 DEFVARIABLE(k, VariableType::INT32(), Int32(0));
32 Label valueIsJSAPIVector(env);
33 Label valueNotJSAPIVector(env);
34 Label objIsJSProxy(env);
35 Label objNotJSProxy(env);
36 Label objIsJSAPIVector(env);
37 Label thisArgUndefined(env);
38 Label thisArgNotUndefined(env);
39 Label callbackUndefined(env);
40 Label callbackNotUndefined(env);
41 Label nextCount(env);
42 Label loopHead(env);
43 Label loopEnd(env);
44 Label next(env);
45 Label afterLoop(env);
46 Label thisValueIsHeapObj(env);
47 GateRef callbackFnHandle;
48 BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
49 Bind(&thisValueIsHeapObj);
50 BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPIVector, &valueNotJSAPIVector);
51 Bind(&valueNotJSAPIVector);
52 {
53 BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
54 Bind(&objIsJSProxy);
55 {
56 GateRef tempObj = GetTarget(glue, *thisObj);
57 BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPIVector, slowPath);
58 Bind(&objIsJSAPIVector);
59 {
60 thisObj = tempObj;
61 Jump(&valueIsJSAPIVector);
62 }
63 }
64 Bind(&objNotJSProxy);
65 Jump(slowPath);
66 }
67 Bind(&valueIsJSAPIVector);
68 {
69 BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
70 Bind(&callbackUndefined);
71 Jump(slowPath);
72 Bind(&callbackNotUndefined);
73 {
74 Label isCall(env);
75 Label notCall(env);
76 Label isHeapObj(env);
77 callbackFnHandle = GetCallArg0(numArgs);
78 BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, slowPath);
79 Bind(&isHeapObj);
80 BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
81 Bind(¬Call);
82 Jump(slowPath);
83 Bind(&isCall);
84 {
85 BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
86 Bind(&thisArgUndefined);
87 Jump(&nextCount);
88 Bind(&thisArgNotUndefined);
89 thisArg = GetCallArg1(numArgs);
90 Jump(&nextCount);
91 }
92 }
93 }
94 Bind(&nextCount);
95 {
96 length = ContainerGetSize(glue, *thisObj, type);
97 Jump(&loopHead);
98 LoopBegin(&loopHead);
99 {
100 Label lenChange(env);
101 Label hasException(env);
102 Label notHasException(env);
103 Label setValue(env);
104 BRANCH(Int32LessThan(*k, *length), &next, &afterLoop);
105 Bind(&next);
106 {
107 kValue = ContainerGetValue(glue, *thisObj, *k, type);
108 if (IsPlainArray(type)) {
109 key = PlainArrayGetKey(glue, *thisObj, *k);
110 } else {
111 key = IntToTaggedInt(*k);
112 }
113 JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
114 callArgs.callThisArg3WithReturnArgs = { *thisArg, *kValue, *key, *thisObj };
115 CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
116 Circuit::NullGate(), callArgs);
117 GateRef retValue = callBuilder.JSCallDispatch();
118 BRANCH(HasPendingException(glue), &hasException, ¬HasException);
119 Bind(&hasException);
120 {
121 result->WriteVariable(retValue);
122 Jump(exit);
123 }
124 Bind(¬HasException);
125 GateRef tempLen = ContainerGetSize(glue, *thisObj, type);
126 BRANCH(Int32NotEqual(tempLen, *length), &lenChange, &setValue);
127 Bind(&lenChange);
128 if (!IsArrayListReplaceAllelements(type)) {
129 length = tempLen;
130 }
131 BRANCH(Int32GreaterThanOrEqual(*k, *length), &afterLoop, &setValue);
132 Bind(&setValue);
133 if (IsReplaceAllElements(type)) {
134 GateRef elementsOffset = IntPtr(JSObject::ELEMENTS_OFFSET);
135 GateRef elements = Load(VariableType::JS_POINTER(), glue, *thisObj, elementsOffset);
136 SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, *k, retValue);
137 }
138 Jump(&loopEnd);
139 }
140 }
141 Bind(&loopEnd);
142 k = Int32Add(*k, Int32(1));
143 LoopEndWithCheckSafePoint(&loopHead, env, glue);
144 }
145 Bind(&afterLoop);
146 Jump(exit);
147 }
148
ContainersLightWeightCall(GateRef glue,GateRef thisValue,GateRef numArgs,Variable * result,Label * exit,Label * slowPath,ContainersType type)149 void ContainersCommonStubBuilder::ContainersLightWeightCall(GateRef glue, GateRef thisValue, GateRef numArgs,
150 Variable* result, Label *exit, Label *slowPath,
151 ContainersType type)
152 {
153 auto env = GetEnvironment();
154 DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
155 DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
156 DEFVARIABLE(key, VariableType::JS_ANY(), Undefined());
157 DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
158 DEFVARIABLE(length, VariableType::INT32(), Int32(0));
159 DEFVARIABLE(index, VariableType::INT32(), Int32(0));
160 Label valueIsJSAPILightWeight(env);
161 Label valueNotJSAPILightWeight(env);
162 Label objIsJSProxy(env);
163 Label objNotJSProxy(env);
164 Label objIsJSAPILightWeight(env);
165 Label thisArgUndefined(env);
166 Label thisArgNotUndefined(env);
167 Label callbackUndefined(env);
168 Label callbackNotUndefined(env);
169 Label nextCount(env);
170 Label loopHead(env);
171 Label loopEnd(env);
172 Label next(env);
173 Label afterLoop(env);
174 Label thisValueIsHeapObj(env);
175 GateRef callbackFnHandle;
176 BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
177 Bind(&thisValueIsHeapObj);
178 BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPILightWeight, &valueNotJSAPILightWeight);
179 Bind(&valueNotJSAPILightWeight);
180 {
181 BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
182 Bind(&objIsJSProxy);
183 {
184 GateRef tempObj = GetTarget(glue, *thisObj);
185 BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPILightWeight, slowPath);
186 Bind(&objIsJSAPILightWeight);
187 {
188 thisObj = tempObj;
189 Jump(&valueIsJSAPILightWeight);
190 }
191 }
192 Bind(&objNotJSProxy);
193 Jump(slowPath);
194 }
195 Bind(&valueIsJSAPILightWeight);
196 {
197 BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
198 Bind(&callbackUndefined);
199 Jump(slowPath);
200 Bind(&callbackNotUndefined);
201 {
202 Label isCall(env);
203 Label notCall(env);
204 Label isHeapObj(env);
205 callbackFnHandle = GetCallArg0(numArgs);
206 BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, slowPath);
207 Bind(&isHeapObj);
208 BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
209 Bind(¬Call);
210 Jump(slowPath);
211 Bind(&isCall);
212 {
213 BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
214 Bind(&thisArgUndefined);
215 Jump(&nextCount);
216 Bind(&thisArgNotUndefined);
217 thisArg = GetCallArg1(numArgs);
218 Jump(&nextCount);
219 }
220 }
221 }
222 Bind(&nextCount);
223 {
224 length = ContainerGetSize(glue, *thisObj, type);
225 Jump(&loopHead);
226 LoopBegin(&loopHead);
227 {
228 Label lenChange(env);
229 Label hasException(env);
230 Label notHasException(env);
231 BRANCH(Int32LessThan(*index, *length), &next, &afterLoop);
232 Bind(&next);
233 {
234 value = ContainerGetValue(glue, *thisObj, *index, type);
235 key = ContainerGetKey(glue, *thisObj, *index, type);
236 JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
237 callArgs.callThisArg3WithReturnArgs = { *thisArg, *value, *key, *thisObj };
238 CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
239 Circuit::NullGate(), callArgs);
240 GateRef retValue = callBuilder.JSCallDispatch();
241 BRANCH(HasPendingException(glue), &hasException, ¬HasException);
242 Bind(&hasException);
243 {
244 result->WriteVariable(retValue);
245 Jump(exit);
246 }
247 Bind(¬HasException);
248 GateRef currentLen = ContainerGetSize(glue, *thisObj, type);
249 BRANCH(Int32NotEqual(currentLen, *length), &lenChange, &loopEnd);
250 Bind(&lenChange);
251 length = currentLen;
252 Jump(&loopEnd);
253 }
254 }
255 Bind(&loopEnd);
256 index = Int32Add(*index, Int32(1));
257 LoopEndWithCheckSafePoint(&loopHead, env, glue);
258 }
259 Bind(&afterLoop);
260 Jump(exit);
261 }
262
ContainersHashCall(GateRef glue,GateRef thisValue,GateRef numArgs,Variable * result,Label * exit,Label * slowPath,ContainersType type)263 void ContainersCommonStubBuilder::ContainersHashCall(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* result,
264 Label *exit, Label *slowPath, ContainersType type)
265 {
266 auto env = GetEnvironment();
267 DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
268 DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
269 DEFVARIABLE(node, VariableType::JS_ANY(), Undefined());
270 DEFVARIABLE(key, VariableType::JS_ANY(), Undefined());
271 DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
272 DEFVARIABLE(length, VariableType::INT32(), Int32(0));
273 DEFVARIABLE(index, VariableType::INT32(), Int32(0));
274 Label valueIsJSAPIHash(env);
275 Label valueNotJSAPIHash(env);
276 Label objIsJSProxy(env);
277 Label objNotJSProxy(env);
278 Label objIsJSAPIHash(env);
279 Label thisArgUndefined(env);
280 Label thisArgNotUndefined(env);
281 Label callbackUndefined(env);
282 Label callbackNotUndefined(env);
283 Label nextCount(env);
284 Label nodeNotHole(env);
285 Label nodeIsLinked(env);
286 Label nodeIsRBTree(env);
287 Label loopLinked(env);
288 Label loopHead(env);
289 Label loopEnd(env);
290 Label next(env);
291 Label afterLoop(env);
292 Label thisValueIsHeapObj(env);
293 GateRef callbackFnHandle;
294 BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
295 Bind(&thisValueIsHeapObj);
296 BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPIHash, &valueNotJSAPIHash);
297 Bind(&valueNotJSAPIHash);
298 {
299 BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
300 Bind(&objIsJSProxy);
301 {
302 GateRef tempObj = GetTarget(glue, *thisObj);
303 BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPIHash, slowPath);
304 Bind(&objIsJSAPIHash);
305 {
306 thisObj = tempObj;
307 Jump(&valueIsJSAPIHash);
308 }
309 }
310 Bind(&objNotJSProxy);
311 Jump(slowPath);
312 }
313 Bind(&valueIsJSAPIHash);
314 {
315 BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
316 Bind(&callbackUndefined);
317 Jump(slowPath);
318 Bind(&callbackNotUndefined);
319 {
320 Label isCall(env);
321 Label notCall(env);
322 Label isHeapObj(env);
323 callbackFnHandle = GetCallArg0(numArgs);
324 BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, ¬Call);
325 Bind(&isHeapObj);
326 BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
327 Bind(¬Call);
328 Jump(slowPath);
329 Bind(&isCall);
330 {
331 BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
332 Bind(&thisArgUndefined);
333 Jump(&nextCount);
334 Bind(&thisArgNotUndefined);
335 thisArg = GetCallArg1(numArgs);
336 Jump(&nextCount);
337 }
338 }
339 }
340 Bind(&nextCount);
341 {
342 length = ContainerGetSize(glue, *thisObj, type);
343 Jump(&loopHead);
344 LoopBegin(&loopHead);
345 {
346 Label hasExceptionLinked(env);
347 Label notHasExceptionLinked(env);
348 Label hasExceptionRBTree(env);
349 Label notHasExceptionRBTree(env);
350 BRANCH(Int32LessThan(*index, *length), &next, &afterLoop);
351 Bind(&next);
352 {
353 node = ContainerGetNode(glue, *thisObj, *index, type);
354 BRANCH(TaggedIsHole(*node), &loopEnd, &nodeNotHole);
355 Bind(&nodeNotHole);
356 BRANCH(IsLinkedNode(glue, *node), &nodeIsLinked, &nodeIsRBTree);
357 LoopBegin(&nodeIsLinked);
358 {
359 value = Load(VariableType::JS_POINTER(), glue, *node, IntPtr(
360 type == ContainersType::HASHSET_FOREACH ? LinkedNode::KEY_OFFSET : LinkedNode::VALUE_OFFSET));
361 key = Load(VariableType::JS_POINTER(), glue, *node, IntPtr(LinkedNode::KEY_OFFSET));
362 JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
363 callArgs.callThisArg3WithReturnArgs = { *thisArg, *value, *key, *thisObj };
364 CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0,
365 nullptr, Circuit::NullGate(), callArgs);
366 GateRef retValue = callBuilder.JSCallDispatch();
367 BRANCH(HasPendingException(glue), &hasExceptionLinked, ¬HasExceptionLinked);
368 Bind(&hasExceptionLinked);
369 {
370 result->WriteVariable(retValue);
371 Jump(exit);
372 }
373 Bind(¬HasExceptionLinked);
374 node = Load(VariableType::JS_POINTER(), glue, *node, IntPtr(LinkedNode::NEXT_OFFSET));
375 BRANCH(TaggedIsHole(*node), &loopEnd, &loopLinked);
376 }
377 Bind(&loopLinked);
378 LoopEnd(&nodeIsLinked);
379 Bind(&nodeIsRBTree);
380 {
381 GateRef retValue = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(),
382 RTSTUB_ID(ContainerRBTreeForEach), { *node, callbackFnHandle, *thisArg, *thisObj,
383 IntToTaggedInt(Int32(static_cast<int32_t>(type))) });
384 BRANCH(HasPendingException(glue), &hasExceptionRBTree, ¬HasExceptionRBTree);
385 Bind(&hasExceptionRBTree);
386 {
387 result->WriteVariable(retValue);
388 Jump(exit);
389 }
390 Bind(¬HasExceptionRBTree);
391 Jump(&loopEnd);
392 }
393 }
394 }
395 Bind(&loopEnd);
396 index = Int32Add(*index, Int32(1));
397 LoopEndWithCheckSafePoint(&loopHead, env, glue);
398 }
399 Bind(&afterLoop);
400 Jump(exit);
401 }
402
ContainersLinkedListCall(GateRef glue,GateRef thisValue,GateRef numArgs,Variable * result,Label * exit,Label * slowPath,ContainersType type)403 void ContainersCommonStubBuilder::ContainersLinkedListCall(GateRef glue, GateRef thisValue, GateRef numArgs,
404 Variable* result, Label *exit, Label *slowPath,
405 ContainersType type)
406 {
407 auto env = GetEnvironment();
408 DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
409 DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
410 DEFVARIABLE(valueNode, VariableType::INT32(), Int32(0));
411 DEFVARIABLE(key, VariableType::JS_ANY(), Undefined());
412 DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
413 DEFVARIABLE(length, VariableType::INT32(), Int32(0));
414 DEFVARIABLE(index, VariableType::INT32(), Int32(0));
415 Label valueIsJSAPILinkedList(env);
416 Label valueNotJSAPILinkedList(env);
417 Label objIsJSProxy(env);
418 Label objNotJSProxy(env);
419 Label objIsJSAPILinkedList(env);
420 Label thisArgUndefined(env);
421 Label thisArgNotUndefined(env);
422 Label callbackUndefined(env);
423 Label callbackNotUndefined(env);
424 Label nextCount(env);
425 Label valueNotHole(env);
426 Label loopHead(env);
427 Label loopEnd(env);
428 Label next(env);
429 Label afterLoop(env);
430 Label thisValueIsHeapObj(env);
431 GateRef callbackFnHandle;
432 BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
433 Bind(&thisValueIsHeapObj);
434 BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPILinkedList, &valueNotJSAPILinkedList);
435 Bind(&valueNotJSAPILinkedList);
436 {
437 BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
438 Bind(&objIsJSProxy);
439 {
440 GateRef tempObj = GetTarget(glue, *thisObj);
441 BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPILinkedList, slowPath);
442 Bind(&objIsJSAPILinkedList);
443 {
444 thisObj = tempObj;
445 Jump(&valueIsJSAPILinkedList);
446 }
447 }
448 Bind(&objNotJSProxy);
449 Jump(slowPath);
450 }
451 Bind(&valueIsJSAPILinkedList);
452 {
453 BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
454 Bind(&callbackUndefined);
455 Jump(slowPath);
456 Bind(&callbackNotUndefined);
457 {
458 Label isCall(env);
459 Label notCall(env);
460 Label isHeapObj(env);
461 callbackFnHandle = GetCallArg0(numArgs);
462 BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, ¬Call);
463 Bind(&isHeapObj);
464 BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
465 Bind(¬Call);
466 Jump(slowPath);
467 Bind(&isCall);
468 {
469 BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
470 Bind(&thisArgUndefined);
471 Jump(&nextCount);
472 Bind(&thisArgNotUndefined);
473 thisArg = GetCallArg1(numArgs);
474 Jump(&nextCount);
475 }
476 }
477 }
478 Bind(&nextCount);
479 {
480 length = ContainerGetSize(glue, *thisObj, type);
481 valueNode = Int32(TaggedList<TaggedArray>::ELEMENTS_START_INDEX);
482 Jump(&loopHead);
483 LoopBegin(&loopHead);
484 {
485 Label hasException(env);
486 Label notHasException(env);
487 BRANCH(Int32LessThan(*index, *length), &next, &afterLoop);
488 Bind(&next);
489 {
490 valueNode = TaggedGetInt(ContainerGetNode(glue, *thisObj,
491 Int32Add(*valueNode, Int32(TaggedList<TaggedArray>::NEXT_PTR_OFFSET)), type));
492 value = ContainerGetNode(glue, *thisObj, *valueNode, type);
493 BRANCH(TaggedIsHole(*value), &loopEnd, &valueNotHole);
494 Bind(&valueNotHole);
495 key = IntToTaggedInt(*index);
496 JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
497 callArgs.callThisArg3WithReturnArgs = { *thisArg, *value, *key, *thisObj };
498 CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
499 Circuit::NullGate(), callArgs);
500 GateRef retValue = callBuilder.JSCallDispatch();
501 BRANCH(HasPendingException(glue), &hasException, ¬HasException);
502 Bind(&hasException);
503 {
504 result->WriteVariable(retValue);
505 Jump(exit);
506 }
507 Bind(¬HasException);
508 Jump(&loopEnd);
509 }
510 }
511 Bind(&loopEnd);
512 index = Int32Add(*index, Int32(1));
513 LoopEndWithCheckSafePoint(&loopHead, env, glue);
514 }
515 Bind(&afterLoop);
516 Jump(exit);
517 }
518 } // namespace panda::ecmascript::kungfu
519