Merge branch 'optimize_mcode'
This commit is contained in:
@@ -2910,6 +2910,84 @@ JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2) {
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/* Concat with over-allocated capacity and NO stoning.
|
||||
Used by MACH_CONCAT self-assign (s = s + x) slow path so that
|
||||
subsequent appends can reuse the excess capacity in-place. */
|
||||
JSValue JS_ConcatStringGrow (JSContext *ctx, JSValue op1, JSValue op2) {
|
||||
if (unlikely (!JS_IsText (op1))) {
|
||||
JSGCRef op2_guard;
|
||||
JS_PushGCRef (ctx, &op2_guard);
|
||||
op2_guard.val = op2;
|
||||
op1 = JS_ToString (ctx, op1);
|
||||
op2 = op2_guard.val;
|
||||
JS_PopGCRef (ctx, &op2_guard);
|
||||
if (JS_IsException (op1)) return JS_EXCEPTION;
|
||||
}
|
||||
if (unlikely (!JS_IsText (op2))) {
|
||||
JSGCRef op1_guard;
|
||||
JS_PushGCRef (ctx, &op1_guard);
|
||||
op1_guard.val = op1;
|
||||
op2 = JS_ToString (ctx, op2);
|
||||
op1 = op1_guard.val;
|
||||
JS_PopGCRef (ctx, &op1_guard);
|
||||
if (JS_IsException (op2)) return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
int len1 = js_string_value_len (op1);
|
||||
int len2 = js_string_value_len (op2);
|
||||
int new_len = len1 + len2;
|
||||
|
||||
/* Try immediate ASCII for short results */
|
||||
if (new_len <= MIST_ASCII_MAX_LEN) {
|
||||
char buf[8];
|
||||
BOOL all_ascii = TRUE;
|
||||
for (int i = 0; i < len1 && all_ascii; i++) {
|
||||
uint32_t c = js_string_value_get (op1, i);
|
||||
if (c >= 0x80) all_ascii = FALSE;
|
||||
else buf[i] = (char)c;
|
||||
}
|
||||
for (int i = 0; i < len2 && all_ascii; i++) {
|
||||
uint32_t c = js_string_value_get (op2, i);
|
||||
if (c >= 0x80) all_ascii = FALSE;
|
||||
else buf[len1 + i] = (char)c;
|
||||
}
|
||||
if (all_ascii) {
|
||||
JSValue imm = MIST_TryNewImmediateASCII (buf, new_len);
|
||||
if (!JS_IsNull (imm)) return imm;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate with 2x growth factor, minimum 16 */
|
||||
int capacity = new_len * 2;
|
||||
if (capacity < 16) capacity = 16;
|
||||
|
||||
JSGCRef op1_ref, op2_ref;
|
||||
JS_PushGCRef (ctx, &op1_ref);
|
||||
op1_ref.val = op1;
|
||||
JS_PushGCRef (ctx, &op2_ref);
|
||||
op2_ref.val = op2;
|
||||
|
||||
JSText *p = js_alloc_string (ctx, capacity);
|
||||
if (!p) {
|
||||
JS_PopGCRef (ctx, &op2_ref);
|
||||
JS_PopGCRef (ctx, &op1_ref);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
op1 = op1_ref.val;
|
||||
op2 = op2_ref.val;
|
||||
JS_PopGCRef (ctx, &op2_ref);
|
||||
JS_PopGCRef (ctx, &op1_ref);
|
||||
|
||||
for (int i = 0; i < len1; i++)
|
||||
string_put (p, i, js_string_value_get (op1, i));
|
||||
for (int i = 0; i < len2; i++)
|
||||
string_put (p, len1 + i, js_string_value_get (op2, i));
|
||||
p->length = new_len;
|
||||
/* Do NOT stone — leave mutable so in-place append can reuse capacity */
|
||||
return JS_MKPTR (p);
|
||||
}
|
||||
|
||||
/* WARNING: proto must be an object or JS_NULL */
|
||||
JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto_val, JSClassID class_id) {
|
||||
JSGCRef proto_ref;
|
||||
|
||||
Reference in New Issue
Block a user