mirror of
https://github.com/bellard/quickjs.git
synced 2024-11-21 21:28:12 +08:00
Improve Date.parse, small fixes
- add `minimum_length` to enforce array length validation - add `JS_NewDate()` API - add `[Symbol.toStringTag]` property in the global object - simplify `string_get_milliseconds` - support more timezone abbrevs using `string_get_tzabbr` and array
This commit is contained in:
parent
6a89d7c270
commit
65ecb0b0d6
6
cutils.h
6
cutils.h
@ -51,6 +51,12 @@
|
|||||||
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
|
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||||
|
#define minimum_length(n) static n
|
||||||
|
#else
|
||||||
|
#define minimum_length(n) n
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef int BOOL;
|
typedef int BOOL;
|
||||||
|
|
||||||
#ifndef FALSE
|
#ifndef FALSE
|
||||||
|
131
quickjs.c
131
quickjs.c
@ -968,6 +968,7 @@ struct JSObject {
|
|||||||
} u;
|
} u;
|
||||||
/* byte sizes: 40/48/72 */
|
/* byte sizes: 40/48/72 */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
__JS_ATOM_NULL = JS_ATOM_NULL,
|
__JS_ATOM_NULL = JS_ATOM_NULL,
|
||||||
#define DEF(name, str) JS_ATOM_ ## name,
|
#define DEF(name, str) JS_ATOM_ ## name,
|
||||||
@ -1103,6 +1104,12 @@ static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
|
|||||||
static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
|
static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
|
||||||
JS_MarkFunc *mark_func);
|
JS_MarkFunc *mark_func);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define HINT_STRING 0
|
||||||
|
#define HINT_NUMBER 1
|
||||||
|
#define HINT_NONE 2
|
||||||
|
#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive
|
||||||
|
static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint);
|
||||||
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
|
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
|
||||||
static int JS_ToBoolFree(JSContext *ctx, JSValue val);
|
static int JS_ToBoolFree(JSContext *ctx, JSValue val);
|
||||||
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
|
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
|
||||||
@ -9848,12 +9855,6 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HINT_STRING 0
|
|
||||||
#define HINT_NUMBER 1
|
|
||||||
#define HINT_NONE 2
|
|
||||||
/* don't try Symbol.toPrimitive */
|
|
||||||
#define HINT_FORCE_ORDINARY (1 << 4)
|
|
||||||
|
|
||||||
static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
|
static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -49404,6 +49405,7 @@ static const JSCFunctionListEntry js_global_funcs[] = {
|
|||||||
JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
|
JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
|
||||||
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
|
JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
|
||||||
JS_PROP_UNDEFINED_DEF("undefined", 0 ),
|
JS_PROP_UNDEFINED_DEF("undefined", 0 ),
|
||||||
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Date */
|
/* Date */
|
||||||
@ -49484,7 +49486,7 @@ static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
|||||||
static char const day_names[] = "SunMonTueWedThuFriSat";
|
static char const day_names[] = "SunMonTueWedThuFriSat";
|
||||||
|
|
||||||
static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
|
static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
|
||||||
double fields[9], int is_local, int force)
|
double fields[minimum_length(9)], int is_local, int force)
|
||||||
{
|
{
|
||||||
double dval;
|
double dval;
|
||||||
int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
|
int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
|
||||||
@ -49497,7 +49499,7 @@ static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
|
|||||||
return FALSE; /* NaN */
|
return FALSE; /* NaN */
|
||||||
d = 0; /* initialize all fields to 0 */
|
d = 0; /* initialize all fields to 0 */
|
||||||
} else {
|
} else {
|
||||||
d = dval;
|
d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */
|
||||||
if (is_local) {
|
if (is_local) {
|
||||||
tz = -getTimezoneOffset(d);
|
tz = -getTimezoneOffset(d);
|
||||||
d += tz * 60000;
|
d += tz * 60000;
|
||||||
@ -49545,7 +49547,7 @@ static double time_clip(double t) {
|
|||||||
|
|
||||||
/* The spec mandates the use of 'double' and it specifies the order
|
/* The spec mandates the use of 'double' and it specifies the order
|
||||||
of the operations */
|
of the operations */
|
||||||
static double set_date_fields(double fields[], int is_local) {
|
static double set_date_fields(double fields[minimum_length(7)], int is_local) {
|
||||||
double y, m, dt, ym, mn, day, h, s, milli, time, tv;
|
double y, m, dt, ym, mn, day, h, s, milli, time, tv;
|
||||||
int yi, mi, i;
|
int yi, mi, i;
|
||||||
int64_t days;
|
int64_t days;
|
||||||
@ -49649,9 +49651,9 @@ static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
|
|||||||
res = FALSE;
|
res = FALSE;
|
||||||
fields[first_field + i] = trunc(a);
|
fields[first_field + i] = trunc(a);
|
||||||
}
|
}
|
||||||
if (res && argc > 0) {
|
if (res && argc > 0)
|
||||||
d = set_date_fields(fields, is_local);
|
d = set_date_fields(fields, is_local);
|
||||||
}
|
|
||||||
return JS_SetThisTimeValue(ctx, this_val, d);
|
return JS_SetThisTimeValue(ctx, this_val, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49856,7 +49858,7 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
|
|||||||
int argc, JSValueConst *argv)
|
int argc, JSValueConst *argv)
|
||||||
{
|
{
|
||||||
// UTC(y, mon, d, h, m, s, ms)
|
// UTC(y, mon, d, h, m, s, ms)
|
||||||
double fields[9] = { 0, 0, 1, 0, 0, 0, 0, 0, 0 };
|
double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
|
||||||
int i, n;
|
int i, n;
|
||||||
double a;
|
double a;
|
||||||
|
|
||||||
@ -49936,14 +49938,15 @@ static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
|
|||||||
static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
|
static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
|
||||||
/* parse optional fractional part as milliseconds and truncate. */
|
/* parse optional fractional part as milliseconds and truncate. */
|
||||||
/* spec does not indicate which rounding should be used */
|
/* spec does not indicate which rounding should be used */
|
||||||
int mul = 1000, ms = 0, c, p_start, p = *pp;
|
int mul = 100, ms = 0, c, p_start, p = *pp;
|
||||||
|
|
||||||
c = sp[p];
|
c = sp[p];
|
||||||
if (c == '.' || c == ',') {
|
if (c == '.' || c == ',') {
|
||||||
p++;
|
p++;
|
||||||
p_start = p;
|
p_start = p;
|
||||||
while ((c = sp[p]) >= '0' && c <= '9') {
|
while ((c = sp[p]) >= '0' && c <= '9') {
|
||||||
ms += (c - '0') * (mul /= 10);
|
ms += (c - '0') * mul;
|
||||||
|
mul /= 10;
|
||||||
p++;
|
p++;
|
||||||
if (p - p_start == 9)
|
if (p - p_start == 9)
|
||||||
break;
|
break;
|
||||||
@ -49957,7 +49960,11 @@ static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL string_get_timezone(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
|
static uint8_t upper_ascii(uint8_t c) {
|
||||||
|
return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
|
||||||
int tz = 0, sgn, hh, mm, p = *pp;
|
int tz = 0, sgn, hh, mm, p = *pp;
|
||||||
|
|
||||||
sgn = sp[p++];
|
sgn = sp[p++];
|
||||||
@ -49995,10 +50002,6 @@ static BOOL string_get_timezone(const uint8_t *sp, int *pp, int *tzp, BOOL stric
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t upper_ascii(uint8_t c) {
|
|
||||||
return c >= 'a' && c <= 'z' ? c - 'a' + 'Z' : c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
|
static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
|
||||||
int p = *pp;
|
int p = *pp;
|
||||||
while (*s != '\0') {
|
while (*s != '\0') {
|
||||||
@ -50091,15 +50094,51 @@ static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_l
|
|||||||
/* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
|
/* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
|
||||||
if (sp[p]) {
|
if (sp[p]) {
|
||||||
*is_local = FALSE;
|
*is_local = FALSE;
|
||||||
if (!string_get_timezone(sp, &p, &fields[8], TRUE))
|
if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
/* error if extraneous characters */
|
/* error if extraneous characters */
|
||||||
return sp[p] == '\0';
|
return sp[p] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
char name[6];
|
||||||
|
int16_t offset;
|
||||||
|
} const js_tzabbr[] = {
|
||||||
|
{ "GMT", 0 }, // Greenwich Mean Time
|
||||||
|
{ "UTC", 0 }, // Coordinated Universal Time
|
||||||
|
{ "UT", 0 }, // Universal Time
|
||||||
|
{ "Z", 0 }, // Zulu Time
|
||||||
|
{ "EDT", -4 * 60 }, // Eastern Daylight Time
|
||||||
|
{ "EST", -5 * 60 }, // Eastern Standard Time
|
||||||
|
{ "CDT", -5 * 60 }, // Central Daylight Time
|
||||||
|
{ "CST", -6 * 60 }, // Central Standard Time
|
||||||
|
{ "MDT", -6 * 60 }, // Mountain Daylight Time
|
||||||
|
{ "MST", -7 * 60 }, // Mountain Standard Time
|
||||||
|
{ "PDT", -7 * 60 }, // Pacific Daylight Time
|
||||||
|
{ "PST", -8 * 60 }, // Pacific Standard Time
|
||||||
|
{ "WET", +0 * 60 }, // Western European Time
|
||||||
|
{ "WEST", +1 * 60 }, // Western European Summer Time
|
||||||
|
{ "CET", +1 * 60 }, // Central European Time
|
||||||
|
{ "CEST", +2 * 60 }, // Central European Summer Time
|
||||||
|
{ "EET", +2 * 60 }, // Eastern European Time
|
||||||
|
{ "EEST", +3 * 60 }, // Eastern European Summer Time
|
||||||
|
};
|
||||||
|
|
||||||
|
static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
|
||||||
|
for (size_t i = 0; i < countof(js_tzabbr); i++) {
|
||||||
|
if (string_match(sp, pp, js_tzabbr[i].name)) {
|
||||||
|
*offset = js_tzabbr[i].offset;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* parse toString, toUTCString and other formats */
|
/* parse toString, toUTCString and other formats */
|
||||||
static BOOL js_date_parse_otherstring(const uint8_t *sp, int fields[9], BOOL *is_local) {
|
static BOOL js_date_parse_otherstring(const uint8_t *sp,
|
||||||
|
int fields[minimum_length(9)],
|
||||||
|
BOOL *is_local) {
|
||||||
int c, i, val, p = 0, p_start;
|
int c, i, val, p = 0, p_start;
|
||||||
int num[3];
|
int num[3];
|
||||||
BOOL has_year = FALSE;
|
BOOL has_year = FALSE;
|
||||||
@ -50119,7 +50158,7 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp, int fields[9], BOOL *is
|
|||||||
while (string_skip_spaces(sp, &p)) {
|
while (string_skip_spaces(sp, &p)) {
|
||||||
p_start = p;
|
p_start = p;
|
||||||
if ((c = sp[p]) == '+' || c == '-') {
|
if ((c = sp[p]) == '+' || c == '-') {
|
||||||
if (has_time && string_get_timezone(sp, &p, &fields[8], FALSE)) {
|
if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
|
||||||
*is_local = FALSE;
|
*is_local = FALSE;
|
||||||
} else {
|
} else {
|
||||||
p++;
|
p++;
|
||||||
@ -50165,11 +50204,6 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp, int fields[9], BOOL *is
|
|||||||
has_mon = TRUE;
|
has_mon = TRUE;
|
||||||
string_skip_until(sp, &p, "0123456789 -/(");
|
string_skip_until(sp, &p, "0123456789 -/(");
|
||||||
} else
|
} else
|
||||||
if (c == 'Z') {
|
|
||||||
*is_local = FALSE;
|
|
||||||
p++;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
if (has_time && string_match(sp, &p, "PM")) {
|
if (has_time && string_match(sp, &p, "PM")) {
|
||||||
if (fields[3] < 12)
|
if (fields[3] < 12)
|
||||||
fields[3] += 12;
|
fields[3] += 12;
|
||||||
@ -50180,34 +50214,7 @@ static BOOL js_date_parse_otherstring(const uint8_t *sp, int fields[9], BOOL *is
|
|||||||
fields[3] -= 12;
|
fields[3] -= 12;
|
||||||
continue;
|
continue;
|
||||||
} else
|
} else
|
||||||
if (string_match(sp, &p, "GMT")
|
if (string_get_tzabbr(sp, &p, &fields[8])) {
|
||||||
|| string_match(sp, &p, "UTC")
|
|
||||||
|| string_match(sp, &p, "UT")) {
|
|
||||||
*is_local = FALSE;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
if (string_match(sp, &p, "EDT")) {
|
|
||||||
fields[8] = -4 * 60;
|
|
||||||
*is_local = FALSE;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
if (string_match(sp, &p, "EST") || string_match(sp, &p, "CDT")) {
|
|
||||||
fields[8] = -5 * 60;
|
|
||||||
*is_local = FALSE;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
if (string_match(sp, &p, "CST") || string_match(sp, &p, "MDT")) {
|
|
||||||
fields[8] = -6 * 60;
|
|
||||||
*is_local = FALSE;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
if (string_match(sp, &p, "MST") || string_match(sp, &p, "PDT")) {
|
|
||||||
fields[8] = -7 * 60;
|
|
||||||
*is_local = FALSE;
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
if (string_match(sp, &p, "PST")) {
|
|
||||||
fields[8] = -8 * 60;
|
|
||||||
*is_local = FALSE;
|
*is_local = FALSE;
|
||||||
continue;
|
continue;
|
||||||
} else
|
} else
|
||||||
@ -50350,9 +50357,7 @@ static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
|
|||||||
}
|
}
|
||||||
switch (hint) {
|
switch (hint) {
|
||||||
case JS_ATOM_number:
|
case JS_ATOM_number:
|
||||||
#ifdef CONFIG_BIGNUM
|
|
||||||
case JS_ATOM_integer:
|
case JS_ATOM_integer:
|
||||||
#endif
|
|
||||||
hint_num = HINT_NUMBER;
|
hint_num = HINT_NUMBER;
|
||||||
break;
|
break;
|
||||||
case JS_ATOM_string:
|
case JS_ATOM_string:
|
||||||
@ -50376,6 +50381,7 @@ static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
|
|||||||
if (isnan(v))
|
if (isnan(v))
|
||||||
return JS_NAN;
|
return JS_NAN;
|
||||||
else
|
else
|
||||||
|
/* assuming -8.64e15 <= v <= -8.64e15 */
|
||||||
return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
|
return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50514,6 +50520,15 @@ static const JSCFunctionListEntry js_date_proto_funcs[] = {
|
|||||||
JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
|
JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
|
||||||
|
{
|
||||||
|
JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
|
||||||
|
if (JS_IsException(obj))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
void JS_AddIntrinsicDate(JSContext *ctx)
|
void JS_AddIntrinsicDate(JSContext *ctx)
|
||||||
{
|
{
|
||||||
JSValueConst obj;
|
JSValueConst obj;
|
||||||
@ -52733,7 +52748,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
|||||||
/* XXX: create auto_initializer */
|
/* XXX: create auto_initializer */
|
||||||
{
|
{
|
||||||
/* initialize Array.prototype[Symbol.unscopables] */
|
/* initialize Array.prototype[Symbol.unscopables] */
|
||||||
char const unscopables[] =
|
static const char unscopables[] =
|
||||||
"copyWithin" "\0"
|
"copyWithin" "\0"
|
||||||
"entries" "\0"
|
"entries" "\0"
|
||||||
"fill" "\0"
|
"fill" "\0"
|
||||||
|
@ -724,6 +724,8 @@ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val)
|
|||||||
JSValue JS_NewArray(JSContext *ctx);
|
JSValue JS_NewArray(JSContext *ctx);
|
||||||
int JS_IsArray(JSContext *ctx, JSValueConst val);
|
int JS_IsArray(JSContext *ctx, JSValueConst val);
|
||||||
|
|
||||||
|
JSValue JS_NewDate(JSContext *ctx, double epoch_ms);
|
||||||
|
|
||||||
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
|
||||||
JSAtom prop, JSValueConst receiver,
|
JSAtom prop, JSValueConst receiver,
|
||||||
JS_BOOL throw_ref_error);
|
JS_BOOL throw_ref_error);
|
||||||
|
Loading…
Reference in New Issue
Block a user