mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 00:25:34 +08:00
Support native function descriptors and RegExp string coercion (#563)
This commit is contained in:
@@ -1615,6 +1615,8 @@ function emitStringExpr(
|
||||
return emitter.newTemp(e.type, `scr_regex_source(${r.name})`);
|
||||
case "flags":
|
||||
return emitter.newTemp(e.type, `scr_regex_flags(${r.name})`);
|
||||
case "toString":
|
||||
return emitter.newTemp(e.type, `scr_regex_to_string(${r.name})`);
|
||||
case "replace":
|
||||
return emitter.newTemp(
|
||||
e.type,
|
||||
|
||||
@@ -114,6 +114,12 @@ export function emitRegexIntrinsic(host: LlvmEmitterContext, e: IrExpr & { kind:
|
||||
B.line(`${t} = call ptr @scr_regex_flags(ptr ${r.name})`);
|
||||
return host.own({ name: t, type: e.type });
|
||||
}
|
||||
case "toString": {
|
||||
host.declare(`declare ptr @scr_regex_to_string(ptr)`);
|
||||
const t = B.tmp();
|
||||
B.line(`${t} = call ptr @scr_regex_to_string(ptr ${r.name})`);
|
||||
return host.own({ name: t, type: e.type });
|
||||
}
|
||||
case "replace": {
|
||||
host.declare(`declare ptr @scr_regex_replace(ptr, ptr, ptr)`);
|
||||
const t = B.tmp();
|
||||
|
||||
@@ -5532,6 +5532,9 @@ export function lowerEnvironmentKey(lowerer: Lowerer, node: ts.Expression): IrEx
|
||||
|
||||
export function ensureString(lowerer: Lowerer, e: IrExpr, node: ts.Node): IrExpr {
|
||||
if (e.type.kind === "string") return e;
|
||||
if (e.type.kind === "regex") {
|
||||
return { kind: "regexIntrinsic", method: "toString", receiver: e, args: [], type: STRING, loc: e.loc };
|
||||
}
|
||||
if (e.type.kind === "bigint") {
|
||||
return {
|
||||
kind: "libCall",
|
||||
|
||||
@@ -1977,6 +1977,7 @@ export type IrRegexIntrinsicMethod =
|
||||
| "search"
|
||||
| "source"
|
||||
| "flags"
|
||||
| "toString"
|
||||
| "replace"
|
||||
| "replaceAll"
|
||||
| "split";
|
||||
|
||||
@@ -73,6 +73,7 @@ export const REGEX_INTRINSIC_SIGS: Record<
|
||||
search: { receiver: STRING, argTypes: [REGEX], result: F64 },
|
||||
source: { receiver: REGEX, argTypes: [], result: STRING },
|
||||
flags: { receiver: REGEX, argTypes: [], result: STRING },
|
||||
toString: { receiver: REGEX, argTypes: [], result: STRING },
|
||||
replace: { receiver: STRING, argTypes: [REGEX, STRING], result: STRING },
|
||||
replaceAll: { receiver: STRING, argTypes: [REGEX, STRING], result: STRING },
|
||||
split: { receiver: STRING, argTypes: [REGEX, F64], result: arrayOf(STRING) },
|
||||
|
||||
@@ -1171,7 +1171,6 @@ ScrDyn *scr_dyn_define_props(ScrDyn *target, ScrDyn *descs) {
|
||||
ScrDyn *pair = pending->v.arr.items[i];
|
||||
ScrDyn *key = pair->v.arr.items[0];
|
||||
ScrDyn *descriptor = pair->v.arr.items[1];
|
||||
ScrStr *name = key->v.str;
|
||||
if (target->kind == SCR_DYN_FUNC &&
|
||||
(scr_dyn_obj_get(descriptor, "get", 3) || scr_dyn_obj_get(descriptor, "set", 3))) {
|
||||
scr_throw_error_msg(SCR_ERR_ERROR,
|
||||
@@ -1180,8 +1179,6 @@ ScrDyn *scr_dyn_define_props(ScrDyn *target, ScrDyn *descs) {
|
||||
scr_dyn_release(pending);
|
||||
return NULL;
|
||||
}
|
||||
ScrDyn *value = scr_dyn_obj_get(descriptor, "value", 5);
|
||||
if (!value) value = scr_dyn_undefined();
|
||||
if (target->kind == SCR_DYN_OBJ) {
|
||||
ScrDyn *defined = scr_dyn_define_property(target, key, descriptor);
|
||||
if (!defined) {
|
||||
@@ -1197,8 +1194,13 @@ ScrDyn *scr_dyn_define_props(ScrDyn *target, ScrDyn *descs) {
|
||||
target->v.fn.clo->props = box;
|
||||
}
|
||||
ScrDyn *table = (ScrDyn *)scr_box_get_ref(target->v.fn.clo->props); /* +1 */
|
||||
scr_dyn_obj_set(table, name->data, name->len, scr_dyn_retain(value));
|
||||
ScrDyn *defined = scr_dyn_define_property(table, key, descriptor);
|
||||
scr_dyn_release(table);
|
||||
if (!defined) {
|
||||
scr_dyn_release(pending);
|
||||
return NULL;
|
||||
}
|
||||
scr_dyn_release(defined);
|
||||
}
|
||||
}
|
||||
scr_dyn_release(pending);
|
||||
|
||||
@@ -869,6 +869,34 @@ bool scr_dyn_proxy_has(const ScrDyn *proxy, const ScrStr *key) {
|
||||
|
||||
ScrDyn *scr_dyn_own_descriptor(const ScrDyn *value, const ScrStr *key) {
|
||||
if (scr_dyn_class_reflection_fence(value)) return NULL;
|
||||
if (value->kind == SCR_DYN_FUNC) {
|
||||
if (value->v.fn.clo->props) {
|
||||
ScrDyn *table = (ScrDyn *)scr_box_get_ref(value->v.fn.clo->props);
|
||||
ScrDyn *desc = table ? scr_dyn_own_descriptor(table, key) : NULL;
|
||||
bool found = desc && desc->kind != SCR_DYN_UNDEF;
|
||||
scr_dyn_release(table);
|
||||
if (found || scr_exc_pending()) return desc;
|
||||
scr_dyn_release(desc);
|
||||
}
|
||||
bool name = key->len == 4 && memcmp(key->data, "name", 4) == 0;
|
||||
bool length = key->len == 6 && memcmp(key->data, "length", 6) == 0;
|
||||
if (!name && !length) return scr_dyn_retain(scr_dyn_undefined());
|
||||
ScrDyn *out = scr_dyn_new_obj();
|
||||
ScrDyn *member = scr_dyn_fn_get(value, key->data, key->len);
|
||||
scr_dyn_obj_set(out, "value", 5, member);
|
||||
scr_dyn_obj_set(out, "writable", 8, scr_dyn_new_bool(false));
|
||||
scr_dyn_obj_set(out, "enumerable", 10, scr_dyn_new_bool(false));
|
||||
scr_dyn_obj_set(out, "configurable", 12, scr_dyn_new_bool(true));
|
||||
return out;
|
||||
}
|
||||
if (value->kind == SCR_DYN_ARR && key->len == 6 && memcmp(key->data, "length", 6) == 0) {
|
||||
ScrDyn *out = scr_dyn_new_obj();
|
||||
scr_dyn_obj_set(out, "value", 5, scr_dyn_new_num((double)value->v.arr.len));
|
||||
scr_dyn_obj_set(out, "writable", 8, scr_dyn_new_bool(true));
|
||||
scr_dyn_obj_set(out, "enumerable", 10, scr_dyn_new_bool(false));
|
||||
scr_dyn_obj_set(out, "configurable", 12, scr_dyn_new_bool(false));
|
||||
return out;
|
||||
}
|
||||
if (value->kind == SCR_DYN_OBJ) {
|
||||
ScrDynEntry *entry = scr_dyn_entry((ScrDyn *)value, key);
|
||||
if (!entry) return scr_dyn_retain(scr_dyn_undefined());
|
||||
@@ -1944,7 +1972,7 @@ ScrDyn *scr_dyn_get_own_property_descriptor(ScrDyn *target, ScrDyn *key) {
|
||||
scr_throw_error_msg(SCR_ERR_TYPE, msg, sizeof msg - 1);
|
||||
return NULL;
|
||||
}
|
||||
if (target->kind != SCR_DYN_OBJ && target->kind != SCR_DYN_PROXY) {
|
||||
if (target->kind != SCR_DYN_OBJ && target->kind != SCR_DYN_PROXY && target->kind != SCR_DYN_FUNC && target->kind != SCR_DYN_ARR) {
|
||||
static const char msg[] = "Object.getOwnPropertyDescriptor on this value is not supported yet";
|
||||
scr_throw_error_msg(SCR_ERR_ERROR, msg, sizeof msg - 1);
|
||||
return NULL;
|
||||
@@ -4692,7 +4720,13 @@ bool scr_dyn_has_own(const ScrDyn *v, const ScrStr *key) {
|
||||
if (v->kind == SCR_DYN_BYTES) {
|
||||
return scr_dyn_canonical_own_index(key, v->v.bytes->len);
|
||||
}
|
||||
if (v->kind == SCR_DYN_FUNC || v->kind == SCR_DYN_HANDLE ||
|
||||
if (v->kind == SCR_DYN_FUNC) {
|
||||
ScrDyn *desc = scr_dyn_own_descriptor(v, key);
|
||||
bool has = desc && desc->kind != SCR_DYN_UNDEF;
|
||||
scr_dyn_release(desc);
|
||||
return has;
|
||||
}
|
||||
if (v->kind == SCR_DYN_HANDLE ||
|
||||
v->kind == SCR_DYN_TYPED_REF) {
|
||||
const char *m = "Own-property checks on this checked-dynamic kind are not supported yet";
|
||||
scr_throw_error_msg(SCR_ERR_ERROR, m, strlen(m));
|
||||
|
||||
@@ -222,6 +222,69 @@ void scr_regex_release_v(void *re) { scr_regex_release(re); }
|
||||
|
||||
ScrStr *scr_regex_source(ScrRegex *re) { return scr_str_retain(re->source); }
|
||||
ScrStr *scr_regex_flags(ScrRegex *re) { return scr_str_retain(re->flags); }
|
||||
ScrStr *scr_regex_to_string(ScrRegex *re) {
|
||||
const char *source = re->source->data;
|
||||
size_t len = re->source->len;
|
||||
size_t escaped = 0;
|
||||
bool in_class = false;
|
||||
bool quoted = false;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
char c = source[i];
|
||||
bool line_separator = i + 2 < len && (unsigned char)c == 0xE2 &&
|
||||
(unsigned char)source[i + 1] == 0x80 &&
|
||||
((unsigned char)source[i + 2] == 0xA8 ||
|
||||
(unsigned char)source[i + 2] == 0xA9);
|
||||
if (!quoted && c == '/' && !in_class) escaped++;
|
||||
if (!quoted && (c == '\n' || c == '\r')) escaped++;
|
||||
if (!quoted && line_separator) escaped += 3;
|
||||
if (!quoted && c == '[') in_class = true;
|
||||
else if (!quoted && c == ']') in_class = false;
|
||||
if (c == '\\') quoted = !quoted;
|
||||
else quoted = false;
|
||||
}
|
||||
size_t total = 2 + len + escaped + re->flags->len;
|
||||
char *buf = malloc(total + 1);
|
||||
if (!buf) scr_regex_oom();
|
||||
size_t pos = 0;
|
||||
buf[pos++] = '/';
|
||||
in_class = false;
|
||||
quoted = false;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
char c = source[i];
|
||||
bool line_separator = i + 2 < len && (unsigned char)c == 0xE2 &&
|
||||
(unsigned char)source[i + 1] == 0x80 &&
|
||||
((unsigned char)source[i + 2] == 0xA8 ||
|
||||
(unsigned char)source[i + 2] == 0xA9);
|
||||
if (!quoted && c == '/' && !in_class) buf[pos++] = '\\';
|
||||
if (!quoted && line_separator) {
|
||||
buf[pos++] = '\\';
|
||||
buf[pos++] = 'u';
|
||||
buf[pos++] = '2';
|
||||
buf[pos++] = '0';
|
||||
buf[pos++] = '2';
|
||||
buf[pos++] = source[i + 2] == (char)0xA8 ? '8' : '9';
|
||||
i += 2;
|
||||
quoted = false;
|
||||
continue;
|
||||
}
|
||||
if (!quoted && (c == '\n' || c == '\r')) {
|
||||
buf[pos++] = '\\';
|
||||
c = c == '\n' ? 'n' : 'r';
|
||||
}
|
||||
buf[pos++] = c;
|
||||
if (!quoted && c == '[') in_class = true;
|
||||
else if (!quoted && c == ']') in_class = false;
|
||||
if (source[i] == '\\') quoted = !quoted;
|
||||
else quoted = false;
|
||||
}
|
||||
buf[pos++] = '/';
|
||||
memcpy(buf + pos, re->flags->data, re->flags->len);
|
||||
pos += re->flags->len;
|
||||
buf[pos] = '\0';
|
||||
ScrStr *out = scr_str_new(buf, pos);
|
||||
free(buf);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ── UTF-8 ⇄ UTF-16 (the exec buffer strategy) ──────────────────────── */
|
||||
|
||||
|
||||
@@ -1194,6 +1194,7 @@ ScrArr *scr_regex_match(ScrStr *s, ScrRegex *re);
|
||||
double scr_regex_search(ScrStr *s, ScrRegex *re);
|
||||
ScrStr *scr_regex_source(ScrRegex *re); /* +1 */
|
||||
ScrStr *scr_regex_flags(ScrRegex *re); /* +1 */
|
||||
ScrStr *scr_regex_to_string(ScrRegex *re); /* +1 */
|
||||
/* replace: first match without /g, every match with it. */
|
||||
ScrStr *scr_regex_replace(ScrStr *s, ScrRegex *re, ScrStr *rep);
|
||||
/* replaceAll: throws Node's TypeError when /g is missing (may-throw). */
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
function named(first, second) { return first + second; }
|
||||
const alias = named;
|
||||
for (const key of ['name', 'length', 'missing']) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(alias, key);
|
||||
if (descriptor) console.log(key, descriptor.value, descriptor.writable, descriptor.enumerable, descriptor.configurable);
|
||||
else console.log(key, descriptor);
|
||||
}
|
||||
Object.defineProperties(named, { label: { value: 'tag', writable: true, enumerable: true, configurable: true } });
|
||||
const label = Object.getOwnPropertyDescriptor(alias, 'label');
|
||||
console.log(label.value, label.writable, label.enumerable, label.configurable);
|
||||
console.log(Object.getOwnPropertyDescriptor(alias, 'name').value);
|
||||
console.log(Object.hasOwn(alias, 'name'), Object.hasOwn(alias, 'length'), Object.hasOwn(alias, 'label'), Object.hasOwn(alias, 'missing'));
|
||||
const arrayLength = Object.getOwnPropertyDescriptor(JSON.parse('[1,2,3]'), 'length');
|
||||
console.log(arrayLength.value, arrayLength.writable, arrayLength.enumerable, arrayLength.configurable);
|
||||
@@ -0,0 +1,13 @@
|
||||
console.log('expression ' + new RegExp('a+', 'gi'));
|
||||
console.log('expression ' + new RegExp(''));
|
||||
console.log('expression ' + /b?/);
|
||||
console.log('expression ' + new RegExp('x/y'));
|
||||
console.log('expression ' + new RegExp('[a/]'));
|
||||
console.log('expression ' + new RegExp('a\nb'));
|
||||
for (const pattern of ['(', 'x{1}{1,}']) {
|
||||
try {
|
||||
console.log('expression ' + new RegExp(pattern));
|
||||
} catch (error) {
|
||||
console.log(error.name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user