]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_bi_global.c
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / src-separate / duk_bi_global.c
1 /*
2 * Global object built-ins
3 */
4
5 #include "duk_internal.h"
6
7 /*
8 * Encoding/decoding helpers
9 */
10
11 /* XXX: Could add fast path (for each transform callback) with direct byte
12 * lookups (no shifting) and no explicit check for x < 0x80 before table
13 * lookup.
14 */
15
16 /* Macros for creating and checking bitmasks for character encoding.
17 * Bit number is a bit counterintuitive, but minimizes code size.
18 */
19 #define DUK__MKBITS(a,b,c,d,e,f,g,h) ((duk_uint8_t) ( \
20 ((a) << 0) | ((b) << 1) | ((c) << 2) | ((d) << 3) | \
21 ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7) \
22 ))
23 #define DUK__CHECK_BITMASK(table,cp) ((table)[(cp) >> 3] & (1 << ((cp) & 0x07)))
24
25 /* E5.1 Section 15.1.3.3: uriReserved + uriUnescaped + '#' */
26 DUK_LOCAL const duk_uint8_t duk__encode_uriunescaped_table[16] = {
27 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */
28 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */
29 DUK__MKBITS(0, 1, 0, 1, 1, 0, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x20-0x2f */
30 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 0, 1, 0, 1), /* 0x30-0x3f */
31 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */
32 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */
33 DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */
34 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0), /* 0x70-0x7f */
35 };
36
37 /* E5.1 Section 15.1.3.4: uriUnescaped */
38 DUK_LOCAL const duk_uint8_t duk__encode_uricomponent_unescaped_table[16] = {
39 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */
40 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */
41 DUK__MKBITS(0, 1, 0, 0, 0, 0, 0, 1), DUK__MKBITS(1, 1, 1, 0, 0, 1, 1, 0), /* 0x20-0x2f */
42 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */
43 DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */
44 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */
45 DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */
46 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 1, 0), /* 0x70-0x7f */
47 };
48
49 /* E5.1 Section 15.1.3.1: uriReserved + '#' */
50 DUK_LOCAL const duk_uint8_t duk__decode_uri_reserved_table[16] = {
51 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */
52 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */
53 DUK__MKBITS(0, 0, 0, 1, 1, 0, 1, 0), DUK__MKBITS(0, 0, 0, 1, 1, 0, 0, 1), /* 0x20-0x2f */
54 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 0, 1), /* 0x30-0x3f */
55 DUK__MKBITS(1, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x40-0x4f */
56 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x50-0x5f */
57 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x60-0x6f */
58 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */
59 };
60
61 /* E5.1 Section 15.1.3.2: empty */
62 DUK_LOCAL const duk_uint8_t duk__decode_uri_component_reserved_table[16] = {
63 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */
64 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */
65 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x20-0x2f */
66 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */
67 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x40-0x4f */
68 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x50-0x5f */
69 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x60-0x6f */
70 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */
71 };
72
73 #ifdef DUK_USE_SECTION_B
74 /* E5.1 Section B.2.2, step 7. */
75 DUK_LOCAL const duk_uint8_t duk__escape_unescaped_table[16] = {
76 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */
77 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x10-0x1f */
78 DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 1, 1, 0, 1, 1, 1), /* 0x20-0x2f */
79 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 0, 0, 0, 0, 0, 0), /* 0x30-0x3f */
80 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x40-0x4f */
81 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 1), /* 0x50-0x5f */
82 DUK__MKBITS(0, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), /* 0x60-0x6f */
83 DUK__MKBITS(1, 1, 1, 1, 1, 1, 1, 1), DUK__MKBITS(1, 1, 1, 0, 0, 0, 0, 0) /* 0x70-0x7f */
84 };
85 #endif /* DUK_USE_SECTION_B */
86
87 #undef DUK__MKBITS
88
89 typedef struct {
90 duk_hthread *thr;
91 duk_hstring *h_str;
92 duk_bufwriter_ctx bw;
93 const duk_uint8_t *p;
94 const duk_uint8_t *p_start;
95 const duk_uint8_t *p_end;
96 } duk__transform_context;
97
98 typedef void (*duk__transform_callback)(duk__transform_context *tfm_ctx, void *udata, duk_codepoint_t cp);
99
100 /* XXX: refactor and share with other code */
101 DUK_LOCAL duk_small_int_t duk__decode_hex_escape(const duk_uint8_t *p, duk_small_int_t n) {
102 duk_small_int_t ch;
103 duk_small_int_t t = 0;
104
105 while (n > 0) {
106 t = t * 16;
107 ch = (duk_small_int_t) duk_hex_dectab[*p++];
108 if (DUK_LIKELY(ch >= 0)) {
109 t += ch;
110 } else {
111 return -1;
112 }
113 n--;
114 }
115 return t;
116 }
117
118 DUK_LOCAL int duk__transform_helper(duk_context *ctx, duk__transform_callback callback, void *udata) {
119 duk_hthread *thr = (duk_hthread *) ctx;
120 duk__transform_context tfm_ctx_alloc;
121 duk__transform_context *tfm_ctx = &tfm_ctx_alloc;
122 duk_codepoint_t cp;
123
124 tfm_ctx->thr = thr;
125
126 tfm_ctx->h_str = duk_to_hstring(ctx, 0);
127 DUK_ASSERT(tfm_ctx->h_str != NULL);
128
129 DUK_BW_INIT_PUSHBUF(thr, &tfm_ctx->bw, DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str)); /* initial size guess */
130
131 tfm_ctx->p_start = DUK_HSTRING_GET_DATA(tfm_ctx->h_str);
132 tfm_ctx->p_end = tfm_ctx->p_start + DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str);
133 tfm_ctx->p = tfm_ctx->p_start;
134
135 while (tfm_ctx->p < tfm_ctx->p_end) {
136 cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end);
137 callback(tfm_ctx, udata, cp);
138 }
139
140 DUK_BW_COMPACT(thr, &tfm_ctx->bw);
141
142 duk_to_string(ctx, -1);
143 return 1;
144 }
145
146 DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ctx, void *udata, duk_codepoint_t cp) {
147 duk_uint8_t xutf8_buf[DUK_UNICODE_MAX_XUTF8_LENGTH];
148 duk_small_int_t len;
149 duk_codepoint_t cp1, cp2;
150 duk_small_int_t i, t;
151 const duk_uint8_t *unescaped_table = (duk_uint8_t *) udata;
152
153 /* UTF-8 encoded bytes escaped as %xx%xx%xx... -> 3 * nbytes.
154 * Codepoint range is restricted so this is a slightly too large
155 * but doesn't matter.
156 */
157 DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 3 * DUK_UNICODE_MAX_XUTF8_LENGTH);
158
159 if (cp < 0) {
160 goto uri_error;
161 } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(unescaped_table, cp)) {
162 DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp);
163 return;
164 } else if (cp >= 0xdc00L && cp <= 0xdfffL) {
165 goto uri_error;
166 } else if (cp >= 0xd800L && cp <= 0xdbffL) {
167 /* Needs lookahead */
168 if (duk_unicode_decode_xutf8(tfm_ctx->thr, &tfm_ctx->p, tfm_ctx->p_start, tfm_ctx->p_end, (duk_ucodepoint_t *) &cp2) == 0) {
169 goto uri_error;
170 }
171 if (!(cp2 >= 0xdc00L && cp2 <= 0xdfffL)) {
172 goto uri_error;
173 }
174 cp1 = cp;
175 cp = ((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L;
176 } else if (cp > 0x10ffffL) {
177 /* Although we can allow non-BMP characters (they'll decode
178 * back into surrogate pairs), we don't allow extended UTF-8
179 * characters; they would encode to URIs which won't decode
180 * back because of strict UTF-8 checks in URI decoding.
181 * (However, we could just as well allow them here.)
182 */
183 goto uri_error;
184 } else {
185 /* Non-BMP characters within valid UTF-8 range: encode as is.
186 * They'll decode back into surrogate pairs if the escaped
187 * output is decoded.
188 */
189 ;
190 }
191
192 len = duk_unicode_encode_xutf8((duk_ucodepoint_t) cp, xutf8_buf);
193 for (i = 0; i < len; i++) {
194 t = (int) xutf8_buf[i];
195 DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr,
196 &tfm_ctx->bw,
197 DUK_ASC_PERCENT,
198 (duk_uint8_t) duk_uc_nybbles[t >> 4],
199 (duk_uint8_t) duk_uc_nybbles[t & 0x0f]);
200 }
201
202 return;
203
204 uri_error:
205 DUK_ERROR(tfm_ctx->thr, DUK_ERR_URI_ERROR, "invalid input");
206 }
207
208 DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ctx, void *udata, duk_codepoint_t cp) {
209 const duk_uint8_t *reserved_table = (duk_uint8_t *) udata;
210 duk_small_uint_t utf8_blen;
211 duk_codepoint_t min_cp;
212 duk_small_int_t t; /* must be signed */
213 duk_small_uint_t i;
214
215 /* Maximum write size: XUTF8 path writes max DUK_UNICODE_MAX_XUTF8_LENGTH,
216 * percent escape path writes max two times CESU-8 encoded BMP length.
217 */
218 DUK_BW_ENSURE(tfm_ctx->thr,
219 &tfm_ctx->bw,
220 (DUK_UNICODE_MAX_XUTF8_LENGTH >= 2 * DUK_UNICODE_MAX_CESU8_BMP_LENGTH ?
221 DUK_UNICODE_MAX_XUTF8_LENGTH : DUK_UNICODE_MAX_CESU8_BMP_LENGTH));
222
223 if (cp == (duk_codepoint_t) '%') {
224 const duk_uint8_t *p = tfm_ctx->p;
225 duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p); /* bytes left */
226
227 DUK_DDD(DUK_DDDPRINT("percent encoding, left=%ld", (long) left));
228
229 if (left < 2) {
230 goto uri_error;
231 }
232
233 t = duk__decode_hex_escape(p, 2);
234 DUK_DDD(DUK_DDDPRINT("first byte: %ld", (long) t));
235 if (t < 0) {
236 goto uri_error;
237 }
238
239 if (t < 0x80) {
240 if (DUK__CHECK_BITMASK(reserved_table, t)) {
241 /* decode '%xx' to '%xx' if decoded char in reserved set */
242 DUK_ASSERT(tfm_ctx->p - 1 >= tfm_ctx->p_start);
243 DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr,
244 &tfm_ctx->bw,
245 DUK_ASC_PERCENT,
246 p[0],
247 p[1]);
248 } else {
249 DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) t);
250 }
251 tfm_ctx->p += 2;
252 return;
253 }
254
255 /* Decode UTF-8 codepoint from a sequence of hex escapes. The
256 * first byte of the sequence has been decoded to 't'.
257 *
258 * Note that UTF-8 validation must be strict according to the
259 * specification: E5.1 Section 15.1.3, decode algorithm step
260 * 4.d.vii.8. URIError from non-shortest encodings is also
261 * specifically noted in the spec.
262 */
263
264 DUK_ASSERT(t >= 0x80);
265 if (t < 0xc0) {
266 /* continuation byte */
267 goto uri_error;
268 } else if (t < 0xe0) {
269 /* 110x xxxx; 2 bytes */
270 utf8_blen = 2;
271 min_cp = 0x80L;
272 cp = t & 0x1f;
273 } else if (t < 0xf0) {
274 /* 1110 xxxx; 3 bytes */
275 utf8_blen = 3;
276 min_cp = 0x800L;
277 cp = t & 0x0f;
278 } else if (t < 0xf8) {
279 /* 1111 0xxx; 4 bytes */
280 utf8_blen = 4;
281 min_cp = 0x10000L;
282 cp = t & 0x07;
283 } else {
284 /* extended utf-8 not allowed for URIs */
285 goto uri_error;
286 }
287
288 if (left < utf8_blen * 3 - 1) {
289 /* '%xx%xx...%xx', p points to char after first '%' */
290 goto uri_error;
291 }
292
293 p += 3;
294 for (i = 1; i < utf8_blen; i++) {
295 /* p points to digit part ('%xy', p points to 'x') */
296 t = duk__decode_hex_escape(p, 2);
297 DUK_DDD(DUK_DDDPRINT("i=%ld utf8_blen=%ld cp=%ld t=0x%02lx",
298 (long) i, (long) utf8_blen, (long) cp, (unsigned long) t));
299 if (t < 0) {
300 goto uri_error;
301 }
302 if ((t & 0xc0) != 0x80) {
303 goto uri_error;
304 }
305 cp = (cp << 6) + (t & 0x3f);
306 p += 3;
307 }
308 p--; /* p overshoots */
309 tfm_ctx->p = p;
310
311 DUK_DDD(DUK_DDDPRINT("final cp=%ld, min_cp=%ld", (long) cp, (long) min_cp));
312
313 if (cp < min_cp || cp > 0x10ffffL || (cp >= 0xd800L && cp <= 0xdfffL)) {
314 goto uri_error;
315 }
316
317 /* The E5.1 algorithm checks whether or not a decoded codepoint
318 * is below 0x80 and perhaps may be in the "reserved" set.
319 * This seems pointless because the single byte UTF-8 case is
320 * handled separately, and non-shortest encodings are rejected.
321 * So, 'cp' cannot be below 0x80 here, and thus cannot be in
322 * the reserved set.
323 */
324
325 /* utf-8 validation ensures these */
326 DUK_ASSERT(cp >= 0x80L && cp <= 0x10ffffL);
327
328 if (cp >= 0x10000L) {
329 cp -= 0x10000L;
330 DUK_ASSERT(cp < 0x100000L);
331
332 DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp >> 10) + 0xd800L));
333 DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffUL) + 0xdc00L));
334 } else {
335 DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
336 }
337 } else {
338 DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
339 }
340 return;
341
342 uri_error:
343 DUK_ERROR(tfm_ctx->thr, DUK_ERR_URI_ERROR, "invalid input");
344 }
345
346 #ifdef DUK_USE_SECTION_B
347 DUK_LOCAL void duk__transform_callback_escape(duk__transform_context *tfm_ctx, void *udata, duk_codepoint_t cp) {
348 DUK_UNREF(udata);
349
350 DUK_BW_ENSURE(tfm_ctx->thr, &tfm_ctx->bw, 6);
351
352 if (cp < 0) {
353 goto esc_error;
354 } else if ((cp < 0x80L) && DUK__CHECK_BITMASK(duk__escape_unescaped_table, cp)) {
355 DUK_BW_WRITE_RAW_U8(tfm_ctx->thr, &tfm_ctx->bw, (duk_uint8_t) cp);
356 } else if (cp < 0x100L) {
357 DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr,
358 &tfm_ctx->bw,
359 (duk_uint8_t) DUK_ASC_PERCENT,
360 (duk_uint8_t) duk_uc_nybbles[cp >> 4],
361 (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]);
362 } else if (cp < 0x10000L) {
363 DUK_BW_WRITE_RAW_U8_6(tfm_ctx->thr,
364 &tfm_ctx->bw,
365 (duk_uint8_t) DUK_ASC_PERCENT,
366 (duk_uint8_t) DUK_ASC_LC_U,
367 (duk_uint8_t) duk_uc_nybbles[cp >> 12],
368 (duk_uint8_t) duk_uc_nybbles[(cp >> 8) & 0x0f],
369 (duk_uint8_t) duk_uc_nybbles[(cp >> 4) & 0x0f],
370 (duk_uint8_t) duk_uc_nybbles[cp & 0x0f]);
371 } else {
372 /* Characters outside BMP cannot be escape()'d. We could
373 * encode them as surrogate pairs (for codepoints inside
374 * valid UTF-8 range, but not extended UTF-8). Because
375 * escape() and unescape() are legacy functions, we don't.
376 */
377 goto esc_error;
378 }
379
380 return;
381
382 esc_error:
383 DUK_ERROR(tfm_ctx->thr, DUK_ERR_TYPE_ERROR, "invalid input");
384 }
385
386 DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, void *udata, duk_codepoint_t cp) {
387 duk_small_int_t t;
388
389 DUK_UNREF(udata);
390
391 if (cp == (duk_codepoint_t) '%') {
392 const duk_uint8_t *p = tfm_ctx->p;
393 duk_size_t left = (duk_size_t) (tfm_ctx->p_end - p); /* bytes left */
394
395 if (left >= 5 && p[0] == 'u' &&
396 ((t = duk__decode_hex_escape(p + 1, 4)) >= 0)) {
397 cp = (duk_codepoint_t) t;
398 tfm_ctx->p += 5;
399 } else if (left >= 2 &&
400 ((t = duk__decode_hex_escape(p, 2)) >= 0)) {
401 cp = (duk_codepoint_t) t;
402 tfm_ctx->p += 2;
403 }
404 }
405
406 DUK_BW_WRITE_ENSURE_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
407 }
408 #endif /* DUK_USE_SECTION_B */
409
410 /*
411 * Eval
412 *
413 * Eval needs to handle both a "direct eval" and an "indirect eval".
414 * Direct eval handling needs access to the caller's activation so that its
415 * lexical environment can be accessed. A direct eval is only possible from
416 * Ecmascript code; an indirect eval call is possible also from C code.
417 * When an indirect eval call is made from C code, there may not be a
418 * calling activation at all which needs careful handling.
419 */
420
421 DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) {
422 duk_hthread *thr = (duk_hthread *) ctx;
423 duk_hstring *h;
424 duk_activation *act_caller;
425 duk_activation *act_eval;
426 duk_activation *act;
427 duk_hcompiledfunction *func;
428 duk_hobject *outer_lex_env;
429 duk_hobject *outer_var_env;
430 duk_bool_t this_to_global = 1;
431 duk_small_uint_t comp_flags;
432
433 DUK_ASSERT_TOP(ctx, 1);
434 DUK_ASSERT(thr->callstack_top >= 1); /* at least this function exists */
435 DUK_ASSERT(((thr->callstack + thr->callstack_top - 1)->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */
436 (thr->callstack_top >= 2)); /* if direct eval, calling activation must exist */
437
438 /*
439 * callstack_top - 1 --> this function
440 * callstack_top - 2 --> caller (may not exist)
441 *
442 * If called directly from C, callstack_top might be 1. If calling
443 * activation doesn't exist, call must be indirect.
444 */
445
446 h = duk_get_hstring(ctx, 0);
447 if (!h) {
448 return 1; /* return arg as-is */
449 }
450
451 /* [ source ] */
452
453 comp_flags = DUK_JS_COMPILE_FLAG_EVAL;
454 act_eval = thr->callstack + thr->callstack_top - 1; /* this function */
455 if (thr->callstack_top >= 2) {
456 /* Have a calling activation, check for direct eval (otherwise
457 * assume indirect eval.
458 */
459 act_caller = thr->callstack + thr->callstack_top - 2; /* caller */
460 if ((act_caller->flags & DUK_ACT_FLAG_STRICT) &&
461 (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL)) {
462 /* Only direct eval inherits strictness from calling code
463 * (E5.1 Section 10.1.1).
464 */
465 comp_flags |= DUK_JS_COMPILE_FLAG_STRICT;
466 }
467 } else {
468 DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0);
469 }
470 act_caller = NULL; /* avoid dereference after potential callstack realloc */
471 act_eval = NULL;
472
473 duk_push_hstring_stridx(ctx, DUK_STRIDX_INPUT); /* XXX: copy from caller? */
474 duk_js_compile(thr,
475 (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h),
476 (duk_size_t) DUK_HSTRING_GET_BYTELEN(h),
477 comp_flags);
478 func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);
479 DUK_ASSERT(func != NULL);
480 DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func));
481
482 /* [ source template ] */
483
484 /* E5 Section 10.4.2 */
485 DUK_ASSERT(thr->callstack_top >= 1);
486 act = thr->callstack + thr->callstack_top - 1; /* this function */
487 if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
488 DUK_ASSERT(thr->callstack_top >= 2);
489 act = thr->callstack + thr->callstack_top - 2; /* caller */
490 if (act->lex_env == NULL) {
491 DUK_ASSERT(act->var_env == NULL);
492 DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
493
494 /* this may have side effects, so re-lookup act */
495 duk_js_init_activation_environment_records_delayed(thr, act);
496 act = thr->callstack + thr->callstack_top - 2;
497 }
498 DUK_ASSERT(act->lex_env != NULL);
499 DUK_ASSERT(act->var_env != NULL);
500
501 this_to_global = 0;
502
503 if (DUK_HOBJECT_HAS_STRICT((duk_hobject *) func)) {
504 duk_hobject *new_env;
505 duk_hobject *act_lex_env;
506
507 DUK_DDD(DUK_DDDPRINT("direct eval call to a strict function -> "
508 "var_env and lex_env to a fresh env, "
509 "this_binding to caller's this_binding"));
510
511 act = thr->callstack + thr->callstack_top - 2; /* caller */
512 act_lex_env = act->lex_env;
513 act = NULL; /* invalidated */
514
515 (void) duk_push_object_helper_proto(ctx,
516 DUK_HOBJECT_FLAG_EXTENSIBLE |
517 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV),
518 act_lex_env);
519 new_env = duk_require_hobject(ctx, -1);
520 DUK_ASSERT(new_env != NULL);
521 DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO",
522 (duk_heaphdr *) new_env));
523
524 outer_lex_env = new_env;
525 outer_var_env = new_env;
526
527 duk_insert(ctx, 0); /* stash to bottom of value stack to keep new_env reachable */
528
529 /* compiler's responsibility */
530 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func));
531 } else {
532 DUK_DDD(DUK_DDDPRINT("direct eval call to a non-strict function -> "
533 "var_env and lex_env to caller's envs, "
534 "this_binding to caller's this_binding"));
535
536 outer_lex_env = act->lex_env;
537 outer_var_env = act->var_env;
538
539 /* compiler's responsibility */
540 DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func));
541 }
542 } else {
543 DUK_DDD(DUK_DDDPRINT("indirect eval call -> var_env and lex_env to "
544 "global object, this_binding to global object"));
545
546 this_to_global = 1;
547 outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
548 outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
549 }
550 act = NULL;
551
552 duk_js_push_closure(thr, func, outer_var_env, outer_lex_env);
553
554 /* [ source template closure ] */
555
556 if (this_to_global) {
557 DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
558 duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL);
559 } else {
560 duk_tval *tv;
561 DUK_ASSERT(thr->callstack_top >= 2);
562 act = thr->callstack + thr->callstack_top - 2; /* caller */
563 tv = thr->valstack + act->idx_bottom - 1; /* this is just beneath bottom */
564 DUK_ASSERT(tv >= thr->valstack);
565 duk_push_tval(ctx, tv);
566 }
567
568 DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T",
569 (duk_heaphdr *) outer_lex_env,
570 (duk_heaphdr *) outer_var_env,
571 (duk_tval *) duk_get_tval(ctx, -1)));
572
573 /* [ source template closure this ] */
574
575 duk_call_method(ctx, 0);
576
577 /* [ source template result ] */
578
579 return 1;
580 }
581
582 /*
583 * Parsing of ints and floats
584 */
585
586 DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx) {
587 duk_bool_t strip_prefix;
588 duk_int32_t radix;
589 duk_small_uint_t s2n_flags;
590
591 DUK_ASSERT_TOP(ctx, 2);
592 duk_to_string(ctx, 0);
593
594 strip_prefix = 1;
595 radix = duk_to_int32(ctx, 1);
596 if (radix != 0) {
597 if (radix < 2 || radix > 36) {
598 goto ret_nan;
599 }
600 /* For octal, setting strip_prefix=0 is not necessary, as zero
601 * is tolerated anyway:
602 *
603 * parseInt('123', 8) === parseInt('0123', 8) with or without strip_prefix
604 * parseInt('123', 16) === parseInt('0x123', 16) requires strip_prefix = 1
605 */
606 if (radix != 16) {
607 strip_prefix = 0;
608 }
609 } else {
610 radix = 10;
611 }
612
613 s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
614 DUK_S2N_FLAG_ALLOW_GARBAGE |
615 DUK_S2N_FLAG_ALLOW_PLUS |
616 DUK_S2N_FLAG_ALLOW_MINUS |
617 DUK_S2N_FLAG_ALLOW_LEADING_ZERO |
618 #ifdef DUK_USE_OCTAL_SUPPORT
619 (strip_prefix ? (DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT | DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT) : 0)
620 #else
621 (strip_prefix ? DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT : 0)
622 #endif
623 ;
624
625 duk_dup(ctx, 0);
626 duk_numconv_parse(ctx, radix, s2n_flags);
627 return 1;
628
629 ret_nan:
630 duk_push_nan(ctx);
631 return 1;
632 }
633
634 DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) {
635 duk_small_uint_t s2n_flags;
636 duk_int32_t radix;
637
638 DUK_ASSERT_TOP(ctx, 1);
639 duk_to_string(ctx, 0);
640
641 radix = 10;
642
643 /* XXX: check flags */
644 s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
645 DUK_S2N_FLAG_ALLOW_EXP |
646 DUK_S2N_FLAG_ALLOW_GARBAGE |
647 DUK_S2N_FLAG_ALLOW_PLUS |
648 DUK_S2N_FLAG_ALLOW_MINUS |
649 DUK_S2N_FLAG_ALLOW_INF |
650 DUK_S2N_FLAG_ALLOW_FRAC |
651 DUK_S2N_FLAG_ALLOW_NAKED_FRAC |
652 DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
653 DUK_S2N_FLAG_ALLOW_LEADING_ZERO;
654
655 duk_numconv_parse(ctx, radix, s2n_flags);
656 return 1;
657 }
658
659 /*
660 * Number checkers
661 */
662
663 DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx) {
664 duk_double_t d = duk_to_number(ctx, 0);
665 duk_push_boolean(ctx, DUK_ISNAN(d));
666 return 1;
667 }
668
669 DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx) {
670 duk_double_t d = duk_to_number(ctx, 0);
671 duk_push_boolean(ctx, DUK_ISFINITE(d));
672 return 1;
673 }
674
675 /*
676 * URI handling
677 */
678
679 DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx) {
680 return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (void *) duk__decode_uri_reserved_table);
681 }
682
683 DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_context *ctx) {
684 return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (void *) duk__decode_uri_component_reserved_table);
685 }
686
687 DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx) {
688 return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (void *) duk__encode_uriunescaped_table);
689 }
690
691 DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx) {
692 return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (void *) duk__encode_uricomponent_unescaped_table);
693 }
694
695 #ifdef DUK_USE_SECTION_B
696 DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) {
697 return duk__transform_helper(ctx, duk__transform_callback_escape, (void *) NULL);
698 }
699
700 DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) {
701 return duk__transform_helper(ctx, duk__transform_callback_unescape, (void *) NULL);
702 }
703 #else /* DUK_USE_SECTION_B */
704 DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) {
705 DUK_UNREF(ctx);
706 return DUK_RET_UNSUPPORTED_ERROR;
707 }
708
709 DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) {
710 DUK_UNREF(ctx);
711 return DUK_RET_UNSUPPORTED_ERROR;
712 }
713 #endif /* DUK_USE_SECTION_B */
714
715 #if defined(DUK_USE_BROWSER_LIKE) && (defined(DUK_USE_FILE_IO) || defined(DUK_USE_DEBUGGER_SUPPORT))
716 DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) {
717 duk_hthread *thr = (duk_hthread *) ctx;
718 duk_int_t magic;
719 duk_idx_t nargs;
720 const duk_uint8_t *buf;
721 duk_size_t sz_buf;
722 const char nl = (const char) DUK_ASC_LF;
723 #ifndef DUK_USE_PREFER_SIZE
724 duk_uint8_t buf_stack[256];
725 #endif
726 #ifdef DUK_USE_FILE_IO
727 duk_file *f_out;
728 #endif
729
730 magic = duk_get_current_magic(ctx);
731 DUK_UNREF(magic);
732
733 nargs = duk_get_top(ctx);
734
735 /* If argument count is 1 and first argument is a buffer, write the buffer
736 * as raw data into the file without a newline; this allows exact control
737 * over stdout/stderr without an additional entrypoint (useful for now).
738 *
739 * Otherwise current print/alert semantics are to ToString() coerce
740 * arguments, join them with a single space, and append a newline.
741 */
742
743 if (nargs == 1 && duk_is_buffer(ctx, 0)) {
744 buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf);
745 DUK_ASSERT(buf != NULL);
746 } else if (nargs > 0) {
747 #ifdef DUK_USE_PREFER_SIZE
748 /* Compact but lots of churn. */
749 duk_push_hstring_stridx(thr, DUK_STRIDX_SPACE);
750 duk_insert(ctx, 0);
751 duk_join(ctx, nargs);
752 duk_push_string(thr, "\n");
753 duk_concat(ctx, 2);
754 buf = (const duk_uint8_t *) duk_get_lstring(ctx, -1, &sz_buf);
755 DUK_ASSERT(buf != NULL);
756 #else /* DUK_USE_PREFER_SIZE */
757 /* Higher footprint, less churn. */
758 duk_idx_t i;
759 duk_size_t sz_str;
760 const duk_uint8_t *p_str;
761 duk_uint8_t *p;
762
763 sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */
764 for (i = 0; i < nargs; i++) {
765 (void) duk_to_lstring(ctx, i, &sz_str);
766 sz_buf += sz_str;
767 }
768
769 if (sz_buf <= sizeof(buf_stack)) {
770 buf = (const duk_uint8_t *) buf_stack;
771 } else {
772 buf = (const duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf);
773 DUK_ASSERT(buf != NULL);
774 }
775
776 p = (duk_uint8_t *) buf;
777 for (i = 0; i < nargs; i++) {
778 p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str);
779 DUK_ASSERT(p_str != NULL);
780 DUK_MEMCPY((void *) p, (const void *) p_str, sz_str);
781 p += sz_str;
782 *p++ = (duk_uint8_t) (i == nargs - 1 ? DUK_ASC_LF : DUK_ASC_SPACE);
783 }
784 DUK_ASSERT((const duk_uint8_t *) p == buf + sz_total);
785 #endif /* DUK_USE_PREFER_SIZE */
786 } else {
787 buf = (const duk_uint8_t *) &nl;
788 sz_buf = 1;
789 }
790
791 /* 'buf' contains the string to write, 'sz_buf' contains the length
792 * (which may be zero).
793 */
794 DUK_ASSERT(buf != NULL);
795
796 if (sz_buf == 0) {
797 return 0;
798 }
799
800 #ifdef DUK_USE_FILE_IO
801 f_out = (magic ? DUK_STDERR : DUK_STDOUT);
802 DUK_FWRITE((const void *) buf, 1, (size_t) sz_buf, f_out);
803 DUK_FFLUSH(f_out);
804 #endif
805
806 #if defined(DUK_USE_DEBUGGER_SUPPORT) && defined(DUK_USE_DEBUGGER_FWD_PRINTALERT)
807 if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
808 duk_debug_write_notify(thr, magic ? DUK_DBG_CMD_ALERT : DUK_DBG_CMD_PRINT);
809 duk_debug_write_string(thr, (const char *) buf, sz_buf);
810 duk_debug_write_eom(thr);
811 }
812 #endif
813 return 0;
814 }
815 #elif defined(DUK_USE_BROWSER_LIKE) /* print provider */
816 DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) {
817 DUK_UNREF(ctx);
818 return 0;
819 }
820 #else /* print provider */
821 DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) {
822 DUK_UNREF(ctx);
823 return DUK_RET_UNSUPPORTED_ERROR;
824 }
825 #endif /* print provider */
826
827 /*
828 * CommonJS require() and modules support
829 */
830
831 #if defined(DUK_USE_COMMONJS_MODULES)
832 DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) {
833 duk_hthread *thr = (duk_hthread *) ctx;
834 duk_size_t mod_id_len;
835 duk_size_t req_id_len;
836 duk_uint8_t buf_in[DUK_BI_COMMONJS_MODULE_ID_LIMIT];
837 duk_uint8_t buf_out[DUK_BI_COMMONJS_MODULE_ID_LIMIT];
838 duk_uint8_t *p;
839 duk_uint8_t *q;
840
841 DUK_ASSERT(req_id != NULL);
842 /* mod_id may be NULL */
843 DUK_ASSERT(sizeof(buf_out) >= sizeof(buf_in)); /* bound checking requires this */
844
845 /*
846 * A few notes on the algorithm:
847 *
848 * - Terms are not allowed to begin with a period unless the term
849 * is either '.' or '..'. This simplifies implementation (and
850 * is within CommonJS modules specification).
851 *
852 * - There are few output bound checks here. This is on purpose:
853 * we check the input length and rely on the output never being
854 * longer than the input, so we cannot run out of output space.
855 *
856 * - Non-ASCII characters are processed as individual bytes and
857 * need no special treatment. However, U+0000 terminates the
858 * algorithm; this is not an issue because U+0000 is not a
859 * desirable term character anyway.
860 */
861
862 /*
863 * Set up the resolution input which is the requested ID directly
864 * (if absolute or no current module path) or with current module
865 * ID prepended (if relative and current module path exists).
866 *
867 * Suppose current module is 'foo/bar' and relative path is './quux'.
868 * The 'bar' component must be replaced so the initial input here is
869 * 'foo/bar/.././quux'.
870 */
871
872 req_id_len = DUK_STRLEN(req_id);
873 if (mod_id != NULL && req_id[0] == '.') {
874 mod_id_len = DUK_STRLEN(mod_id);
875 if (mod_id_len + 4 + req_id_len + 1 >= sizeof(buf_in)) {
876 DUK_DD(DUK_DDPRINT("resolve error: current and requested module ID don't fit into resolve input buffer"));
877 goto resolve_error;
878 }
879 (void) DUK_SNPRINTF((char *) buf_in, sizeof(buf_in), "%s/../%s", (const char *) mod_id, (const char *) req_id);
880 } else {
881 if (req_id_len + 1 >= sizeof(buf_in)) {
882 DUK_DD(DUK_DDPRINT("resolve error: requested module ID doesn't fit into resolve input buffer"));
883 goto resolve_error;
884 }
885 (void) DUK_SNPRINTF((char *) buf_in, sizeof(buf_in), "%s", (const char *) req_id);
886 }
887 buf_in[sizeof(buf_in) - 1] = (duk_uint8_t) 0;
888
889 DUK_DDD(DUK_DDDPRINT("input module id: '%s'", (const char *) buf_in));
890
891 /*
892 * Resolution loop. At the top of the loop we're expecting a valid
893 * term: '.', '..', or a non-empty identifier not starting with a period.
894 */
895
896 p = buf_in;
897 q = buf_out;
898 for (;;) {
899 duk_uint_fast8_t c;
900
901 /* Here 'p' always points to the start of a term. */
902 DUK_DDD(DUK_DDDPRINT("resolve loop top: p -> '%s', q=%p, buf_out=%p",
903 (const char *) p, (void *) q, (void *) buf_out));
904
905 c = *p++;
906 if (DUK_UNLIKELY(c == 0)) {
907 DUK_DD(DUK_DDPRINT("resolve error: requested ID must end with a non-empty term"));
908 goto resolve_error;
909 } else if (DUK_UNLIKELY(c == '.')) {
910 c = *p++;
911 if (c == '/') {
912 /* Term was '.' and is eaten entirely (including dup slashes). */
913 goto eat_dup_slashes;
914 }
915 if (c == '.' && *p == '/') {
916 /* Term was '..', backtrack resolved name by one component.
917 * q[-1] = previous slash (or beyond start of buffer)
918 * q[-2] = last char of previous component (or beyond start of buffer)
919 */
920 p++; /* eat (first) input slash */
921 DUK_ASSERT(q >= buf_out);
922 if (q == buf_out) {
923 DUK_DD(DUK_DDPRINT("resolve error: term was '..' but nothing to backtrack"));
924 goto resolve_error;
925 }
926 DUK_ASSERT(*(q - 1) == '/');
927 q--; /* backtrack to last output slash */
928 for (;;) {
929 /* Backtrack to previous slash or start of buffer. */
930 DUK_ASSERT(q >= buf_out);
931 if (q == buf_out) {
932 break;
933 }
934 if (*(q - 1) == '/') {
935 break;
936 }
937 q--;
938 }
939 goto eat_dup_slashes;
940 }
941 DUK_DD(DUK_DDPRINT("resolve error: term begins with '.' but is not '.' or '..' (not allowed now)"));
942 goto resolve_error;
943 } else if (DUK_UNLIKELY(c == '/')) {
944 /* e.g. require('/foo'), empty terms not allowed */
945 DUK_DD(DUK_DDPRINT("resolve error: empty term (not allowed now)"));
946 goto resolve_error;
947 } else {
948 for (;;) {
949 /* Copy term name until end or '/'. */
950 *q++ = c;
951 c = *p++;
952 if (DUK_UNLIKELY(c == 0)) {
953 goto loop_done;
954 } else if (DUK_UNLIKELY(c == '/')) {
955 *q++ = '/';
956 break;
957 } else {
958 /* write on next loop */
959 }
960 }
961 }
962
963 eat_dup_slashes:
964 for (;;) {
965 /* eat dup slashes */
966 c = *p;
967 if (DUK_LIKELY(c != '/')) {
968 break;
969 }
970 p++;
971 }
972 }
973 loop_done:
974
975 duk_push_lstring(ctx, (const char *) buf_out, (size_t) (q - buf_out));
976 return;
977
978 resolve_error:
979 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "cannot resolve module id: %s", (const char *) req_id);
980 }
981 #endif /* DUK_USE_COMMONJS_MODULES */
982
983 #if defined(DUK_USE_COMMONJS_MODULES)
984 DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
985 const char *str_req_id; /* requested identifier */
986 const char *str_mod_id; /* require.id of current module */
987 duk_int_t pcall_rc;
988
989 /* NOTE: we try to minimize code size by avoiding unnecessary pops,
990 * so the stack looks a bit cluttered in this function. DUK_ASSERT_TOP()
991 * assertions are used to ensure stack configuration is correct at each
992 * step.
993 */
994
995 /*
996 * Resolve module identifier into canonical absolute form.
997 */
998
999 str_req_id = duk_require_string(ctx, 0);
1000 duk_push_current_function(ctx);
1001 duk_get_prop_stridx(ctx, -1, DUK_STRIDX_ID);
1002 str_mod_id = duk_get_string(ctx, 2); /* ignore non-strings */
1003 DUK_DDD(DUK_DDDPRINT("resolve module id: requested=%!T, currentmodule=%!T",
1004 (duk_tval *) duk_get_tval(ctx, 0),
1005 (duk_tval *) duk_get_tval(ctx, 2)));
1006 duk__bi_global_resolve_module_id(ctx, str_req_id, str_mod_id);
1007 str_req_id = NULL;
1008 str_mod_id = NULL;
1009 DUK_DDD(DUK_DDDPRINT("resolved module id: requested=%!T, currentmodule=%!T, result=%!T",
1010 (duk_tval *) duk_get_tval(ctx, 0),
1011 (duk_tval *) duk_get_tval(ctx, 2),
1012 (duk_tval *) duk_get_tval(ctx, 3)));
1013
1014 /* [ requested_id require require.id resolved_id ] */
1015 DUK_ASSERT_TOP(ctx, 4);
1016
1017 /*
1018 * Cached module check.
1019 *
1020 * If module has been loaded or its loading has already begun without
1021 * finishing, return the same cached value ('exports'). The value is
1022 * registered when module load starts so that circular references can
1023 * be supported to some extent.
1024 */
1025
1026 /* [ requested_id require require.id resolved_id ] */
1027 DUK_ASSERT_TOP(ctx, 4);
1028
1029 duk_push_hobject_bidx(ctx, DUK_BIDX_DUKTAPE);
1030 duk_get_prop_stridx(ctx, 4, DUK_STRIDX_MOD_LOADED); /* Duktape.modLoaded */
1031 (void) duk_require_hobject(ctx, 5);
1032
1033 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded ] */
1034 DUK_ASSERT_TOP(ctx, 6);
1035
1036 duk_dup(ctx, 3);
1037 if (duk_get_prop(ctx, 5)) {
1038 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
1039 DUK_DD(DUK_DDPRINT("module already loaded: %!T",
1040 (duk_tval *) duk_get_tval(ctx, 3)));
1041 duk_get_prop_stridx(ctx, -1, DUK_STRIDX_EXPORTS); /* return module.exports */
1042 return 1;
1043 }
1044
1045 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined ] */
1046 DUK_ASSERT_TOP(ctx, 7);
1047
1048 /*
1049 * Module not loaded (and loading not started previously).
1050 *
1051 * Create a new require() function with 'id' set to resolved ID
1052 * of module being loaded. Also create 'exports' and 'module'
1053 * tables but don't register exports to the loaded table yet.
1054 * We don't want to do that unless the user module search callbacks
1055 * succeeds in finding the module.
1056 */
1057
1058 DUK_DD(DUK_DDPRINT("module not yet loaded: %!T",
1059 (duk_tval *) duk_get_tval(ctx, 3)));
1060
1061 /* Fresh require: require.id is left configurable (but not writable)
1062 * so that is not easy to accidentally tweak it, but it can still be
1063 * done with Object.defineProperty().
1064 *
1065 * XXX: require.id could also be just made non-configurable, as there
1066 * is no practical reason to touch it.
1067 */
1068 duk_push_c_function(ctx, duk_bi_global_object_require, 1 /*nargs*/);
1069 duk_dup(ctx, 3);
1070 duk_xdef_prop_stridx(ctx, 7, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_C); /* a fresh require() with require.id = resolved target module id */
1071
1072 /* Module table:
1073 * - module.exports: initial exports table (may be replaced by user)
1074 * - module.id is non-writable and non-configurable, as the CommonJS
1075 * spec suggests this if possible.
1076 */
1077 duk_push_object(ctx); /* exports */
1078 duk_push_object(ctx); /* module */
1079 duk_dup(ctx, -2);
1080 duk_xdef_prop_stridx(ctx, 9, DUK_STRIDX_EXPORTS, DUK_PROPDESC_FLAGS_WC); /* module.exports = exports */
1081 duk_dup(ctx, 3); /* resolved id: require(id) must return this same module */
1082 duk_xdef_prop_stridx(ctx, 9, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_NONE); /* module.id = resolved_id */
1083 duk_compact(ctx, 9); /* module table remains registered to modLoaded, minimize its size */
1084
1085 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module ] */
1086 DUK_ASSERT_TOP(ctx, 10);
1087
1088 /* Register the module table early to modLoaded[] so that we can
1089 * support circular references even in modSearch(). If an error
1090 * is thrown, we'll delete the reference.
1091 */
1092 duk_dup(ctx, 3);
1093 duk_dup(ctx, 9);
1094 duk_put_prop(ctx, 5); /* Duktape.modLoaded[resolved_id] = module */
1095
1096 /*
1097 * Call user provided module search function and build the wrapped
1098 * module source code (if necessary). The module search function
1099 * can be used to implement pure Ecmacsript, pure C, and mixed
1100 * Ecmascript/C modules.
1101 *
1102 * The module search function can operate on the exports table directly
1103 * (e.g. DLL code can register values to it). It can also return a
1104 * string which is interpreted as module source code (if a non-string
1105 * is returned the module is assumed to be a pure C one). If a module
1106 * cannot be found, an error must be thrown by the user callback.
1107 *
1108 * Because Duktape.modLoaded[] already contains the module being
1109 * loaded, circular references for C modules should also work
1110 * (although expected to be quite rare).
1111 */
1112
1113 duk_push_string(ctx, "(function(require,exports,module){");
1114
1115 /* Duktape.modSearch(resolved_id, fresh_require, exports, module). */
1116 duk_get_prop_stridx(ctx, 4, DUK_STRIDX_MOD_SEARCH); /* Duktape.modSearch */
1117 duk_dup(ctx, 3);
1118 duk_dup(ctx, 7);
1119 duk_dup(ctx, 8);
1120 duk_dup(ctx, 9); /* [ ... Duktape.modSearch resolved_id fresh_require exports module ] */
1121 pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */
1122 DUK_ASSERT_TOP(ctx, 12);
1123
1124 if (pcall_rc != DUK_EXEC_SUCCESS) {
1125 /* Delete entry in Duktape.modLoaded[] and rethrow. */
1126 goto delete_rethrow;
1127 }
1128
1129 /* If user callback did not return source code, module loading
1130 * is finished (user callback initialized exports table directly).
1131 */
1132 if (!duk_is_string(ctx, 11)) {
1133 /* User callback did not return source code, so module loading
1134 * is finished: just update modLoaded with final module.exports
1135 * and we're done.
1136 */
1137 goto return_exports;
1138 }
1139
1140 /* Finish the wrapped module source. Force resolved module ID as the
1141 * fileName so it gets set for functions defined within a module. This
1142 * also ensures loggers created within the module get the module ID as
1143 * their default logger name.
1144 */
1145 duk_push_string(ctx, "})");
1146 duk_concat(ctx, 3);
1147 duk_dup(ctx, 3); /* resolved module ID for fileName */
1148 duk_eval_raw(ctx, NULL, 0, DUK_COMPILE_EVAL);
1149
1150 /* XXX: The module wrapper function is currently anonymous and is shown
1151 * in stack traces. It would be nice to force it to match the module
1152 * name (perhaps just the cleaned up last term). At the moment 'name'
1153 * is write protected so we can't change it directly. Note that we must
1154 * not introduce an actual name binding into the function scope (which
1155 * is usually the case with a named function) because it would affect
1156 * the scope seen by the module and shadow accesses to globals of the
1157 * same name.
1158 */
1159
1160 /*
1161 * Call the wrapped module function.
1162 *
1163 * Use a protected call so that we can update Duktape.modLoaded[resolved_id]
1164 * even if the module throws an error.
1165 */
1166
1167 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
1168 DUK_ASSERT_TOP(ctx, 11);
1169
1170 duk_dup(ctx, 8); /* exports (this binding) */
1171 duk_dup(ctx, 7); /* fresh require (argument) */
1172 duk_get_prop_stridx(ctx, 9, DUK_STRIDX_EXPORTS); /* relookup exports from module.exports in case it was changed by modSearch */
1173 duk_dup(ctx, 9); /* module (argument) */
1174
1175 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
1176 DUK_ASSERT_TOP(ctx, 15);
1177
1178 pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/);
1179 if (pcall_rc != DUK_EXEC_SUCCESS) {
1180 /* Module loading failed. Node.js will forget the module
1181 * registration so that another require() will try to load
1182 * the module again. Mimic that behavior.
1183 */
1184 goto delete_rethrow;
1185 }
1186
1187 /* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
1188 DUK_ASSERT_TOP(ctx, 11);
1189
1190 /* fall through */
1191
1192 return_exports:
1193 duk_get_prop_stridx(ctx, 9, DUK_STRIDX_EXPORTS);
1194 return 1; /* return module.exports */
1195
1196 delete_rethrow:
1197 duk_dup(ctx, 3);
1198 duk_del_prop(ctx, 5); /* delete Duktape.modLoaded[resolved_id] */
1199 duk_throw(ctx); /* rethrow original error */
1200 return 0; /* not reachable */
1201 }
1202 #else
1203 DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
1204 DUK_UNREF(ctx);
1205 return DUK_RET_UNSUPPORTED_ERROR;
1206 }
1207 #endif /* DUK_USE_COMMONJS_MODULES */