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 module dmdscript.dglobal;
19 
20 import std.uri;
21 import core.stdc.stdlib;
22 import core.stdc..string;
23 import std.stdio;
24 import std.algorithm;
25 import std.math;
26 import std.exception;
27 
28 import dmdscript.script;
29 import dmdscript.protoerror;
30 import dmdscript.parse;
31 import dmdscript.text;
32 import dmdscript.dobject;
33 import dmdscript.value;
34 import dmdscript.statement;
35 import dmdscript.threadcontext;
36 import dmdscript.functiondefinition;
37 import dmdscript.scopex;
38 import dmdscript.opcodes;
39 import dmdscript.property;
40 
41 import dmdscript.dstring;
42 import dmdscript.darray;
43 import dmdscript.dregexp;
44 import dmdscript.dnumber;
45 import dmdscript.dboolean;
46 import dmdscript.dfunction;
47 import dmdscript.dnative;
48 
49 immutable(char)[] arg0string(Value[] arglist)
50 {
51     Value* v = arglist.length ? &arglist[0] : &vundefined;
52     return v.toString();
53 }
54 
55 /* ====================== Dglobal_eval ================ */
56 
57 void* Dglobal_eval(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
58 {
59     // ECMA 15.1.2.1
60     Value* v;
61     immutable(char)[] s;
62     FunctionDefinition fd;
63     ErrInfo errinfo;
64     void *result;
65 
66     //FuncLog funclog(L"Global.eval()");
67 
68     v = arglist.length ? &arglist[0] : &vundefined;
69     if(v.getType() != TypeString)
70     {
71         Value.copy(ret, v);
72         return null;
73     }
74     s = v.toString();
75     //writef("eval('%ls')\n", s);
76 
77     // Parse program
78     TopStatement[] topstatements;
79     Parser p = new Parser("eval", s, 0);
80     if(p.parseProgram(topstatements, &errinfo))
81         goto Lsyntaxerror;
82 
83     // Analyze, generate code
84     fd = new FunctionDefinition(topstatements);
85     fd.iseval = 1;
86     {
87         Scope sc;
88         sc.ctor(fd);
89         sc.src = s;
90         fd.semantic(&sc);
91         errinfo = sc.errinfo;
92         sc.dtor();
93     }
94     if(errinfo.message)
95         goto Lsyntaxerror;
96     fd.toIR(null);
97 
98     // Execute code
99     Value[] locals;
100     Value[] p1 = null;
101 
102     Value* v1 = null;
103     static immutable ntry = 0;
104 
105     if(fd.nlocals < 128)
106         v1 = cast(Value*)alloca(fd.nlocals * Value.sizeof);
107     if(v1)
108         locals = v1[0 .. fd.nlocals];
109     else
110     {
111         p1 = new Value[fd.nlocals];
112         locals = p1;
113     }
114 
115    
116     version(none)
117     {
118         Array scopex;
119         scopex.reserve(cc.scoperoot + fd.withdepth + 2);
120         for(uint u = 0; u < cc.scoperoot; u++)
121             scopex.push(cc.scopex.data[u]);
122 
123         Array *scopesave = cc.scopex;
124         cc.scopex = &scopex;
125         Dobject variablesave = cc.variable;
126         cc.variable = cc.global;
127 
128         fd.instantiate(cc.variable, 0);
129 
130         // The this value is the same as the this value of the
131         // calling context.
132         result = IR.call(cc, othis, fd.code, ret, locals);
133 
134         delete p1;
135         cc.variable = variablesave;
136         cc.scopex = scopesave;
137         return result;
138     }
139     else
140     {
141         
142         // The scope chain is initialized to contain the same objects,
143         // in the same order, as the calling context's scope chain.
144         // This includes objects added to the calling context's
145         // scope chain by WithStatement.
146 //    cc.scopex.reserve(fd.withdepth);
147 
148         // Variable instantiation is performed using the calling
149         // context's variable object and using empty
150         // property attributes
151         fd.instantiate(cc.scopex, cc.variable, 0);
152 
153         // The this value is the same as the this value of the
154         // calling context.
155         assert(cc.callerothis);
156         result = IR.call(cc, cc.callerothis, fd.code, ret, locals.ptr);
157         if(p1)
158             delete p1;
159         fd = null;
160         // if (result) writef("result = '%s'\n", (cast(Value* )result).toString());
161         return result;
162     }
163 
164 Lsyntaxerror:
165     Dobject o;
166 
167     // For eval()'s, use location of caller, not the string
168     errinfo.linnum = 0;
169 
170     ret.putVundefined();
171     o = new syntaxerror.D0(&errinfo);
172     Value* v2 = new Value;
173     v2.putVobject(o);
174     return v2;
175 }
176 
177 /* ====================== Dglobal_parseInt ================ */
178 
179 void* Dglobal_parseInt(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
180 {
181     static import std.utf;
182 
183     // ECMA 15.1.2.2
184     Value* v2;
185     immutable(char) * s;
186     immutable(char) * z;
187     d_int32 radix;
188     int sign = 1;
189     d_number number;
190     size_t i;
191     d_string string;
192 
193     string = arg0string(arglist);
194 
195     //writefln("Dglobal_parseInt('%s')", string);
196 
197     while(i < string.length)
198     {
199         size_t idx = i;
200         dchar c = std.utf.decode(string, idx);
201         if(!isStrWhiteSpaceChar(c))
202             break;
203         i = idx;
204     }
205     s = string.ptr + i;
206     i = string.length - i;
207 
208     if(i)
209     {
210         if(*s == '-')
211         {
212             sign = -1;
213             s++;
214             i--;
215         }
216         else if(*s == '+')
217         {
218             s++;
219             i--;
220         }
221     }
222 
223     radix = 0;
224     if(arglist.length >= 2)
225     {
226         v2 = &arglist[1];
227         radix = v2.toInt32();
228     }
229 
230     if(radix)
231     {
232         if(radix < 2 || radix > 36)
233         {
234             number = d_number.nan;
235             goto Lret;
236         }
237         if(radix == 16 && i >= 2 && *s == '0' &&
238            (s[1] == 'x' || s[1] == 'X'))
239         {
240             s += 2;
241             i -= 2;
242         }
243     }
244     else if(i >= 1 && *s != '0')
245     {
246         radix = 10;
247     }
248     else if(i >= 2 && (s[1] == 'x' || s[1] == 'X'))
249     {
250         radix = 16;
251         s += 2;
252         i -= 2;
253     }
254     else
255         radix = 8;
256 
257     number = 0;
258     for(z = s; i; z++, i--)
259     {
260         d_int32 n;
261         tchar c;
262 
263         c = *z;
264         if('0' <= c && c <= '9')
265             n = c - '0';
266         else if('A' <= c && c <= 'Z')
267             n = c - 'A' + 10;
268         else if('a' <= c && c <= 'z')
269             n = c - 'a' + 10;
270         else
271             break;
272         if(radix <= n)
273             break;
274         number = number * radix + n;
275     }
276     if(z == s)
277     {
278         number = d_number.nan;
279         goto Lret;
280     }
281     if(sign < 0)
282         number = -number;
283 
284     version(none)     // ECMA says to silently ignore trailing characters
285     {
286         while(z - &string[0] < string.length)
287         {
288             if(!isStrWhiteSpaceChar(*z))
289             {
290                 number = d_number.nan;
291                 goto Lret;
292             }
293             z++;
294         }
295     }
296 
297     Lret:
298     ret.putVnumber(number);
299     return null;
300 }
301 
302 /* ====================== Dglobal_parseFloat ================ */
303 
304 void* Dglobal_parseFloat(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
305 {
306     // ECMA 15.1.2.3
307     d_number n;
308     size_t endidx;
309 
310     d_string string = arg0string(arglist);
311     n = StringNumericLiteral(string, endidx, 1);
312 
313     ret.putVnumber(n);
314     return null;
315 }
316 
317 /* ====================== Dglobal_escape ================ */
318 
319 int ISURIALNUM(dchar c)
320 {
321     return (c >= 'a' && c <= 'z') ||
322            (c >= 'A' && c <= 'Z') ||
323            (c >= '0' && c <= '9');
324 }
325 
326 immutable tchar[16 + 1] TOHEX = "0123456789ABCDEF";
327 
328 void* Dglobal_escape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
329 {
330     import std..string : indexOf;
331 
332     // ECMA 15.1.2.4
333     d_string s;
334     uint escapes;
335     uint unicodes;
336     size_t slen;
337 
338     s = arg0string(arglist);
339     escapes = 0;
340     unicodes = 0;
341     foreach(dchar c; s)
342     {
343         slen++;
344         if(c >= 0x100)
345             unicodes++;
346         else
347         if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && indexOf("*@-_+./", c) == -1))
348             escapes++;
349     }
350     if((escapes + unicodes) == 0)
351     {
352         ret.putVstring(assumeUnique(s));
353         return null;
354     }
355     else
356     {
357         //writefln("s.length = %d, escapes = %d, unicodes = %d", s.length, escapes, unicodes);
358         char[] R = new char[slen + escapes * 2 + unicodes * 5];
359         char* r = R.ptr;
360         foreach(dchar c; s)
361         {
362             if(c >= 0x100)
363             {
364                 r[0] = '%';
365                 r[1] = 'u';
366                 r[2] = TOHEX[(c >> 12) & 15];
367                 r[3] = TOHEX[(c >> 8) & 15];
368                 r[4] = TOHEX[(c >> 4) & 15];
369                 r[5] = TOHEX[c & 15];
370                 r += 6;
371             }
372             else if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && indexOf("*@-_+./", c) == -1))
373             {
374                 r[0] = '%';
375                 r[1] = TOHEX[c >> 4];
376                 r[2] = TOHEX[c & 15];
377                 r += 3;
378             }
379             else
380             {
381                 r[0] = cast(tchar)c;
382                 r++;
383             }
384         }
385         assert(r - R.ptr == R.length);
386         ret.putVstring(assumeUnique(R));
387         return null;
388     }
389 }
390 
391 /* ====================== Dglobal_unescape ================ */
392 
393 void* Dglobal_unescape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
394 {
395     static import dmdscript.utf;
396 
397     // ECMA 15.1.2.5
398     d_string s;
399     d_string R;
400 
401     s = arg0string(arglist);
402     //writefln("Dglobal.unescape(s = '%s')", s);
403     for(size_t k = 0; k < s.length; k++)
404     {
405         tchar c = s[k];
406 
407         if(c == '%')
408         {
409             if(k + 6 <= s.length && s[k + 1] == 'u')
410             {
411                 uint u;
412 
413                 u = 0;
414                 for(int i = 2;; i++)
415                 {
416                     uint x;
417 
418                     if(i == 6)
419                     {
420                         dmdscript.utf.encode(R, cast(dchar)u);
421                         k += 5;
422                         goto L1;
423                     }
424                     x = s[k + i];
425                     if('0' <= x && x <= '9')
426                         x = x - '0';
427                     else if('A' <= x && x <= 'F')
428                         x = x - 'A' + 10;
429                     else if('a' <= x && x <= 'f')
430                         x = x - 'a' + 10;
431                     else
432                         break;
433                     u = (u << 4) + x;
434                 }
435             }
436             else if(k + 3 <= s.length)
437             {
438                 uint u;
439 
440                 u = 0;
441                 for(int i = 1;; i++)
442                 {
443                     uint x;
444 
445                     if(i == 3)
446                     {
447                         dmdscript.utf.encode(R, cast(dchar)u);
448                         k += 2;
449                         goto L1;
450                     }
451                     x = s[k + i];
452                     if('0' <= x && x <= '9')
453                         x = x - '0';
454                     else if('A' <= x && x <= 'F')
455                         x = x - 'A' + 10;
456                     else if('a' <= x && x <= 'f')
457                         x = x - 'a' + 10;
458                     else
459                         break;
460                     u = (u << 4) + x;
461                 }
462             }
463         }
464         R ~= c;
465         L1:
466         ;
467     }
468 
469     ret.putVstring(R);
470     return null;
471 }
472 
473 /* ====================== Dglobal_isNaN ================ */
474 
475 void* Dglobal_isNaN(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
476 {
477     // ECMA 15.1.2.6
478     Value* v;
479     d_number n;
480     d_boolean b;
481 
482     if(arglist.length)
483         v = &arglist[0];
484     else
485         v = &vundefined;
486     n = v.toNumber();
487     b = isNaN(n) ? true : false;
488     ret.putVboolean(b);
489     return null;
490 }
491 
492 /* ====================== Dglobal_isFinite ================ */
493 
494 void* Dglobal_isFinite(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
495 {
496     // ECMA 15.1.2.7
497     Value* v;
498     d_number n;
499     d_boolean b;
500 
501     if(arglist.length)
502         v = &arglist[0];
503     else
504         v = &vundefined;
505     n = v.toNumber();
506     b = isFinite(n) ? true : false;
507     ret.putVboolean(b);
508     return null;
509 }
510 
511 /* ====================== Dglobal_ URI Functions ================ */
512 
513 void* URI_error(d_string s)
514 {
515     Dobject o = new urierror.D0(s ~ "() failure");
516     Value* v = new Value;
517     v.putVobject(o);
518     return v;
519 }
520 
521 void* Dglobal_decodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
522 {
523     // ECMA v3 15.1.3.1
524     d_string s;
525 
526     s = arg0string(arglist);
527     try
528     {
529         s = std.uri.decode(s);
530     }
531     catch(URIException u)
532     {
533         ret.putVundefined();
534         return URI_error(TEXT_decodeURI);
535     }
536     ret.putVstring(s);
537     return null;
538 }
539 
540 void* Dglobal_decodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
541 {
542     // ECMA v3 15.1.3.2
543     d_string s;
544 
545     s = arg0string(arglist);
546     try
547     {
548         s = std.uri.decodeComponent(s);
549     }
550     catch(URIException u)
551     {
552         ret.putVundefined();
553         return URI_error(TEXT_decodeURIComponent);
554     }
555     ret.putVstring(s);
556     return null;
557 }
558 
559 void* Dglobal_encodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
560 {
561     // ECMA v3 15.1.3.3
562     d_string s;
563 
564     s = arg0string(arglist);
565     try
566     {
567         s = std.uri.encode(s);
568     }
569     catch(URIException u)
570     {
571         ret.putVundefined();
572         return URI_error(TEXT_encodeURI);
573     }
574     ret.putVstring(s);
575     return null;
576 }
577 
578 void* Dglobal_encodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
579 {
580     // ECMA v3 15.1.3.4
581     d_string s;
582 
583     s = arg0string(arglist);
584     try
585     {
586         s = std.uri.encodeComponent(s);
587     }
588     catch(URIException u)
589     {
590         ret.putVundefined();
591         return URI_error(TEXT_encodeURIComponent);
592     }
593     ret.putVstring(s);
594     return null;
595 }
596 
597 /* ====================== Dglobal_print ================ */
598 
599 static void dglobal_print(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
600 {
601     // Our own extension
602     if(arglist.length)
603     {
604         uint i;
605 
606         for(i = 0; i < arglist.length; i++)
607         {
608             d_string s = arglist[i].toString();
609 
610             writef("%s", s);
611         }
612     }
613 
614     ret.putVundefined();
615 }
616 
617 void* Dglobal_print(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
618 {
619     // Our own extension
620     dglobal_print(cc, othis, ret, arglist);
621     return null;
622 }
623 
624 /* ====================== Dglobal_println ================ */
625 
626 void* Dglobal_println(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
627 {
628     // Our own extension
629     dglobal_print(cc, othis, ret, arglist);
630     writef("\n");
631     return null;
632 }
633 
634 /* ====================== Dglobal_readln ================ */
635 
636 void* Dglobal_readln(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
637 {
638     static import dmdscript.utf;
639 
640     // Our own extension
641     dchar c;
642     d_string s;
643 
644     for(;; )
645     {
646         version(linux)
647         {
648             c = core.stdc.stdio.getchar();
649             if(c == EOF)
650                 break;
651         }
652         else version(Windows)
653         {
654             c = core.stdc.stdio.getchar();
655             if(c == EOF)
656                 break;
657         }
658         else version(OSX)
659         {
660             c = core.stdc.stdio.getchar();
661             if(c == EOF)
662                 break;
663         }
664         else version(FreeBSD)
665         {
666             c = core.stdc.stdio.getchar();
667             if(c == EOF)
668                 break;
669         }
670         else
671         {
672             static assert(0);
673         }
674         if(c == '\n')
675             break;
676         dmdscript.utf.encode(s, c);
677     }
678     ret.putVstring(s);
679     return null;
680 }
681 
682 /* ====================== Dglobal_getenv ================ */
683 
684 void* Dglobal_getenv(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
685 {
686     import std..string : toStringz;
687 
688     // Our own extension
689     ret.putVundefined();
690     if(arglist.length)
691     {
692         d_string s = arglist[0].toString();
693         char* p = getenv(toStringz(s));
694         if(p)
695             ret.putVstring(p[0 .. strlen(p)].idup);
696         else
697             ret.putVnull();
698     }
699     return null;
700 }
701 
702 
703 /* ====================== Dglobal_ScriptEngine ================ */
704 
705 void* Dglobal_ScriptEngine(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
706 {
707     ret.putVstring(TEXT_DMDScript);
708     return null;
709 }
710 
711 void* Dglobal_ScriptEngineBuildVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
712 {
713     ret.putVnumber(BUILD_VERSION);
714     return null;
715 }
716 
717 void* Dglobal_ScriptEngineMajorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
718 {
719     ret.putVnumber(MAJOR_VERSION);
720     return null;
721 }
722 
723 void* Dglobal_ScriptEngineMinorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
724 {
725     ret.putVnumber(MINOR_VERSION);
726     return null;
727 }
728 
729 /* ====================== Dglobal =========================== */
730 
731 class Dglobal : Dobject
732 {
733     this(tchar[][] argv)
734     {
735         super(Dobject.getPrototype());  // Dglobal.prototype is implementation-dependent
736 
737         //writef("Dglobal.Dglobal(%x)\n", this);
738 
739         Dobject f = Dfunction.getPrototype();
740 
741         classname = TEXT_global;
742 
743         // ECMA 15.1
744         // Add in built-in objects which have attribute { DontEnum }
745 
746         // Value properties
747 
748         Put(TEXT_NaN, d_number.nan, DontEnum | DontDelete);
749         Put(TEXT_Infinity, d_number.infinity, DontEnum| DontDelete);
750 		Put(TEXT_undefined, &vundefined, DontEnum| DontDelete);
751         static enum NativeFunctionData[] nfd =
752         [
753             // Function properties
754             { TEXT_eval, &Dglobal_eval, 1 },
755             { TEXT_parseInt, &Dglobal_parseInt, 2 },
756             { TEXT_parseFloat, &Dglobal_parseFloat, 1 },
757             { TEXT_escape, &Dglobal_escape, 1 },
758             { TEXT_unescape, &Dglobal_unescape, 1 },
759             { TEXT_isNaN, &Dglobal_isNaN, 1 },
760             { TEXT_isFinite, &Dglobal_isFinite, 1 },
761             { TEXT_decodeURI, &Dglobal_decodeURI, 1 },
762             { TEXT_decodeURIComponent, &Dglobal_decodeURIComponent, 1 },
763             { TEXT_encodeURI, &Dglobal_encodeURI, 1 },
764             { TEXT_encodeURIComponent, &Dglobal_encodeURIComponent, 1 },
765 
766             // Dscript unique function properties
767             { TEXT_print, &Dglobal_print, 1 },
768             { TEXT_println, &Dglobal_println, 1 },
769             { TEXT_readln, &Dglobal_readln, 0 },
770             { TEXT_getenv, &Dglobal_getenv, 1 },
771 
772             // Jscript compatible extensions
773             { TEXT_ScriptEngine, &Dglobal_ScriptEngine, 0 },
774             { TEXT_ScriptEngineBuildVersion, &Dglobal_ScriptEngineBuildVersion, 0 },
775             { TEXT_ScriptEngineMajorVersion, &Dglobal_ScriptEngineMajorVersion, 0 },
776             { TEXT_ScriptEngineMinorVersion, &Dglobal_ScriptEngineMinorVersion, 0 },
777         ];
778 
779         DnativeFunction.initialize(this, nfd, DontEnum);
780 
781         // Now handled by AssertExp()
782         // Put(TEXT_assert, Dglobal_assert(), DontEnum);
783 
784         // Constructor properties
785 
786         Put(TEXT_Object, Dobject_constructor, DontEnum);
787         Put(TEXT_Function, Dfunction_constructor, DontEnum);
788         Put(TEXT_Array, Darray_constructor, DontEnum);
789         Put(TEXT_String, Dstring_constructor, DontEnum);
790         Put(TEXT_Boolean, Dboolean_constructor, DontEnum);
791         Put(TEXT_Number, Dnumber_constructor, DontEnum);
792         Put(TEXT_Date, Ddate_constructor, DontEnum);
793         Put(TEXT_RegExp, Dregexp_constructor, DontEnum);
794         Put(TEXT_Error, Derror_constructor, DontEnum);
795 
796         foreach(d_string key, Dfunction ctor; ctorTable)
797         {
798             Put(key, ctor, DontEnum);
799         }
800 
801         // Other properties
802 
803         assert(Dmath_object);
804         Put(TEXT_Math, Dmath_object, DontEnum);
805 
806         // Build an "arguments" property out of argv[],
807         // and add it to the global object.
808         Darray arguments;
809 
810         arguments = new Darray();
811         Put(TEXT_arguments, arguments, DontDelete);
812         arguments.length.putVnumber(argv.length);
813         for(int i = 0; i < argv.length; i++)
814         {
815             arguments.Put(i, argv[i].idup, DontEnum);
816         }
817         arguments.Put(TEXT_callee, &vnull, DontEnum);
818     }
819 }