1 /* Digital Mars DMDScript source code.
2  * Copyright (c) 2000-2002 by Chromium Communications
3  * D version Copyright (c) 2004-2010 by Digital Mars
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  * written by Walter Bright
7  * http://www.digitalmars.com
8  *
9  * D2 port by Dmitry Olshansky 
10  *
11  * DMDScript is implemented in the D Programming Language,
12  * http://www.digitalmars.com/d/
13  *
14  * For a C++ implementation of DMDScript, including COM support, see
15  * http://www.digitalmars.com/dscript/cppscript.html
16  */
17 
18 
19 module dmdscript.dstring;
20 
21 import undead.regexp;
22 import std.utf;
23 import std.c.stdlib;
24 import std.c..string;
25 import std.exception;
26 import std.algorithm;
27 import std.range;
28 import std.stdio;
29 
30 import dmdscript.script;
31 import dmdscript.dobject;
32 import dmdscript.dregexp;
33 import dmdscript.darray;
34 import dmdscript.value;
35 import dmdscript.threadcontext;
36 import dmdscript.dfunction;
37 import dmdscript.text;
38 import dmdscript.property;
39 import dmdscript.errmsgs;
40 import dmdscript.dnative;
41 
42 //alias script.tchar tchar;
43 
44 /* ===================== Dstring_fromCharCode ==================== */
45 
46 void* Dstring_fromCharCode(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
47 {
48     // ECMA 15.5.3.2
49     d_string s;
50 
51     for(size_t i = 0; i < arglist.length; i++)
52     {
53         Value* v;
54         uint u;
55 
56         v = &arglist[i];
57         u = v.toUint16();
58         //writef("string.fromCharCode(%x)", u);
59         if(!std.utf.isValidDchar(u))
60         {
61             ErrInfo errinfo;
62 
63             ret.putVundefined();
64             return pthis.RuntimeError(&errinfo,
65                                       errmsgtbl[ERR_NOT_VALID_UTF],
66                                       "String", "fromCharCode()",
67                                       u);
68         }
69         dmdscript.utf.encode(s, u);
70         //writefln("s[0] = %x, s = '%s'", s[0], s);
71     }
72     ret.putVstring(s);
73     return null;
74 }
75 
76 /* ===================== Dstring_constructor ==================== */
77 
78 class DstringConstructor : Dfunction
79 {
80     this()
81     {
82         super(1, Dfunction_prototype);
83         name = "String";
84 
85         static enum NativeFunctionData nfd[] =
86         [
87             { TEXT_fromCharCode, &Dstring_fromCharCode, 1 },
88         ];
89 
90         DnativeFunction.initialize(this, nfd, 0);
91     }
92 
93     override void *Construct(CallContext *cc, Value *ret, Value[] arglist)
94     {
95         // ECMA 15.5.2
96         d_string s;
97         Dobject o;
98 
99         s = (arglist.length) ? arglist[0].toString() : TEXT_;
100         o = new Dstring(s);
101         ret.putVobject(o);
102         return null;
103     }
104 
105     override void *Call(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
106     {
107         // ECMA 15.5.1
108         d_string s;
109 
110         s = (arglist.length) ? arglist[0].toString() : TEXT_;
111         ret.putVstring(s);
112         return null;
113     }
114 }
115 
116 
117 /* ===================== Dstring_prototype_toString =============== */
118 
119 void* Dstring_prototype_toString(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
120 {
121     //writef("Dstring.prototype.toString()\n");
122     // othis must be a String
123     if(!othis.isClass(TEXT_String))
124     {
125         ErrInfo errinfo;
126 
127         ret.putVundefined();
128         return pthis.RuntimeError(&errinfo,
129                                   errmsgtbl[ERR_FUNCTION_WANTS_STRING],
130                                   TEXT_toString,
131                                   othis.classname);
132     }
133     else
134     {
135         Value *v;
136 
137         v = &(cast(Dstring)othis).value;
138         Value.copy(ret, v);
139     }
140     return null;
141 }
142 
143 /* ===================== Dstring_prototype_valueOf =============== */
144 
145 void* Dstring_prototype_valueOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
146 {
147     // Does same thing as String.prototype.toString()
148 
149     //writef("string.prototype.valueOf()\n");
150     // othis must be a String
151     if(!othis.isClass(TEXT_String))
152     {
153         ErrInfo errinfo;
154 
155         ret.putVundefined();
156         return pthis.RuntimeError(&errinfo,
157                                   errmsgtbl[ERR_FUNCTION_WANTS_STRING],
158                                   TEXT_valueOf,
159                                   othis.classname);
160     }
161     else
162     {
163         Value *v;
164 
165         v = &(cast(Dstring)othis).value;
166         Value.copy(ret, v);
167     }
168     return null;
169 }
170 
171 /* ===================== Dstring_prototype_charAt =============== */
172 
173 void* Dstring_prototype_charAt(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
174 {
175     // ECMA 15.5.4.4
176 
177     Value *v;
178     int pos;            // ECMA says pos should be a d_number,
179                         // but int should behave the same
180     d_string s;
181     d_string result;
182 
183     v = &othis.value;
184     s = v.toString();
185     v = arglist.length ? &arglist[0] : &vundefined;
186     pos = cast(int)v.toInteger();
187 
188     result = TEXT_;
189 
190     if(pos >= 0)
191     {
192         size_t idx;
193 
194         while(1)
195         {
196             if(idx == s.length)
197                 break;
198             if(pos == 0)
199             {
200                 result = s[idx .. idx + std.utf.stride(s, idx)];
201                 break;
202             }
203             idx += std.utf.stride(s, idx);
204             pos--;
205         }
206     }
207 
208     ret.putVstring(result);
209     return null;
210 }
211 
212 /* ===================== Dstring_prototype_charCodeAt ============= */
213 
214 void* Dstring_prototype_charCodeAt(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
215 {
216     // ECMA 15.5.4.5
217 
218     Value *v;
219     int pos;            // ECMA says pos should be a d_number,
220                         // but int should behave the same
221     d_string s;
222     uint len;
223     d_number result;
224 
225     v = &othis.value;
226     s = v.toString();
227     v = arglist.length ? &arglist[0] : &vundefined;
228     pos = cast(int)v.toInteger();
229 
230     result = d_number.nan;
231 
232     if(pos >= 0)
233     {
234         size_t idx;
235 
236         while(1)
237         {
238             assert(idx <= s.length);
239             if(idx == s.length)
240                 break;
241             if(pos == 0)
242             {
243                 result = std.utf.decode(s, idx);
244                 break;
245             }
246             idx += std.utf.stride(s, idx);
247             pos--;
248         }
249     }
250 
251     ret.putVnumber(result);
252     return null;
253 }
254 
255 /* ===================== Dstring_prototype_concat ============= */
256 
257 void* Dstring_prototype_concat(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
258 {
259     // ECMA v3 15.5.4.6
260     d_string s;
261 
262     //writefln("Dstring.prototype.concat()");
263 
264     s = othis.value.toString();
265     for(size_t a = 0; a < arglist.length; a++)
266         s ~= arglist[a].toString();
267 
268     ret.putVstring(s);
269     return null;
270 }
271 
272 /* ===================== Dstring_prototype_indexOf ============= */
273 
274 void* Dstring_prototype_indexOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
275 {
276     // ECMA 15.5.4.6
277     // String.prototype.indexOf(searchString, position)
278 
279     Value* v1;
280     Value* v2;
281     ptrdiff_t pos;            // ECMA says pos should be a d_number,
282                         // but I can't find a reason.
283     d_string s;
284     size_t sUCSdim;
285 
286     d_string searchString;
287     ptrdiff_t k;
288 
289     Value xx;
290     xx.putVobject(othis);
291     s = xx.toString();
292     sUCSdim = std.utf.toUCSindex(s, s.length);
293 
294     v1 = arglist.length ? &arglist[0] : &vundefined;
295     v2 = (arglist.length >= 2) ? &arglist[1] : &vundefined;
296 
297     searchString = v1.toString();
298     pos = cast(int)v2.toInteger();
299 
300     if(pos < 0)
301         pos = 0;
302     else if(pos > sUCSdim)
303         pos = sUCSdim;
304 
305     if(searchString.length == 0)
306         k = pos;
307     else
308     {
309         pos = std.utf.toUTFindex(s, pos);
310         k = std..string.indexOf(s[pos .. $], searchString);
311         if(k != -1)
312             k = std.utf.toUCSindex(s, pos + k);
313     }
314 
315     ret.putVnumber(k);
316     return null;
317 }
318 
319 /* ===================== Dstring_prototype_lastIndexOf ============= */
320 
321 void* Dstring_prototype_lastIndexOf(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
322 {
323     // ECMA v3 15.5.4.8
324     // String.prototype.lastIndexOf(searchString, position)
325 
326     Value *v1;
327     ptrdiff_t pos;            // ECMA says pos should be a d_number,
328                         // but I can't find a reason.
329     d_string s;
330     size_t sUCSdim;
331     d_string searchString;
332     ptrdiff_t k;
333 
334     version(all)
335     {
336         {
337             // This is the 'transferable' version
338             Value *v;
339             void *a;
340             v = othis.Get(TEXT_toString);
341             a = v.Call(cc, othis, ret, null);
342             if(a)                       // if exception was thrown
343                 return a;
344             s = ret.toString();
345         }
346     }
347     else
348     {
349         // the 'builtin' version
350         s = othis.value.toString();
351     }
352     sUCSdim = std.utf.toUCSindex(s, s.length);
353 
354     v1 = arglist.length ? &arglist[0] : &vundefined;
355     searchString = v1.toString();
356     if(arglist.length >= 2)
357     {
358         d_number n;
359         Value *v = &arglist[1];
360 
361         n = v.toNumber();
362         if(std.math.isnan(n) || n > sUCSdim)
363             pos = sUCSdim;
364         else if(n < 0)
365             pos = 0;
366         else
367             pos = cast(int)n;
368     }
369     else
370         pos = sUCSdim;
371 
372     //writef("len = %d, p = '%ls'\n", len, p);
373     //writef("pos = %d, sslen = %d, ssptr = '%ls'\n", pos, sslen, ssptr);
374     //writefln("s = '%s', pos = %s, searchString = '%s'", s, pos, searchString);
375 
376     if(searchString.length == 0)
377         k = pos;
378     else
379     {
380         pos = std.utf.toUTFindex(s, pos);
381         pos += searchString.length;
382         if(pos > s.length)
383             pos = s.length;
384         k = std..string.lastIndexOf(s[0 .. pos], searchString);
385         //writefln("s = '%s', pos = %s, searchString = '%s', k = %d", s, pos, searchString, k);
386         if(k != -1)
387             k = std.utf.toUCSindex(s, k);
388     }
389     ret.putVnumber(k);
390     return null;
391 }
392 
393 /* ===================== Dstring_prototype_localeCompare ============= */
394 
395 void* Dstring_prototype_localeCompare(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
396 {
397     // ECMA v3 15.5.4.9
398     d_string s1;
399     d_string s2;
400     d_number n;
401     Value *v;
402 
403     v = &othis.value;
404     s1 = v.toString();
405     s2 = arglist.length ? arglist[0].toString() : vundefined.toString();
406     n = localeCompare(cc, s1, s2);
407     ret.putVnumber(n);
408     return null;
409 }
410 
411 /* ===================== Dstring_prototype_match ============= */
412 
413 void* Dstring_prototype_match(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
414 {
415     // ECMA v3 15.5.4.10
416     Dregexp r;
417     Dobject o;
418 
419     if(arglist.length && !arglist[0].isPrimitive() &&
420        (o = arglist[0].toObject()).isDregexp())
421     {
422     }
423     else
424     {
425         Value regret;
426 
427         regret.putVobject(null);
428         Dregexp.getConstructor().Construct(cc, &regret, arglist);
429         o = regret.object;
430     }
431 
432     r = cast(Dregexp)o;
433     if(r.global.dbool)
434     {
435         Darray a = new Darray;
436         d_int32 n;
437         d_int32 i;
438         d_int32 lasti;
439 
440         i = 0;
441         lasti = 0;
442         for(n = 0;; n++)
443         {
444             r.lastIndex.putVnumber(i);
445             Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_STRING);
446             if(!ret..string)             // if match failed
447             {
448                 r.lastIndex.putVnumber(i);
449                 break;
450             }
451             lasti = i;
452             i = cast(d_int32)r.lastIndex.toInt32();
453             if(i == lasti)              // if no source was consumed
454                 i++;                    // consume a character
455 
456             a.Put(n, ret, 0);           // a[n] = ret;
457         }
458         ret.putVobject(a);
459     }
460     else
461     {
462         Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_ARRAY);
463     }
464     return null;
465 }
466 
467 /* ===================== Dstring_prototype_replace ============= */
468 
469 void* Dstring_prototype_replace(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
470 {
471     // ECMA v3 15.5.4.11
472     // String.prototype.replace(searchValue, replaceValue)
473 
474     d_string string;
475     d_string searchString;
476     d_string newstring;
477     Value *searchValue;
478     Value *replaceValue;
479     Dregexp r;
480     RegExp re;
481     d_string replacement;
482     d_string result;
483     int m;
484     int i;
485     int lasti;
486     regmatch_t[1] pmatch;
487     Dfunction f;
488     Value* v;
489 
490     v = &othis.value;
491     string = v.toString();
492     searchValue = (arglist.length >= 1) ? &arglist[0] : &vundefined;
493     replaceValue = (arglist.length >= 2) ? &arglist[1] : &vundefined;
494     r = Dregexp.isRegExp(searchValue);
495     f = Dfunction.isFunction(replaceValue);
496     if(r)
497     {
498         int offset = 0;
499 
500         re = r.re;
501         i = 0;
502         result = string;
503 
504         r.lastIndex.putVnumber(0);
505         for(;; )
506         {
507             Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_STRING);
508             if(!ret..string)             // if match failed
509                 break;
510 
511             m = re.re_nsub;
512             if(f)
513             {
514                 Value* alist;
515 
516                 alist = cast(Value *)alloca((m + 3) * Value.sizeof);
517                 assert(alist);
518                 alist[0].putVstring(ret..string);
519                 for(i = 0; i < m; i++)
520                 {
521                     alist[1 + i].putVstring(
522                         string[re.pmatch[1 + i].rm_so .. re.pmatch[1 + i].rm_eo]);
523                 }
524                 alist[m + 1].putVnumber(re.pmatch[0].rm_so);
525                 alist[m + 2].putVstring(string);
526                 f.Call(cc, f, ret, alist[0 .. m + 3]);
527                 replacement = ret.toString();
528             }
529             else
530             {
531                 newstring = replaceValue.toString();
532                 replacement = re.replace(newstring);
533             }
534             ptrdiff_t starti = re.pmatch[0].rm_so + offset;
535             ptrdiff_t endi = re.pmatch[0].rm_eo + offset;
536             result = string[0 .. starti] ~
537                      replacement ~
538                      string[endi .. $];
539 
540             if(re.attributes & RegExp.REA.global)
541             {
542                 offset += replacement.length - (endi - starti);
543 
544                 // If no source was consumed, consume a character
545                 lasti = i;
546                 i = cast(d_int32)r.lastIndex.toInt32();
547                 if(i == lasti)
548                 {
549                     i++;
550                     r.lastIndex.putVnumber(i);
551                 }
552             }
553             else
554                 break;
555         }
556     }
557     else
558     {
559         searchString = searchValue.toString();
560         ptrdiff_t match = std..string.indexOf(string, searchString);
561         if(match >= 0)
562         {
563             pmatch[0].rm_so = match;
564             pmatch[0].rm_eo = match + searchString.length;
565             if(f)
566             {
567                 Value[3] alist;
568 
569                 alist[0].putVstring(searchString);
570                 alist[1].putVnumber(pmatch[0].rm_so);
571                 alist[2].putVstring(string);
572                 f.Call(cc, f, ret, alist);
573                 replacement = ret.toString();
574             }
575             else
576             {
577                 newstring = replaceValue.toString();
578                 replacement = RegExp.replace3(newstring, string, pmatch);
579             }
580             result = string[0 .. match] ~
581                      replacement ~
582                      string[match + searchString.length .. $];
583         }
584         else
585         {
586             result = string;
587         }
588     }
589 
590     ret.putVstring(result);
591     return null;
592 }
593 
594 /* ===================== Dstring_prototype_search ============= */
595 
596 void* Dstring_prototype_search(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
597 {
598     // ECMA v3 15.5.4.12
599     Dregexp r;
600     Dobject o;
601 
602     //writef("String.prototype.search()\n");
603     if(arglist.length && !arglist[0].isPrimitive() &&
604        (o = arglist[0].toObject()).isDregexp())
605     {
606     }
607     else
608     {
609         Value regret;
610 
611         regret.putVobject(null);
612         Dregexp.getConstructor().Construct(cc, &regret, arglist);
613         o = regret.object;
614     }
615 
616     r = cast(Dregexp)o;
617     Dregexp.exec(r, ret, (&othis.value)[0 .. 1], EXEC_INDEX);
618     return null;
619 }
620 
621 /* ===================== Dstring_prototype_slice ============= */
622 
623 void* Dstring_prototype_slice(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
624 {
625     // ECMA v3 15.5.4.13
626     ptrdiff_t start;
627     ptrdiff_t end;
628     ptrdiff_t sUCSdim;
629     d_string s;
630     d_string r;
631     Value *v;
632 
633     v = &othis.value;
634     s = v.toString();
635     sUCSdim = std.utf.toUCSindex(s, s.length);
636     switch(arglist.length)
637     {
638     case 0:
639         start = 0;
640         end = sUCSdim;
641         break;
642 
643     case 1:
644         start = arglist[0].toInt32();
645         end = sUCSdim;
646         break;
647 
648     default:
649         start = arglist[0].toInt32();
650         end = arglist[1].toInt32();
651         break;
652     }
653 
654     if(start < 0)
655     {
656         start += sUCSdim;
657         if(start < 0)
658             start = 0;
659     }
660     else if(start >= sUCSdim)
661         start = sUCSdim;
662 
663     if(end < 0)
664     {
665         end += sUCSdim;
666         if(end < 0)
667             end = 0;
668     }
669     else if(end >= sUCSdim)
670         end = sUCSdim;
671 
672     if(start > end)
673         end = start;
674 
675     start = toUTFindex(s, start);
676     end = toUTFindex(s, end);
677     r = s[start .. end];
678 
679     ret.putVstring(r);
680     return null;
681 }
682 
683 
684 /* ===================== Dstring_prototype_split ============= */
685 
686 void* Dstring_prototype_split(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
687 {
688     // ECMA v3 15.5.4.14
689     // String.prototype.split(separator, limit)
690     size_t lim;
691     size_t p;
692     size_t q;
693     size_t e;
694     Value* separator = &vundefined;
695     Value* limit = &vundefined;
696     Dregexp R;
697     RegExp re;
698     d_string rs;
699     d_string T;
700     d_string S;
701     Darray A;
702     int str;
703 
704     //writefln("Dstring_prototype_split()");
705     switch(arglist.length)
706     {
707     default:
708         limit = &arglist[1];
709         goto case;
710     case 1:
711         separator = &arglist[0];
712         goto case;
713     case 0:
714         break;
715     }
716 
717     Value *v;
718     v = &othis.value;
719     S = v.toString();
720     A = new Darray;
721     if(limit.isUndefined())
722         lim = ~0u;
723     else
724         lim = limit.toUint32();
725     p = 0;
726     R = Dregexp.isRegExp(separator);
727     if(R)       // regular expression
728     {
729         re = R.re;
730         assert(re);
731         rs = null;
732         str = 0;
733     }
734     else        // string
735     {
736         re = null;
737         rs = separator.toString();
738         str = 1;
739     }
740     if(lim == 0)
741         goto Lret;
742 
743     // ECMA v3 15.5.4.14 is specific: "If separator is undefined, then the
744     // result array contains just one string, which is the this value
745     // (converted to a string)." However, neither Javascript nor Jscript
746     // do that, they regard an undefined as being the string "undefined".
747     // We match Javascript/Jscript behavior here, not ECMA.
748 
749     // Uncomment for ECMA compatibility
750     //if (!separator.isUndefined())
751     {
752         //writefln("test1 S = '%s', rs = '%s'", S, rs);
753         if(S.length)
754         {
755             L10:
756             for(q = p; q != S.length; q++)
757             {
758                 if(str)                 // string
759                 {
760                     if(q + rs.length <= S.length && !memcmp(S.ptr + q, rs.ptr, rs.length * tchar.sizeof))
761                     {
762                         e = q + rs.length;
763                         if(e != p)
764                         {
765                             T = S[p .. q];
766                             A.Put(cast(uint)A.length.number, T, 0);
767                             if(A.length.number == lim)
768                                 goto Lret;
769                             p = e;
770                             goto L10;
771                         }
772                     }
773                 }
774                 else            // regular expression
775                 {
776                     if(re.test(S, q))
777                     {
778                         q = re.pmatch[0].rm_so;
779                         e = re.pmatch[0].rm_eo;
780                         if(e != p)
781                         {
782                             T = S[p .. q];
783                             //writefln("S = '%s', T = '%s', p = %d, q = %d, e = %d\n", S, T, p, q, e);
784                             A.Put(cast(uint)A.length.number, T, 0);
785                             if(A.length.number == lim)
786                                 goto Lret;
787                             p = e;
788                             for(uint i = 0; i < re.re_nsub; i++)
789                             {
790                                 ptrdiff_t so = re.pmatch[1 + i].rm_so;
791                                 ptrdiff_t eo = re.pmatch[1 + i].rm_eo;
792 
793                                 //writefln("i = %d, nsub = %s, so = %s, eo = %s, S.length = %s", i, re.re_nsub, so, eo, S.length);
794                                 if(so != -1 && eo != -1)
795                                     T = S[so .. eo];
796                                 else
797                                     T = null;
798                                 A.Put(cast(uint)A.length.number, T, 0);
799                                 if(A.length.number == lim)
800                                     goto Lret;
801                             }
802                             goto L10;
803                         }
804                     }
805                 }
806             }
807             T = S[p .. S.length];
808             A.Put(cast(uint)A.length.number, T, 0);
809             goto Lret;
810         }
811         if(str)                 // string
812         {
813             if(rs.length <= S.length && S[0 .. rs.length] == rs[])
814                 goto Lret;
815         }
816         else            // regular expression
817         {
818             if(re.test(S, 0))
819                 goto Lret;
820         }
821     }
822 
823     A.Put(0u, S, 0);
824     Lret:
825     ret.putVobject(A);
826     return null;
827 }
828 
829 
830 /* ===================== Dstring_prototype_substr ============= */
831 
832 void *dstring_substring(d_string s, size_t sUCSdim, d_number start, d_number end, Value *ret)
833 {
834     d_string sb;
835     d_int32 sb_len;
836 
837     if(std.math.isnan(start))
838         start = 0;
839     else if(start > sUCSdim)
840         start = sUCSdim;
841     else if(start < 0)
842         start = 0;
843 
844     if(std.math.isnan(end))
845         end = 0;
846     else if(end > sUCSdim)
847         end = sUCSdim;
848     else if(end < 0)
849         end = 0;
850 
851     if(end < start)             // swap
852     {
853         d_number t;
854 
855         t = start;
856         start = end;
857         end = t;
858     }
859 
860     size_t st = std.utf.toUTFindex(s, cast(size_t)start);
861     size_t en = std.utf.toUTFindex(s, cast(size_t)end);
862     sb = s[st .. en];
863 
864     ret.putVstring(sb);
865     return null;
866 }
867 
868 void* Dstring_prototype_substr(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
869 {
870     // Javascript: TDG pg. 689
871     // String.prototype.substr(start, length)
872     d_number start;
873     d_number length;
874     d_string s;
875 
876     s = othis.value.toString();
877     size_t sUCSdim = std.utf.toUCSindex(s, s.length);
878     start = 0;
879     length = 0;
880     if(arglist.length >= 1)
881     {
882         start = arglist[0].toInteger();
883         if(start < 0)
884             start = sUCSdim + start;
885         if(arglist.length >= 2)
886         {
887             length = arglist[1].toInteger();
888             if(std.math.isnan(length) || length < 0)
889                 length = 0;
890         }
891         else
892             length = sUCSdim - start;
893     }
894 
895     return dstring_substring(s, sUCSdim, start, start + length, ret);
896 }
897 
898 /* ===================== Dstring_prototype_substring ============= */
899 
900 void* Dstring_prototype_substring(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
901 {
902     // ECMA 15.5.4.9
903     // String.prototype.substring(start)
904     // String.prototype.substring(start, end)
905     d_number start;
906     d_number end;
907     d_string s;
908 
909     //writefln("String.prototype.substring()");
910     s = othis.value.toString();
911     size_t sUCSdim = std.utf.toUCSindex(s, s.length);
912     start = 0;
913     end = sUCSdim;
914     if(arglist.length >= 1)
915     {
916         start = arglist[0].toInteger();
917         if(arglist.length >= 2)
918             end = arglist[1].toInteger();
919         //writef("s = '%ls', start = %d, end = %d\n", s, start, end);
920     }
921 
922     void* p = dstring_substring(s, sUCSdim, start, end, ret);
923     return p;
924 }
925 
926 /* ===================== Dstring_prototype_toLowerCase ============= */
927 
928 enum CASE
929 {
930     Lower,
931     Upper,
932     LocaleLower,
933     LocaleUpper
934 };
935 
936 void *tocase(Dobject othis, Value *ret, CASE caseflag)
937 {
938     d_string s;
939 
940     s = othis.value.toString();
941     switch(caseflag)
942     {
943     case CASE.Lower:
944         s = std..string.toLower(s);
945         break;
946     case CASE.Upper:
947         s = std..string.toUpper(s);
948         break;
949     case CASE.LocaleLower:
950         s = std..string.toLower(s);
951         break;
952     case CASE.LocaleUpper:
953         s = std..string.toUpper(s);
954         break;
955     default:
956         assert(0);
957     }
958 
959     ret.putVstring(s);
960     return null;
961 }
962 
963 void* Dstring_prototype_toLowerCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
964 {
965     // ECMA 15.5.4.11
966     // String.prototype.toLowerCase()
967 
968     //writef("Dstring_prototype_toLowerCase()\n");
969     return tocase(othis, ret, CASE.Lower);
970 }
971 
972 /* ===================== Dstring_prototype_toLocaleLowerCase ============= */
973 
974 void* Dstring_prototype_toLocaleLowerCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
975 {
976     // ECMA v3 15.5.4.17
977 
978     //writef("Dstring_prototype_toLocaleLowerCase()\n");
979     return tocase(othis, ret, CASE.LocaleLower);
980 }
981 
982 /* ===================== Dstring_prototype_toUpperCase ============= */
983 
984 void* Dstring_prototype_toUpperCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
985 {
986     // ECMA 15.5.4.12
987     // String.prototype.toUpperCase()
988 
989     return tocase(othis, ret, CASE.Upper);
990 }
991 
992 /* ===================== Dstring_prototype_toLocaleUpperCase ============= */
993 
994 void* Dstring_prototype_toLocaleUpperCase(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
995 {
996     // ECMA v3 15.5.4.18
997 
998     return tocase(othis, ret, CASE.LocaleUpper);
999 }
1000 
1001 /* ===================== Dstring_prototype_anchor ============= */
1002 
1003 void *dstring_anchor(Dobject othis, Value* ret, d_string tag, d_string name, Value[] arglist)
1004 {
1005     // For example:
1006     //	"foo".anchor("bar")
1007     // produces:
1008     //	<tag name="bar">foo</tag>
1009 
1010     d_string foo = othis.value.toString();
1011     Value* va = arglist.length ? &arglist[0] : &vundefined;
1012     d_string bar = va.toString();
1013 
1014     d_string s;
1015 
1016     s = "<"     ~
1017         tag     ~
1018         " "     ~
1019         name    ~
1020         "=\""   ~
1021         bar     ~
1022         "\">"   ~
1023         foo     ~
1024         "</"    ~
1025         tag     ~
1026         ">";
1027 
1028     ret.putVstring(s);
1029     return null;
1030 }
1031 
1032 
1033 void* Dstring_prototype_anchor(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1034 {
1035     // Non-standard extension
1036     // String.prototype.anchor(anchor)
1037     // For example:
1038     //	"foo".anchor("bar")
1039     // produces:
1040     //	<A NAME="bar">foo</A>
1041 
1042     return dstring_anchor(othis, ret, "A", "NAME", arglist);
1043 }
1044 
1045 void* Dstring_prototype_fontcolor(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1046 {
1047     return dstring_anchor(othis, ret, "FONT", "COLOR", arglist);
1048 }
1049 
1050 void* Dstring_prototype_fontsize(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1051 {
1052     return dstring_anchor(othis, ret, "FONT", "SIZE", arglist);
1053 }
1054 
1055 void* Dstring_prototype_link(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1056 {
1057     return dstring_anchor(othis, ret, "A", "HREF", arglist);
1058 }
1059 
1060 
1061 /* ===================== Dstring_prototype bracketing ============= */
1062 
1063 /***************************
1064  * Produce <tag>othis</tag>
1065  */
1066 
1067 void *dstring_bracket(Dobject othis, Value* ret, d_string tag)
1068 {
1069     d_string foo = othis.value.toString();
1070     d_string s;
1071 
1072     s = "<"     ~
1073         tag     ~
1074         ">"     ~
1075         foo     ~
1076         "</"    ~
1077         tag     ~
1078         ">";
1079 
1080     ret.putVstring(s);
1081     return null;
1082 }
1083 
1084 void* Dstring_prototype_big(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1085 {
1086     // Non-standard extension
1087     // String.prototype.big()
1088     // For example:
1089     //	"foo".big()
1090     // produces:
1091     //	<BIG>foo</BIG>
1092 
1093     return dstring_bracket(othis, ret, "BIG");
1094 }
1095 
1096 void* Dstring_prototype_blink(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1097 {
1098     return dstring_bracket(othis, ret, "BLINK");
1099 }
1100 
1101 void* Dstring_prototype_bold(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1102 {
1103     return dstring_bracket(othis, ret, "B");
1104 }
1105 
1106 void* Dstring_prototype_fixed(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1107 {
1108     return dstring_bracket(othis, ret, "TT");
1109 }
1110 
1111 void* Dstring_prototype_italics(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1112 {
1113     return dstring_bracket(othis, ret, "I");
1114 }
1115 
1116 void* Dstring_prototype_small(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1117 {
1118     return dstring_bracket(othis, ret, "SMALL");
1119 }
1120 
1121 void* Dstring_prototype_strike(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1122 {
1123     return dstring_bracket(othis, ret, "STRIKE");
1124 }
1125 
1126 void* Dstring_prototype_sub(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1127 {
1128     return dstring_bracket(othis, ret, "SUB");
1129 }
1130 
1131 void* Dstring_prototype_sup(Dobject pthis, CallContext *cc, Dobject othis, Value *ret, Value[] arglist)
1132 {
1133     return dstring_bracket(othis, ret, "SUP");
1134 }
1135 
1136 
1137 
1138 /* ===================== Dstring_prototype ==================== */
1139 
1140 class DstringPrototype : Dstring
1141 {
1142     this()
1143     {
1144         super(Dobject_prototype);
1145 
1146         Put(TEXT_constructor, Dstring_constructor, DontEnum);
1147 
1148         static enum NativeFunctionData nfd[] =
1149         [
1150             { TEXT_toString, &Dstring_prototype_toString, 0 },
1151             { TEXT_valueOf, &Dstring_prototype_valueOf, 0 },
1152             { TEXT_charAt, &Dstring_prototype_charAt, 1 },
1153             { TEXT_charCodeAt, &Dstring_prototype_charCodeAt, 1 },
1154             { TEXT_concat, &Dstring_prototype_concat, 1 },
1155             { TEXT_indexOf, &Dstring_prototype_indexOf, 1 },
1156             { TEXT_lastIndexOf, &Dstring_prototype_lastIndexOf, 1 },
1157             { TEXT_localeCompare, &Dstring_prototype_localeCompare, 1 },
1158             { TEXT_match, &Dstring_prototype_match, 1 },
1159             { TEXT_replace, &Dstring_prototype_replace, 2 },
1160             { TEXT_search, &Dstring_prototype_search, 1 },
1161             { TEXT_slice, &Dstring_prototype_slice, 2 },
1162             { TEXT_split, &Dstring_prototype_split, 2 },
1163             { TEXT_substr, &Dstring_prototype_substr, 2 },
1164             { TEXT_substring, &Dstring_prototype_substring, 2 },
1165             { TEXT_toLowerCase, &Dstring_prototype_toLowerCase, 0 },
1166             { TEXT_toLocaleLowerCase, &Dstring_prototype_toLocaleLowerCase, 0 },
1167             { TEXT_toUpperCase, &Dstring_prototype_toUpperCase, 0 },
1168             { TEXT_toLocaleUpperCase, &Dstring_prototype_toLocaleUpperCase, 0 },
1169             { TEXT_anchor, &Dstring_prototype_anchor, 1 },
1170             { TEXT_fontcolor, &Dstring_prototype_fontcolor, 1 },
1171             { TEXT_fontsize, &Dstring_prototype_fontsize, 1 },
1172             { TEXT_link, &Dstring_prototype_link, 1 },
1173             { TEXT_big, &Dstring_prototype_big, 0 },
1174             { TEXT_blink, &Dstring_prototype_blink, 0 },
1175             { TEXT_bold, &Dstring_prototype_bold, 0 },
1176             { TEXT_fixed, &Dstring_prototype_fixed, 0 },
1177             { TEXT_italics, &Dstring_prototype_italics, 0 },
1178             { TEXT_small, &Dstring_prototype_small, 0 },
1179             { TEXT_strike, &Dstring_prototype_strike, 0 },
1180             { TEXT_sub, &Dstring_prototype_sub, 0 },
1181             { TEXT_sup, &Dstring_prototype_sup, 0 },
1182         ];
1183 
1184         DnativeFunction.initialize(this, nfd, DontEnum);
1185     }
1186 }
1187 
1188 /* ===================== Dstring ==================== */
1189 
1190 class Dstring : Dobject
1191 {
1192     this(d_string s)
1193     {
1194         super(getPrototype());
1195         classname = TEXT_String;
1196 
1197         Put(TEXT_length, std.utf.toUCSindex(s, s.length), DontEnum | DontDelete | ReadOnly);
1198         value.putVstring(s);
1199     }
1200 
1201     this(Dobject prototype)
1202     {
1203         super(prototype);
1204 
1205         classname = TEXT_String;
1206         Put(TEXT_length, 0, DontEnum | DontDelete | ReadOnly);
1207         value.putVstring(null);
1208     }
1209 
1210     static void initialize()
1211     {
1212         Dstring_constructor = new DstringConstructor();
1213         Dstring_prototype = new DstringPrototype();
1214 
1215         Dstring_constructor.Put(TEXT_prototype, Dstring_prototype, DontEnum | DontDelete | ReadOnly);
1216     }
1217 
1218     static Dfunction getConstructor()
1219     {
1220         return Dstring_constructor;
1221     }
1222 
1223     static Dobject getPrototype()
1224     {
1225         return Dstring_prototype;
1226     }
1227 }