1 /*
2  *  RegExp built-ins
3  */
4 
5 #include "duk_internal.h"
6 
7 #ifdef DUK_USE_REGEXP_SUPPORT
8 
duk__get_this_regexp(duk_context * ctx)9 DUK_LOCAL void duk__get_this_regexp(duk_context *ctx) {
10 	duk_hobject *h;
11 
12 	duk_push_this(ctx);
13 	h = duk_require_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_REGEXP);
14 	DUK_ASSERT(h != NULL);
15 	DUK_UNREF(h);
16 	duk_insert(ctx, 0);  /* prepend regexp to valstack 0 index */
17 }
18 
19 /* XXX: much to improve (code size) */
duk_bi_regexp_constructor(duk_context * ctx)20 DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx) {
21 	duk_hthread *thr = (duk_hthread *) ctx;
22 	duk_hobject *h_pattern;
23 
24 	DUK_ASSERT_TOP(ctx, 2);
25 	h_pattern = duk_get_hobject(ctx, 0);
26 
27 	if (!duk_is_constructor_call(ctx) &&
28 	    h_pattern != NULL &&
29 	    DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP &&
30 	    duk_is_undefined(ctx, 1)) {
31 		/* Called as a function, pattern has [[Class]] "RegExp" and
32 		 * flags is undefined -> return object as is.
33 		 */
34 		duk_dup(ctx, 0);
35 		return 1;
36 	}
37 
38 	/* Else functionality is identical for function call and constructor
39 	 * call.
40 	 */
41 
42 	if (h_pattern != NULL &&
43 	    DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP) {
44 		if (duk_is_undefined(ctx, 1)) {
45 			duk_bool_t flag_g, flag_i, flag_m;
46 			duk_get_prop_stridx(ctx, 0, DUK_STRIDX_SOURCE);
47 			flag_g = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL);
48 			flag_i = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_IGNORE_CASE, NULL);
49 			flag_m = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_MULTILINE, NULL);
50 
51 			duk_push_sprintf(ctx, "%s%s%s",
52 			                 (const char *) (flag_g ? "g" : ""),
53 			                 (const char *) (flag_i ? "i" : ""),
54 			                 (const char *) (flag_m ? "m" : ""));
55 
56 			/* [ ... pattern flags ] */
57 		} else {
58 			return DUK_RET_TYPE_ERROR;
59 		}
60 	} else {
61 		if (duk_is_undefined(ctx, 0)) {
62 			duk_push_string(ctx, "");
63 		} else {
64 			duk_dup(ctx, 0);
65 			duk_to_string(ctx, -1);
66 		}
67 		if (duk_is_undefined(ctx, 1)) {
68 			duk_push_string(ctx, "");
69 		} else {
70 			duk_dup(ctx, 1);
71 			duk_to_string(ctx, -1);
72 		}
73 
74 		/* [ ... pattern flags ] */
75 	}
76 
77 	DUK_DDD(DUK_DDDPRINT("RegExp constructor/function call, pattern=%!T, flags=%!T",
78 	                     (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1)));
79 
80 	/* [ ... pattern flags ] */
81 
82 	duk_regexp_compile(thr);
83 
84 	/* [ ... bytecode escaped_source ] */
85 
86 	duk_regexp_create_instance(thr);
87 
88 	/* [ ... RegExp ] */
89 
90 	return 1;
91 }
92 
duk_bi_regexp_prototype_exec(duk_context * ctx)93 DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_context *ctx) {
94 	duk__get_this_regexp(ctx);
95 
96 	/* [ regexp input ] */
97 
98 	duk_regexp_match((duk_hthread *) ctx);
99 
100 	/* [ result ] */
101 
102 	return 1;
103 }
104 
duk_bi_regexp_prototype_test(duk_context * ctx)105 DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_context *ctx) {
106 	duk__get_this_regexp(ctx);
107 
108 	/* [ regexp input ] */
109 
110 	/* result object is created and discarded; wasteful but saves code space */
111 	duk_regexp_match((duk_hthread *) ctx);
112 
113 	/* [ result ] */
114 
115 	duk_push_boolean(ctx, (duk_is_null(ctx, -1) ? 0 : 1));
116 
117 	return 1;
118 }
119 
duk_bi_regexp_prototype_to_string(duk_context * ctx)120 DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_to_string(duk_context *ctx) {
121 	duk_hstring *h_bc;
122 	duk_small_int_t re_flags;
123 
124 #if 0
125 	/* A little tricky string approach to provide the flags string.
126 	 * This depends on the specific flag values in duk_regexp.h,
127 	 * which needs to be asserted for.  In practice this doesn't
128 	 * produce more compact code than the easier approach in use.
129 	 */
130 
131 	const char *flag_strings = "gim\0gi\0gm\0g\0";
132 	duk_uint8_t flag_offsets[8] = {
133 		(duk_uint8_t) 3,   /* flags: ""    */
134 		(duk_uint8_t) 10,  /* flags: "g"   */
135 		(duk_uint8_t) 5,   /* flags: "i"   */
136 		(duk_uint8_t) 4,   /* flags: "gi"  */
137 		(duk_uint8_t) 2,   /* flags: "m"   */
138 		(duk_uint8_t) 7,   /* flags: "gm"  */
139 		(duk_uint8_t) 1,   /* flags: "im"  */
140 		(duk_uint8_t) 0,   /* flags: "gim" */
141 	};
142 	DUK_ASSERT(DUK_RE_FLAG_GLOBAL == 1);
143 	DUK_ASSERT(DUK_RE_FLAG_IGNORE_CASE == 2);
144 	DUK_ASSERT(DUK_RE_FLAG_MULTILINE == 4);
145 #endif
146 
147 	duk__get_this_regexp(ctx);
148 
149 	/* [ regexp ] */
150 
151 	duk_get_prop_stridx(ctx, 0, DUK_STRIDX_SOURCE);
152 	duk_get_prop_stridx(ctx, 0, DUK_STRIDX_INT_BYTECODE);
153 	h_bc = duk_get_hstring(ctx, -1);
154 	DUK_ASSERT(h_bc != NULL);
155 	DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h_bc) >= 1);
156 	DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h_bc) >= 1);
157 	DUK_ASSERT(DUK_HSTRING_GET_DATA(h_bc)[0] < 0x80);
158 	re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0];
159 
160 	/* [ regexp source bytecode ] */
161 
162 #if 1
163 	/* This is a cleaner approach and also produces smaller code than
164 	 * the other alternative.  Use duk_require_string() for format
165 	 * safety (although the source property should always exist).
166 	 */
167 	duk_push_sprintf(ctx, "/%s/%s%s%s",
168 	                 (const char *) duk_require_string(ctx, -2),  /* require to be safe */
169 	                 (re_flags & DUK_RE_FLAG_GLOBAL) ? "g" : "",
170 	                 (re_flags & DUK_RE_FLAG_IGNORE_CASE) ? "i" : "",
171 	                 (re_flags & DUK_RE_FLAG_MULTILINE) ? "m" : "");
172 #else
173 	/* This should not be necessary because no-one should tamper with the
174 	 * regexp bytecode, but is prudent to avoid potential segfaults if that
175 	 * were to happen for some reason.
176 	 */
177 	re_flags &= 0x07;
178 	DUK_ASSERT(re_flags >= 0 && re_flags <= 7);  /* three flags */
179 	duk_push_sprintf(ctx, "/%s/%s",
180 	                 (const char *) duk_require_string(ctx, -2),
181 	                 (const char *) (flag_strings + flag_offsets[re_flags]));
182 #endif
183 
184 	return 1;
185 }
186 
187 #else  /* DUK_USE_REGEXP_SUPPORT */
188 
duk_bi_regexp_constructor(duk_context * ctx)189 DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx) {
190 	DUK_UNREF(ctx);
191 	return DUK_RET_UNSUPPORTED_ERROR;
192 }
193 
duk_bi_regexp_prototype_exec(duk_context * ctx)194 DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_context *ctx) {
195 	DUK_UNREF(ctx);
196 	return DUK_RET_UNSUPPORTED_ERROR;
197 }
198 
duk_bi_regexp_prototype_test(duk_context * ctx)199 DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_context *ctx) {
200 	DUK_UNREF(ctx);
201 	return DUK_RET_UNSUPPORTED_ERROR;
202 }
203 
duk_bi_regexp_prototype_to_string(duk_context * ctx)204 DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_to_string(duk_context *ctx) {
205 	DUK_UNREF(ctx);
206 	return DUK_RET_UNSUPPORTED_ERROR;
207 }
208 
209 #endif  /* DUK_USE_REGEXP_SUPPORT */
210