• 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 "js_date.h"
17 #include <ctime>
18 #include <regex>
19 #include <sys/time.h>
20 #include "base/builtins_base.h"
21 
22 namespace panda::ecmascript {
23 using NumberHelper = base::NumberHelper;
24 bool DateUtils::isCached_ = false;
25 int32_t DateUtils::preSumDays_ = 0;
26 int32_t DateUtils::preDays_ = 0;
27 int32_t DateUtils::preYear_ = 0;
TransferTimeToDate(int64_t timeMs,std::array<int64_t,DATE_LENGTH> * date)28 void DateUtils::TransferTimeToDate(int64_t timeMs, std::array<int64_t, DATE_LENGTH> *date)
29 {
30     (*date)[HOUR] = Mod(timeMs, MS_PER_DAY);                                 // ms from hour, minutes, second, ms
31     (*date)[DAYS] = (timeMs - (*date)[HOUR]) / MS_PER_DAY;                   // days from year, month, day
32     (*date)[MS] = (*date)[HOUR] % MS_PER_SECOND;                             // ms
33     (*date)[HOUR] = ((*date)[HOUR] - (*date)[MS]) / MS_PER_SECOND;           // s from hour, minutes, second
34     (*date)[SEC] = (*date)[HOUR] % SEC_PER_MINUTE;                           // second
35     (*date)[HOUR] = ((*date)[HOUR] - (*date)[SEC]) / SEC_PER_MINUTE;         // min from hour, minutes
36     (*date)[MIN] = (*date)[HOUR] % SEC_PER_MINUTE;                           // min
37     (*date)[HOUR] = ((*date)[HOUR] - (*date)[MIN]) / SEC_PER_MINUTE;         // hour
38     (*date)[WEEKDAY] = Mod(((*date)[DAYS] + LEAP_NUMBER[0]), DAY_PER_WEEK);  // weekday
39     (*date)[YEAR] = GetYearFromDays(&((*date)[DAYS]));                       // year
40 }
41 // static
IsLeap(int64_t year)42 bool DateUtils::IsLeap(int64_t year)
43 {
44     return year % LEAP_NUMBER[0] == 0 && (year % LEAP_NUMBER[1] != 0 || year % LEAP_NUMBER[2] == 0);  // 2: means index
45 }
46 
47 // static
GetDaysInYear(int64_t year)48 int64_t DateUtils::GetDaysInYear(int64_t year)
49 {
50     int64_t number = IsLeap(year) ? (DAYS_IN_YEAR + 1) : DAYS_IN_YEAR;
51     return number;
52 }
53 
54 // static
GetDaysFromYear(int64_t year)55 int64_t DateUtils::GetDaysFromYear(int64_t year)
56 {
57     return DAYS_IN_YEAR * (year - YEAR_NUMBER[0]) + FloorDiv(year - YEAR_NUMBER[1], LEAP_NUMBER[0]) -
58            FloorDiv(year - YEAR_NUMBER[2], LEAP_NUMBER[1]) +  // 2: year index
59            FloorDiv(year - YEAR_NUMBER[3], LEAP_NUMBER[2]);   // 3, 2: year index
60 }
61 
62 // static
FloorDiv(int64_t a,int64_t b)63 int64_t DateUtils::FloorDiv(int64_t a, int64_t b)
64 {
65     ASSERT(b != 0);
66     int64_t m = a % b;
67     int64_t res = m < 0 ? ((a - m - b) / b) : ((a - m) / b);
68     return res;
69 }
70 
71 // static
GetYearFromDays(int64_t * days)72 int64_t DateUtils::GetYearFromDays(int64_t *days)
73 {
74     if (isCached_ && preSumDays_ == *days) {
75         *days = preDays_;
76         return preYear_;
77     }
78     int64_t realDay;
79     int64_t dayTemp = 0;
80     int64_t d = *days;
81     preSumDays_ = d;
82     int64_t year = FloorDiv(d * APPROXIMATION_NUMBER[0], APPROXIMATION_NUMBER[1]) + YEAR_NUMBER[0];
83     realDay = d - GetDaysFromYear(year);
84     while (realDay != 0) {
85         if (realDay < 0) {
86             year--;
87         } else {
88             dayTemp = GetDaysInYear(year);
89             if (realDay < dayTemp) {
90                 break;
91             }
92             year++;
93         }
94         realDay = d - GetDaysFromYear(year);
95     }
96     *days = realDay;
97     preDays_ = realDay;
98     preYear_ = year;
99     isCached_ = true;
100     return year;
101 }
102 
103 // static
Mod(int64_t a,int b)104 int64_t DateUtils::Mod(int64_t a, int b)
105 {
106     ASSERT(b != 0);
107     int64_t m = a % b;
108     int64_t res = m < 0 ? (m + b) : m;
109     return res;
110 }
111 
112 // static
113 // 20.4.1.11
MakeTime(double hour,double min,double sec,double ms)114 double JSDate::MakeTime(double hour, double min, double sec, double ms)
115 {
116     if (std::isfinite(hour) && std::isfinite(min) && std::isfinite(sec) && std::isfinite(ms)) {
117         double hourInteger = NumberHelper::TruncateDouble(hour);
118         double minInteger = NumberHelper::TruncateDouble(min);
119         double secInteger = NumberHelper::TruncateDouble(sec);
120         double msInteger = NumberHelper::TruncateDouble(ms);
121         return hourInteger * MS_PER_HOUR + minInteger * MS_PER_MINUTE + secInteger * MS_PER_SECOND + msInteger;
122     }
123     return base::NAN_VALUE;
124 }
125 
126 // static
127 // 20.4.1.12
MakeDay(double year,double month,double date)128 double JSDate::MakeDay(double year, double month, double date)
129 {
130     if (std::isfinite(year) && std::isfinite(month) && std::isfinite(date)) {
131         double yearInteger = NumberHelper::TruncateDouble(year);
132         double monthInteger = NumberHelper::TruncateDouble(month);
133         int64_t y = static_cast<int64_t>(yearInteger) + static_cast<int64_t>(monthInteger / MOUTH_PER_YEAR);
134         int64_t m = static_cast<int64_t>(monthInteger) % MOUTH_PER_YEAR;
135         if (m < 0) {
136             m += MOUTH_PER_YEAR;
137             y -= 1;
138         }
139 
140         int64_t days = DateUtils::GetDaysFromYear(y);
141         int index = DateUtils::IsLeap(year) ? 1 : 0;
142         days += DAYS_FROM_MONTH[index][m];
143         return static_cast<double>(days - 1) + NumberHelper::TruncateDouble(date);
144     }
145     return base::NAN_VALUE;
146 }
147 
148 // static
149 // 20.4.1.13
MakeDate(double day,double time)150 double JSDate::MakeDate(double day, double time)
151 {
152     if (std::isfinite(day) && std::isfinite(time)) {
153         return time + day * MS_PER_DAY;
154     }
155     return base::NAN_VALUE;
156 }
157 
158 // static
159 // 20.4.1.14
TimeClip(double time)160 double JSDate::TimeClip(double time)
161 {
162     if (-MAX_TIME_IN_MS <= time && time <= MAX_TIME_IN_MS) {
163         return NumberHelper::TruncateDouble(time);
164     }
165     return base::NAN_VALUE;
166 }
167 
168 // 20.4.1.8
LocalTime(double timeMs) const169 double JSDate::LocalTime(double timeMs) const
170 {
171     return timeMs + GetLocalOffsetFromOS(timeMs, true);
172 }
173 
174 // 20.4.1.9
UTCTime(double timeMs) const175 double JSDate::UTCTime(double timeMs) const
176 {
177     return timeMs - GetLocalOffsetFromOS(timeMs, false);
178 }
179 
180 // static
GetSignedNumFromString(const CString & str,int len,int * index)181 int JSDate::GetSignedNumFromString(const CString &str, int len, int *index)
182 {
183     int res = 0;
184     GetNumFromString(str, len, index, &res);
185     if (str.at(0) == NEG) {
186         return -res;
187     }
188     return res;
189 }
190 
191 // static
GetNumFromString(const CString & str,int len,int * index,int * num)192 bool JSDate::GetNumFromString(const CString &str, int len, int *index, int *num)
193 {
194     int indexStr = *index;
195     char oneByte = 0;
196     while (indexStr < len) {
197         oneByte = str.at(indexStr);
198         if (oneByte >= '0' && oneByte <= '9') {
199             break;
200         }
201         indexStr++;
202     }
203     if (indexStr >= len) {
204         return false;
205     }
206     int value = 0;
207     while (indexStr < len) {
208         oneByte = str.at(indexStr);
209         int val = oneByte - '0';
210         if (val >= 0 && val <= NUM_NINE) {
211             value = value * TEN + val;
212             indexStr++;
213         } else {
214             break;
215         }
216     }
217     *num = value;
218     *index = indexStr;
219     return true;
220 }
221 
222 // 20.4.1.7
GetLocalOffsetInMin(const JSThread * thread,int64_t timeMs,bool isLocal)223 int64_t JSDate::GetLocalOffsetInMin(const JSThread *thread, int64_t timeMs, bool isLocal)
224 {
225     if (!isLocal) {
226         return 0;
227     }
228     double localOffset = this->GetLocalOffset().GetDouble();
229     if (localOffset == MAX_DOUBLE) {
230         localOffset = static_cast<double>(GetLocalOffsetFromOS(timeMs, isLocal));
231         SetLocalOffset(thread, JSTaggedValue(localOffset));
232     }
233     return localOffset;
234 }
235 
236 // static
LocalParseStringToMs(const CString & str)237 JSTaggedValue JSDate::LocalParseStringToMs(const CString &str)
238 {
239     int year = 0;
240     int month = 0;
241     int date = 1;
242     int hours = 0;
243     int minutes = 0;
244     int seconds = 0;
245     int ms = 0;
246     int index = 0;
247     int len = str.length();
248     bool isLocal = false;
249     CString::size_type indexGmt;
250     CString::size_type indexPlus = CString::npos;
251     std::array<CString, MOUTH_PER_YEAR> monthName = {
252         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
253         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
254         };
255     int localTime = 0;
256     int localHours = 0;
257     int localMinutes = 0;
258     int64_t localMs = 0;
259     CString::size_type localSpace = str.find(' ', index);
260     CString strMonth = str.substr(localSpace + 1, LENGTH_MONTH_NAME);
261     for (int i = 0; i < MOUTH_PER_YEAR; i++) {
262         if (strMonth == monthName[i]) {
263             month = i;
264             break;
265         }
266     }
267     index += (LENGTH_MONTH_NAME + 1);
268     GetNumFromString(str, len, &index, &date);
269     GetNumFromString(str, len, &index, &year);
270     indexGmt = str.find("GMT", index);
271     if (indexGmt == CString::npos) {
272         GetNumFromString(str, len, &index, &hours);
273         GetNumFromString(str, len, &index, &minutes);
274         GetNumFromString(str, len, &index, &seconds);
275         isLocal = true;
276         localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE);
277     } else {
278         indexPlus = str.find(PLUS, indexGmt);
279         int indexLocal = static_cast<int>(indexGmt);
280         GetNumFromString(str, indexGmt, &index, &hours);
281         GetNumFromString(str, indexGmt, &index, &minutes);
282         GetNumFromString(str, indexGmt, &index, &seconds);
283         GetNumFromString(str, len, &indexLocal, &localTime);
284         localHours = localTime / HUNDRED;
285         localMinutes = localTime % HUNDRED;
286         localMs = static_cast<int64_t>(MakeTime(localHours, localMinutes, 0, 0));
287         if (indexPlus != CString::npos) {
288             localMs = -localMs;
289         }
290     }
291     double day = MakeDay(year, month, date);
292     double time = MakeTime(hours, minutes, seconds, ms);
293     double timeValue = TimeClip(MakeDate(day, time));
294     if (std::isnan(timeValue)) {
295         return JSTaggedValue(timeValue);
296     }
297     if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) {
298         timeValue += static_cast<double>(localMs - CHINA_BEFORE_1901_MS);
299     } else {
300         timeValue += localMs;
301     }
302     return JSTaggedValue(timeValue);
303 }
304 
305 // static
UtcParseStringToMs(const CString & str)306 JSTaggedValue JSDate::UtcParseStringToMs(const CString &str)
307 {
308     int year = 0;
309     int month = 0;
310     int date = 1;
311     int hours = 0;
312     int minutes = 0;
313     int seconds = 0;
314     int ms = 0;
315     int index = 0;
316     int len = str.length();
317     CString::size_type indexGmt;
318     CString::size_type indexPlus = CString::npos;
319     int localTime = 0;
320     int localHours = 0;
321     int localMinutes = 0;
322     int64_t localMs = 0;
323     bool isLocal = false;
324     std::array<CString, MOUTH_PER_YEAR> monthName = {
325         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
326         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
327     };
328     GetNumFromString(str, len, &index, &date);
329     CString strMonth = str.substr(index + 1, LENGTH_MONTH_NAME);
330     for (int i = 0; i < MOUTH_PER_YEAR; i++) {
331         if (strMonth == monthName[i]) {
332             month = i;
333             break;
334         }
335     }
336     index += (LENGTH_MONTH_NAME + 1);
337     GetNumFromString(str, len, &index, &year);
338     indexGmt = str.find("GMT", index);
339     if (indexGmt == CString::npos) {
340         GetNumFromString(str, len, &index, &hours);
341         GetNumFromString(str, len, &index, &minutes);
342         GetNumFromString(str, len, &index, &seconds);
343         isLocal = true;
344         localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE);
345     } else {
346         indexPlus = str.find(PLUS, indexGmt);
347         int indexLocal = static_cast<int>(indexGmt);
348         GetNumFromString(str, indexGmt, &index, &hours);
349         GetNumFromString(str, indexGmt, &index, &minutes);
350         GetNumFromString(str, indexGmt, &index, &seconds);
351         GetNumFromString(str, len, &indexLocal, &localTime);
352         localHours = localTime / HUNDRED;
353         localMinutes = localTime % HUNDRED;
354         localMs = static_cast<int64_t>(MakeTime(localHours, localMinutes, 0, 0));
355         if (indexPlus != CString::npos) {
356             localMs = -localMs;
357         }
358     }
359     double day = MakeDay(year, month, date);
360     double time = MakeTime(hours, minutes, seconds, ms);
361     double timeValue = TimeClip(MakeDate(day, time));
362     if (std::isnan(timeValue)) {
363         return JSTaggedValue(timeValue);
364     }
365     if (isLocal && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) {
366         timeValue += static_cast<double>(localMs - CHINA_BEFORE_1901_MS);
367     } else {
368         timeValue += localMs;
369     }
370     return JSTaggedValue(timeValue);
371 }
372 // static
IsoParseStringToMs(const CString & str)373 JSTaggedValue JSDate::IsoParseStringToMs(const CString &str)
374 {
375     char flag = 0;
376     int year;
377     int month = 1;
378     int date = 1;
379     int hours = 0;
380     int minutes = 0;
381     int seconds = 0;
382     int ms = 0;
383     int index = 0;
384     int len = str.length();
385     year = GetSignedNumFromString(str, len, &index);
386     CString::size_type indexT = str.find(FLAG_TIME, index);
387     CString::size_type indexZ = str.find(FLAG_UTC, index);
388     CString::size_type indexEndFlag = 0;
389     int64_t localMs = 0;
390     if (indexZ != CString::npos) {
391         indexEndFlag = indexZ;
392     } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == NEG) {
393         indexEndFlag = len - INDEX_PLUS_NEG;
394         flag = NEG;
395     } else if (len >= MIN_LENGTH && str.at(len - INDEX_PLUS_NEG) == PLUS) {
396         indexEndFlag = len - INDEX_PLUS_NEG;
397         flag = PLUS;
398     }
399     if (indexT != CString::npos) {
400         if ((indexT - index) == LENGTH_PER_TIME) {
401             GetNumFromString(str, len, &index, &month);
402         } else if ((indexT - index) == (LENGTH_PER_TIME + LENGTH_PER_TIME)) {
403             GetNumFromString(str, len, &index, &month);
404             GetNumFromString(str, len, &index, &date);
405         }
406         GetNumFromString(str, len, &index, &hours);
407         GetNumFromString(str, len, &index, &minutes);
408         if (indexEndFlag > 0) {
409             if (indexEndFlag - index == LENGTH_PER_TIME) {
410                 GetNumFromString(str, len, &index, &seconds);
411             } else if (indexEndFlag - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) {
412                 GetNumFromString(str, len, &index, &seconds);
413                 GetNumFromString(str, len, &index, &ms);
414             }
415         } else {
416             if (len - index == LENGTH_PER_TIME) {
417                 GetNumFromString(str, len, &index, &seconds);
418             } else if (len - index == (LENGTH_PER_TIME + LENGTH_PER_TIME + 1)) {
419                 GetNumFromString(str, len, &index, &seconds);
420                 GetNumFromString(str, len, &index, &ms);
421             }
422         }
423     } else {
424         GetNumFromString(str, len, &index, &month);
425         GetNumFromString(str, len, &index, &date);
426     }
427     if (indexEndFlag > 0) {
428         int localHours = 0;
429         int localMinutes = 0;
430         if (indexZ == CString::npos) {
431             GetNumFromString(str, len, &index, &localHours);
432             GetNumFromString(str, len, &index, &localMinutes);
433             if (flag == PLUS) {
434                 localMs = static_cast<int64_t>(-MakeTime(localHours, localMinutes, 0, 0));
435             } else {
436                 localMs = static_cast<int64_t>(MakeTime(localHours, localMinutes, 0, 0));
437             }
438         }
439     }
440     if (indexEndFlag == 0 && indexT != CString::npos) {
441         localMs -= (GetLocalOffsetFromOS(localMs, true) * MS_PER_MINUTE);
442     }
443 
444     double day = MakeDay(year, month - 1, date);
445     double time = MakeTime(hours, minutes, seconds, ms);
446     double timeValue = TimeClip(MakeDate(day, time));
447     if (std::isnan(timeValue)) {
448         return JSTaggedValue(timeValue);
449     }
450     if (flag == 0 && timeValue < CHINA_1901_MS && (-localMs / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) {
451         timeValue += static_cast<double>(localMs - CHINA_BEFORE_1901_MS);
452     } else {
453         timeValue += localMs;
454     }
455     return JSTaggedValue(timeValue);
456 }
457 
458 // 20.4.3.2 static
Parse(EcmaRuntimeCallInfo * argv)459 JSTaggedValue JSDate::Parse(EcmaRuntimeCallInfo *argv)
460 {
461     ASSERT(argv);
462     JSThread *thread = argv->GetThread();
463     const CString isoPriStr = "(^|(\\+|-)(\\d{2}))";
464     const CString isoDateStr =
465         "(((\\d{4})-(0?[1-9]|1[0-2])-(0?[1-9]|1[0-9]|2[0-9]|3[0-1]))"
466         "|((\\d{4})-(0?[1-9]|1[0-2]))|(\\d{4}))";
467     const CString isoTimeStr =
468         "((T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])"
469         "\\.(\\d{3}))|(T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))|"
470         "(T([01][0-9]|2[0-3]):([0-5][0-9]))|(T([01][0-9]|2[0-3])))"
471         "($|Z|((\\+|-)(([01][0-9]|2[0-3]):([0-5][0-9]))))";
472     const CString isoRegStr = isoPriStr + isoDateStr + "($|Z|(" + isoTimeStr + "))";
473     const CString utcDateStr =
474         "^\\D*(0?[1-9]|1[0-9]|2[0-9]|3[0-1]) "
475         "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\\d{4})";
476     const CString timeStr =
477         "(( ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))|( ([01][0-9]|2[0-3]):([0-5][0-9])))"
478         "($| *| GMT *| GMT((\\+|-)(\\d{4})) *)";
479     const CString utcRegStr = utcDateStr + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *|(" + timeStr + "))";
480     const CString localDateStr =
481         "^[a-zA-Z]* (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) "
482         "(0?[1-9]|1[0-9]|2[0-9]|3[0-1]) (\\d{4})";
483     const CString localRegStr = localDateStr + "($| *| GMT *| GMT((\\+|-)(\\d{4})) *|(" + timeStr + "))";
484 
485     std::regex isoReg(isoRegStr);
486     std::regex utcReg(utcRegStr);
487     std::regex localReg(localRegStr);
488     JSHandle<JSTaggedValue> msg = base::BuiltinsBase::GetCallArg(argv, 0);
489     JSHandle<JSTaggedValue> str = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, msg));
490     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
491     CString date = ConvertToString(EcmaString::Cast(str->GetTaggedObject()));
492     if (std::regex_match(date, isoReg)) {
493         return IsoParseStringToMs(date);
494     }
495     if (std::regex_match(date, utcReg)) {
496         return UtcParseStringToMs(date);
497     }
498     if (std::regex_match(date, localReg)) {
499         return LocalParseStringToMs(date);
500     }
501     return JSTaggedValue(base::NAN_VALUE);
502 }
503 
504 // 20.4.3.1
Now()505 JSTaggedValue JSDate::Now()
506 {
507     // time from now is in ms.
508     int64_t ans;
509     struct timeval tv {
510     };
511     gettimeofday(&tv, nullptr);
512     ans = static_cast<int64_t>(tv.tv_sec) * MS_PER_SECOND + (tv.tv_usec / MS_PER_SECOND);
513     return JSTaggedValue(static_cast<double>(ans));
514 }
515 
516 // 20.4.4.2 static
UTC(EcmaRuntimeCallInfo * argv)517 JSTaggedValue JSDate::UTC(EcmaRuntimeCallInfo *argv)
518 {
519     double year;
520     double month = 0;
521     double date = 1;
522     double hours = 0;
523     double minutes = 0;
524     double seconds = 0;
525     double ms = 0;
526     JSThread *thread = argv->GetThread();
527     JSHandle<JSTaggedValue> yearArg = base::BuiltinsBase::GetCallArg(argv, 0);
528     JSTaggedNumber yearValue = JSTaggedValue::ToNumber(thread, yearArg);
529     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
530     if (yearValue.IsNumber()) {
531         year = yearValue.GetNumber();
532         if (std::isfinite(year) && !yearValue.IsInt()) {
533             year = NumberHelper::TruncateDouble(year);
534         }
535         if (year >= 0 && year <= (HUNDRED - 1)) {
536             year = year + NINETEEN_HUNDRED_YEAR;
537         }
538     } else {
539         year = base::NAN_VALUE;
540     }
541     uint32_t index = 1;
542     uint32_t numArgs = argv->GetArgsNumber();
543     JSTaggedValue res;
544     if (numArgs > index) {
545         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index);
546         res = JSTaggedValue::ToNumber(thread, value);
547         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
548         month = res.GetNumber();
549         index++;
550     }
551     if (numArgs > index) {
552         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index);
553         res = JSTaggedValue::ToNumber(thread, value);
554         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
555         date = res.GetNumber();
556         index++;
557     }
558     if (numArgs > index) {
559         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index);
560         res = JSTaggedValue::ToNumber(thread, value);
561         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
562         hours = res.GetNumber();
563         index++;
564     }
565     if (numArgs > index) {
566         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index);
567         res = JSTaggedValue::ToNumber(thread, value);
568         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
569         minutes = res.GetNumber();
570         index++;
571     }
572     if (numArgs > index) {
573         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index);
574         res = JSTaggedValue::ToNumber(thread, value);
575         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
576         seconds = res.GetNumber();
577         index++;
578     }
579     if (numArgs > index) {
580         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, index);
581         res = JSTaggedValue::ToNumber(thread, value);
582         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
583         ms = res.GetNumber();
584     }
585     double day = MakeDay(year, month, date);
586     double time = MakeTime(hours, minutes, seconds, ms);
587     return JSTaggedValue(TimeClip(MakeDate(day, time)));
588 }
589 
GetLocalOffsetFromOS(int64_t timeMs,bool isLocal)590 int64_t JSDate::GetLocalOffsetFromOS(int64_t timeMs, bool isLocal)
591 {
592     // Preserve the old behavior for non-ICU implementation by ignoring both timeMs and is_utc.
593     if (!isLocal) {
594         return 0;
595     }
596     timeMs /= JSDate::THOUSAND;
597     time_t tv = std::time(reinterpret_cast<time_t *>(&timeMs));
598     struct tm tm {
599     };
600     // localtime_r is only suitable for linux.
601     struct tm *t = localtime_r(&tv, &tm);
602     // tm_gmtoff includes any daylight savings offset.
603     return t->tm_gmtoff / SEC_PER_MINUTE;
604 }
605 
606 // 20.4.4.10
GetTime() const607 JSTaggedValue JSDate::GetTime() const
608 {
609     return GetTimeValue();
610 }
611 
612 // static
StrToTargetLength(const CString & str,int length)613 CString JSDate::StrToTargetLength(const CString &str, int length)
614 {
615     int len;
616     if (str[0] == NEG) {
617         len = static_cast<int>(str.length() - 1);
618     } else {
619         len = static_cast<int>(str.length());
620     }
621     int dif = length - len;
622     CString sub;
623     for (int i = 0; i < dif; i++) {
624         sub += '0';
625     }
626     if (str[0] == NEG) {
627         sub = NEG + sub + str.substr(1, len);
628     } else {
629         sub = sub + str;
630     }
631     return sub;
632 }
633 
GetThisDateValues(std::array<int64_t,DATE_LENGTH> * date,bool isLocal) const634 bool JSDate::GetThisDateValues(std::array<int64_t, DATE_LENGTH> *date, bool isLocal) const
635 {
636     double timeMs = this->GetTimeValue().GetDouble();
637     if (std::isnan(timeMs)) {
638         return false;
639     }
640     GetDateValues(timeMs, date, isLocal);
641     return true;
642 }
643 
644 // 20.4.4.35
ToDateString(JSThread * thread) const645 JSTaggedValue JSDate::ToDateString(JSThread *thread) const
646 {
647     std::array<CString, MOUTH_PER_YEAR> monthName = {
648         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
649         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
650     };
651     std::array<CString, DAY_PER_WEEK> weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
652     std::array<int64_t, DATE_LENGTH> fields = {0};
653     if (!GetThisDateValues(&fields, true)) {
654         return JSTaggedValue(base::NAN_VALUE);
655     }
656     CString year = StrToTargetLength(ToCString(fields[YEAR]), STR_LENGTH_YEAR);
657     CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS);
658     CString str = weekdayName[fields[WEEKDAY]] + SPACE + monthName[fields[MONTH]] + SPACE + day + SPACE + year;
659     JSHandle<EcmaString> result = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str);
660     return result.GetTaggedValue();
661 }
662 
663 // static
ToDateString(double timeMs)664 CString JSDate::ToDateString(double timeMs)
665 {
666     if (std::isnan(timeMs)) {
667         return "Invalid Date";
668     }
669     std::array<CString, MOUTH_PER_YEAR> monthName = {
670         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
671         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
672     };
673     std::array<CString, DAY_PER_WEEK> weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
674     std::array<int64_t, DATE_LENGTH> fields = {0};
675     GetDateValues(timeMs, &fields, true);
676     CString localTime;
677     int localMin = 0;
678     localMin = GetLocalOffsetFromOS(localMin, true);
679     if (timeMs < CHINA_BEFORE_1900_MS && localMin == CHINA_AFTER_1901_MIN) {
680         localMin = CHINA_BEFORE_1901_MIN;
681     }
682     if (localMin >= 0) {
683         localTime += PLUS;
684     } else if (localMin < 0) {
685         localTime += NEG;
686         localMin = -localMin;
687     }
688     localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS);
689     localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS);
690     CString year = ToCString(fields[YEAR]);
691     year = StrToTargetLength(year, STR_LENGTH_YEAR);
692     CString weekday = weekdayName[fields[WEEKDAY]];
693     CString month = monthName[fields[MONTH]];
694     CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS);
695     CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS);
696     CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS);
697     CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS);
698     CString str = weekday + SPACE + month + SPACE + day + SPACE + year + SPACE + hour + COLON + minute + COLON +
699                   second + SPACE + "GMT" + localTime;
700     return str;
701 }
702 // 20.4.4.36
ToISOString(JSThread * thread) const703 JSTaggedValue JSDate::ToISOString(JSThread *thread) const
704 {
705     std::array<int64_t, DATE_LENGTH> fields = {0};
706     if (!GetThisDateValues(&fields, false)) {
707         return JSTaggedValue(base::NAN_VALUE);
708     }
709     CString year = ToCString(fields[YEAR]);
710     if (year[0] == NEG) {
711         year = StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS);
712     } else if (year.length() > STR_LENGTH_YEAR) {
713         year = PLUS + StrToTargetLength(year, STR_LENGTH_YEAR + STR_LENGTH_OTHERS);
714     } else {
715         year = StrToTargetLength(year, STR_LENGTH_YEAR);
716     }
717     CString month = StrToTargetLength(ToCString(fields[MONTH] + 1), STR_LENGTH_OTHERS);
718     CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS);
719     CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS);
720     CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS);
721     CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS);
722     CString ms = StrToTargetLength(ToCString(fields[MS]), STR_LENGTH_OTHERS + 1);
723     CString str =
724         year + NEG + month + NEG + day + FLAG_TIME + hour + COLON + minute + COLON + second + POINT + ms + FLAG_UTC;
725     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue();
726 }
727 
GetLocaleTimeStr(const std::array<int64_t,DATE_LENGTH> & fields) const728 CString JSDate::GetLocaleTimeStr(const std::array<int64_t, DATE_LENGTH> &fields) const
729 {
730     CString hour;
731     if (fields[HOUR] > MOUTH_PER_YEAR) {
732         hour = ToCString(fields[HOUR] - MOUTH_PER_YEAR);
733     } else {
734         hour = ToCString(fields[HOUR]);
735     }
736     CString minute = ToCString(fields[MIN]);
737     CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS);
738     CString str = hour + COLON + minute + COLON + second;
739     if (fields[HOUR] >= MOUTH_PER_YEAR) {
740         str = "下午" + str;
741     } else {
742         str = "上午" + str;
743     }
744     return str;
745 }
746 
GetLocaleDateStr(const std::array<int64_t,DATE_LENGTH> & fields) const747 CString JSDate::GetLocaleDateStr(const std::array<int64_t, DATE_LENGTH> &fields) const
748 {
749     CString year;
750     if (fields[YEAR] < 0) {
751         year = ToCString(-fields[YEAR] + 1);
752     } else {
753         year = ToCString(fields[YEAR]);
754     }
755     CString month = ToCString(fields[MONTH] + 1);
756     CString day = ToCString(fields[DAYS]);
757     CString str = year + VIRGULE + month + VIRGULE + day;
758     return str;
759 }
760 
761 // 20.4.4.38
ToLocaleDateString(JSThread * thread) const762 JSTaggedValue JSDate::ToLocaleDateString(JSThread *thread) const
763 {
764     std::array<int64_t, DATE_LENGTH> fields = {0};
765     if (!GetThisDateValues(&fields, true)) {
766         return JSTaggedValue(base::NAN_VALUE);
767     }
768     CString str = GetLocaleDateStr(fields);
769     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue();
770 }
771 
772 // 20.4.4.39
ToLocaleString(JSThread * thread) const773 JSTaggedValue JSDate::ToLocaleString(JSThread *thread) const
774 {
775     std::array<int64_t, DATE_LENGTH> fields = {0};
776     if (!GetThisDateValues(&fields, true)) {
777         return JSTaggedValue(base::NAN_VALUE);
778     }
779     CString strDate = GetLocaleDateStr(fields);
780     CString strTime = GetLocaleTimeStr(fields);
781     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(strDate + SPACE + strTime).GetTaggedValue();
782 }
783 
784 // 20.4.4.40
ToLocaleTimeString(JSThread * thread) const785 JSTaggedValue JSDate::ToLocaleTimeString(JSThread *thread) const
786 {
787     std::array<int64_t, DATE_LENGTH> fields = {0};
788     if (!GetThisDateValues(&fields, true)) {
789         return JSTaggedValue(base::NAN_VALUE);
790     }
791     CString str = GetLocaleTimeStr(fields);
792     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue();
793 }
794 
795 // 20.4.4.41
ToString(JSThread * thread) const796 JSTaggedValue JSDate::ToString(JSThread *thread) const
797 {
798     std::array<CString, DAY_PER_WEEK> weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
799     std::array<CString, MOUTH_PER_YEAR> monthName = {
800         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
801         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
802     };
803     int localMin = 0;
804     std::array<int64_t, DATE_LENGTH> fields = {0};
805     if (!GetThisDateValues(&fields, true)) {
806         return JSTaggedValue(base::NAN_VALUE);
807     }
808     CString localTime;
809     localMin = GetLocalOffsetFromOS(localMin, true);
810     if (static_cast<int64_t>(this->GetTimeValue().GetDouble()) < CHINA_BEFORE_1900_MS &&
811         localMin == CHINA_AFTER_1901_MIN) {
812         localMin = CHINA_BEFORE_1901_MIN;
813     }
814     if (localMin >= 0) {
815         localTime += PLUS;
816     } else if (localMin < 0) {
817         localTime += NEG;
818         localMin = -localMin;
819     }
820     localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS);
821     localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS);
822     CString year = ToCString(fields[YEAR]);
823     year = StrToTargetLength(year, STR_LENGTH_YEAR);
824     CString weekday = weekdayName[fields[WEEKDAY]];
825     CString month = monthName[fields[MONTH]];
826     CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS);
827     CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS);
828     CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS);
829     CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS);
830     CString str = weekday + SPACE + month + SPACE + day + SPACE + year + SPACE + hour + COLON + minute + COLON +
831                   second + SPACE + "GMT" + localTime;
832     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue();
833 }
834 
835 // 20.4.4.42
ToTimeString(JSThread * thread) const836 JSTaggedValue JSDate::ToTimeString(JSThread *thread) const
837 {
838     int localMin = 0;
839     std::array<int64_t, DATE_LENGTH> fields = {0};
840     if (!GetThisDateValues(&fields, true)) {
841         return JSTaggedValue(base::NAN_VALUE);
842     }
843     CString localTime;
844     localMin = GetLocalOffsetFromOS(localMin, true);
845     if (static_cast<int64_t>(this->GetTimeValue().GetDouble()) < CHINA_BEFORE_1900_MS &&
846         localMin == CHINA_AFTER_1901_MIN) {
847         localMin = CHINA_BEFORE_1901_MIN;
848     }
849     if (localMin >= 0) {
850         localTime += PLUS;
851     } else {
852         localTime += NEG;
853         localMin = -localMin;
854     }
855     localTime = localTime + StrToTargetLength(ToCString(localMin / MINUTE_PER_HOUR), STR_LENGTH_OTHERS);
856     localTime = localTime + StrToTargetLength(ToCString(localMin % MINUTE_PER_HOUR), STR_LENGTH_OTHERS);
857     CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS);
858     CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS);
859     CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS);
860     CString str = hour + COLON + minute + COLON + second + SPACE + "GMT" + localTime;
861     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue();
862 }
863 
864 // 20.4.4.43
ToUTCString(JSThread * thread) const865 JSTaggedValue JSDate::ToUTCString(JSThread *thread) const
866 {
867     std::array<CString, DAY_PER_WEEK> weekdayName = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
868     std::array<CString, MOUTH_PER_YEAR> monthName = {
869         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
870         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
871     };
872     std::array<int64_t, DATE_LENGTH> fields = {0};
873     if (!GetThisDateValues(&fields, false)) {
874         return JSTaggedValue(base::NAN_VALUE);
875     }
876     CString year = ToCString(fields[YEAR]);
877     year = StrToTargetLength(year, STR_LENGTH_YEAR);
878     CString weekday = weekdayName[fields[WEEKDAY]];
879     CString month = monthName[fields[MONTH]];
880     CString day = StrToTargetLength(ToCString(fields[DAYS]), STR_LENGTH_OTHERS);
881     CString hour = StrToTargetLength(ToCString(fields[HOUR]), STR_LENGTH_OTHERS);
882     CString minute = StrToTargetLength(ToCString(fields[MIN]), STR_LENGTH_OTHERS);
883     CString second = StrToTargetLength(ToCString(fields[SEC]), STR_LENGTH_OTHERS);
884     CString ms = StrToTargetLength(ToCString(fields[MS]), STR_LENGTH_OTHERS);
885     CString str = weekday + COMMA + SPACE + day + SPACE + month + SPACE + year + SPACE + hour + COLON + minute + COLON +
886                   second + SPACE + "GMT";
887     return thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(str).GetTaggedValue();
888 }
889 
890 // 20.4.4.44
ValueOf() const891 JSTaggedValue JSDate::ValueOf() const
892 {
893     return this->GetTimeValue();
894 }
895 
896 // static
GetDateValues(double timeMs,std::array<int64_t,DATE_LENGTH> * date,bool isLocal)897 void JSDate::GetDateValues(double timeMs, std::array<int64_t, DATE_LENGTH> *date, bool isLocal)
898 {
899     int64_t tz = 0;
900     int64_t timeMsInt;
901     int month = 0;
902     timeMsInt = static_cast<int64_t>(timeMs);
903     if (isLocal) {  // timezone offset
904         tz = GetLocalOffsetFromOS(timeMsInt, isLocal);
905         if (timeMsInt < CHINA_BEFORE_1900_MS && tz == CHINA_AFTER_1901_MIN) {
906             timeMsInt += CHINA_BEFORE_1901_ADDMS;
907             timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE;
908             tz = CHINA_BEFORE_1901_MIN;
909         } else {
910             timeMsInt += tz * MS_PER_SECOND * SEC_PER_MINUTE;
911         }
912     }
913 
914     DateUtils::TransferTimeToDate(timeMsInt, date);
915 
916     int index = DateUtils::IsLeap((*date)[YEAR]) ? 1 : 0;
917     int left = 0;
918     int right = MONTH_PER_YEAR;
919     while (left <= right) {
920         int middle = (left + right) / 2; // 2 : half
921         if (DAYS_FROM_MONTH[index][middle] <= (*date)[DAYS] && DAYS_FROM_MONTH[index][middle + 1] > (*date)[DAYS]) {
922             month = middle;
923             (*date)[DAYS] -= DAYS_FROM_MONTH[index][month];
924             break;
925         } else if ((*date)[DAYS] > DAYS_FROM_MONTH[index][middle]) { // NOLINT(readability-else-after-return)
926             left = middle + 1;
927         } else if ((*date)[DAYS] < DAYS_FROM_MONTH[index][middle]) {
928             right = middle - 1;
929         }
930     }
931 
932     (*date)[MONTH] = month;
933     (*date)[DAYS] = (*date)[DAYS] + 1;
934     (*date)[TIMEZONE] = -tz;
935 }
936 
GetDateValue(double timeMs,uint8_t code,bool isLocal) const937 double JSDate::GetDateValue(double timeMs, uint8_t code, bool isLocal) const
938 {
939     if (std::isnan(timeMs)) {
940         return base::NAN_VALUE;
941     }
942     std::array<int64_t, DATE_LENGTH> date = {0};
943     GetDateValues(timeMs, &date, isLocal);
944     return static_cast<double>(date[code]);
945 }
946 
SetDateValue(EcmaRuntimeCallInfo * argv,uint32_t code,bool isLocal) const947 JSTaggedValue JSDate::SetDateValue(EcmaRuntimeCallInfo *argv, uint32_t code, bool isLocal) const
948 {
949     // get date values.
950     std::array<int64_t, DATE_LENGTH> date = {0};
951     double timeMs = this->GetTimeValue().GetDouble();
952 
953     // get values from argv.
954     uint32_t argc = argv->GetArgsNumber();
955     if (argc == 0) {
956         return JSTaggedValue(base::NAN_VALUE);
957     }
958 
959     uint32_t firstValue = code & CODE_FLAG;
960     uint32_t endValue = (code >> CODE_4_BIT) & CODE_FLAG;
961     uint32_t count = endValue - firstValue;
962 
963     if (argc < count) {
964         count = argc;
965     }
966 
967     if (std::isnan(timeMs) && firstValue == 0) {
968         timeMs = 0.0;
969         GetDateValues(timeMs, &date, false);
970     } else {
971         GetDateValues(timeMs, &date, isLocal);
972     }
973 
974     for (uint32_t i = 0; i < count; i++) {
975         JSHandle<JSTaggedValue> value = base::BuiltinsBase::GetCallArg(argv, i);
976         JSThread *thread = argv->GetThread();
977         JSTaggedNumber res = JSTaggedValue::ToNumber(thread, value);
978         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
979         double temp = res.GetNumber();
980         if (std::isnan(temp)) {
981             return JSTaggedValue(base::NAN_VALUE);
982         }
983         date[firstValue + i] = NumberHelper::TruncateDouble(temp);
984     }
985     // set date values.
986     return JSTaggedValue(SetDateValues(&date, isLocal));
987 }
988 
989 // static
SetDateValues(const std::array<int64_t,DATE_LENGTH> * date,bool isLocal)990 double JSDate::SetDateValues(const std::array<int64_t, DATE_LENGTH> *date, bool isLocal)
991 {
992     int64_t month = DateUtils::Mod((*date)[MONTH], MONTH_PER_YEAR);
993     int64_t year = (*date)[YEAR] + ((*date)[MONTH] - month) / MONTH_PER_YEAR;
994     int64_t days = DateUtils::GetDaysFromYear(year);
995     int index = DateUtils::IsLeap(year) ? 1 : 0;
996     days += DAYS_FROM_MONTH[index][month];
997 
998     days += (*date)[DAYS] - 1;
999     int64_t millisecond =
1000         (((*date)[HOUR] * MIN_PER_HOUR + (*date)[MIN]) * SEC_PER_MINUTE + (*date)[SEC]) * MS_PER_SECOND + (*date)[MS];
1001     int64_t result = days * MS_PER_DAY + millisecond;
1002     if (isLocal) {
1003         int64_t offset = GetLocalOffsetFromOS(result, isLocal) * SEC_PER_MINUTE * MS_PER_SECOND;
1004         if (result < CHINA_1901_MS && (offset / MS_PER_MINUTE) == CHINA_AFTER_1901_MIN) {
1005             offset += CHINA_BEFORE_1901_ADDMS;
1006         }
1007         result -= offset;
1008     }
1009     return TimeClip(result);
1010 }
1011 }  // namespace panda::ecmascript
1012