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 std.c.stdlib;
22 import std.c..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 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     // ECMA 15.1.2.2
182     Value* v2;
183     immutable(char) * s;
184     immutable(char) * z;
185     d_int32 radix;
186     int sign = 1;
187     d_number number;
188     size_t i;
189     d_string string;
190 
191     string = arg0string(arglist);
192 
193     //writefln("Dglobal_parseInt('%s')", string);
194 
195     while(i < string.length)
196     {
197         size_t idx = i;
198         dchar c = std.utf.decode(string, idx);
199         if(!isStrWhiteSpaceChar(c))
200             break;
201         i = idx;
202     }
203     s = string.ptr + i;
204     i = string.length - i;
205 
206     if(i)
207     {
208         if(*s == '-')
209         {
210             sign = -1;
211             s++;
212             i--;
213         }
214         else if(*s == '+')
215         {
216             s++;
217             i--;
218         }
219     }
220 
221     radix = 0;
222     if(arglist.length >= 2)
223     {
224         v2 = &arglist[1];
225         radix = v2.toInt32();
226     }
227 
228     if(radix)
229     {
230         if(radix < 2 || radix > 36)
231         {
232             number = d_number.nan;
233             goto Lret;
234         }
235         if(radix == 16 && i >= 2 && *s == '0' &&
236            (s[1] == 'x' || s[1] == 'X'))
237         {
238             s += 2;
239             i -= 2;
240         }
241     }
242     else if(i >= 1 && *s != '0')
243     {
244         radix = 10;
245     }
246     else if(i >= 2 && (s[1] == 'x' || s[1] == 'X'))
247     {
248         radix = 16;
249         s += 2;
250         i -= 2;
251     }
252     else
253         radix = 8;
254 
255     number = 0;
256     for(z = s; i; z++, i--)
257     {
258         d_int32 n;
259         tchar c;
260 
261         c = *z;
262         if('0' <= c && c <= '9')
263             n = c - '0';
264         else if('A' <= c && c <= 'Z')
265             n = c - 'A' + 10;
266         else if('a' <= c && c <= 'z')
267             n = c - 'a' + 10;
268         else
269             break;
270         if(radix <= n)
271             break;
272         number = number * radix + n;
273     }
274     if(z == s)
275     {
276         number = d_number.nan;
277         goto Lret;
278     }
279     if(sign < 0)
280         number = -number;
281 
282     version(none)     // ECMA says to silently ignore trailing characters
283     {
284         while(z - &string[0] < string.length)
285         {
286             if(!isStrWhiteSpaceChar(*z))
287             {
288                 number = d_number.nan;
289                 goto Lret;
290             }
291             z++;
292         }
293     }
294 
295     Lret:
296     ret.putVnumber(number);
297     return null;
298 }
299 
300 /* ====================== Dglobal_parseFloat ================ */
301 
302 void* Dglobal_parseFloat(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
303 {
304     // ECMA 15.1.2.3
305     d_number n;
306     size_t endidx;
307 
308     d_string string = arg0string(arglist);
309     n = StringNumericLiteral(string, endidx, 1);
310 
311     ret.putVnumber(n);
312     return null;
313 }
314 
315 /* ====================== Dglobal_escape ================ */
316 
317 int ISURIALNUM(dchar c)
318 {
319     return (c >= 'a' && c <= 'z') ||
320            (c >= 'A' && c <= 'Z') ||
321            (c >= '0' && c <= '9');
322 }
323 
324 tchar TOHEX[16 + 1] = "0123456789ABCDEF";
325 
326 void* Dglobal_escape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
327 {
328     // ECMA 15.1.2.4
329     d_string s;
330     uint escapes;
331     uint unicodes;
332     size_t slen;
333 
334     s = arg0string(arglist);
335     escapes = 0;
336     unicodes = 0;
337     foreach(dchar c; s)
338     {
339         slen++;
340         if(c >= 0x100)
341             unicodes++;
342         else
343         if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && std..string.indexOf("*@-_+./", c) == -1))
344             escapes++;
345     }
346     if((escapes + unicodes) == 0)
347     {
348         ret.putVstring(assumeUnique(s));
349         return null;
350     }
351     else
352     {
353         //writefln("s.length = %d, escapes = %d, unicodes = %d", s.length, escapes, unicodes);
354         char[] R = new char[slen + escapes * 2 + unicodes * 5];
355         char* r = R.ptr;
356         foreach(dchar c; s)
357         {
358             if(c >= 0x100)
359             {
360                 r[0] = '%';
361                 r[1] = 'u';
362                 r[2] = TOHEX[(c >> 12) & 15];
363                 r[3] = TOHEX[(c >> 8) & 15];
364                 r[4] = TOHEX[(c >> 4) & 15];
365                 r[5] = TOHEX[c & 15];
366                 r += 6;
367             }
368             else if(c == 0 || c >= 0x80 || (!ISURIALNUM(c) && std..string.indexOf("*@-_+./", c) == -1))
369             {
370                 r[0] = '%';
371                 r[1] = TOHEX[c >> 4];
372                 r[2] = TOHEX[c & 15];
373                 r += 3;
374             }
375             else
376             {
377                 r[0] = cast(tchar)c;
378                 r++;
379             }
380         }
381         assert(r - R.ptr == R.length);
382         ret.putVstring(assumeUnique(R));
383         return null;
384     }
385 }
386 
387 /* ====================== Dglobal_unescape ================ */
388 
389 void* Dglobal_unescape(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
390 {
391     // ECMA 15.1.2.5
392     d_string s;
393     d_string R;
394 
395     s = arg0string(arglist);
396     //writefln("Dglobal.unescape(s = '%s')", s);
397     for(size_t k = 0; k < s.length; k++)
398     {
399         tchar c = s[k];
400 
401         if(c == '%')
402         {
403             if(k + 6 <= s.length && s[k + 1] == 'u')
404             {
405                 uint u;
406 
407                 u = 0;
408                 for(int i = 2;; i++)
409                 {
410                     uint x;
411 
412                     if(i == 6)
413                     {
414                         dmdscript.utf.encode(R, cast(dchar)u);
415                         k += 5;
416                         goto L1;
417                     }
418                     x = s[k + i];
419                     if('0' <= x && x <= '9')
420                         x = x - '0';
421                     else if('A' <= x && x <= 'F')
422                         x = x - 'A' + 10;
423                     else if('a' <= x && x <= 'f')
424                         x = x - 'a' + 10;
425                     else
426                         break;
427                     u = (u << 4) + x;
428                 }
429             }
430             else if(k + 3 <= s.length)
431             {
432                 uint u;
433 
434                 u = 0;
435                 for(int i = 1;; i++)
436                 {
437                     uint x;
438 
439                     if(i == 3)
440                     {
441                         dmdscript.utf.encode(R, cast(dchar)u);
442                         k += 2;
443                         goto L1;
444                     }
445                     x = s[k + i];
446                     if('0' <= x && x <= '9')
447                         x = x - '0';
448                     else if('A' <= x && x <= 'F')
449                         x = x - 'A' + 10;
450                     else if('a' <= x && x <= 'f')
451                         x = x - 'a' + 10;
452                     else
453                         break;
454                     u = (u << 4) + x;
455                 }
456             }
457         }
458         R ~= c;
459         L1:
460         ;
461     }
462 
463     ret.putVstring(R);
464     return null;
465 }
466 
467 /* ====================== Dglobal_isNaN ================ */
468 
469 void* Dglobal_isNaN(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
470 {
471     // ECMA 15.1.2.6
472     Value* v;
473     d_number n;
474     d_boolean b;
475 
476     if(arglist.length)
477         v = &arglist[0];
478     else
479         v = &vundefined;
480     n = v.toNumber();
481     b = isnan(n) ? true : false;
482     ret.putVboolean(b);
483     return null;
484 }
485 
486 /* ====================== Dglobal_isFinite ================ */
487 
488 void* Dglobal_isFinite(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
489 {
490     // ECMA 15.1.2.7
491     Value* v;
492     d_number n;
493     d_boolean b;
494 
495     if(arglist.length)
496         v = &arglist[0];
497     else
498         v = &vundefined;
499     n = v.toNumber();
500     b = isfinite(n) ? true : false;
501     ret.putVboolean(b);
502     return null;
503 }
504 
505 /* ====================== Dglobal_ URI Functions ================ */
506 
507 void* URI_error(d_string s)
508 {
509     Dobject o = new urierror.D0(s ~ "() failure");
510     Value* v = new Value;
511     v.putVobject(o);
512     return v;
513 }
514 
515 void* Dglobal_decodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
516 {
517     // ECMA v3 15.1.3.1
518     d_string s;
519 
520     s = arg0string(arglist);
521     try
522     {
523         s = std.uri.decode(s);
524     }
525     catch(URIException u)
526     {
527         ret.putVundefined();
528         return URI_error(TEXT_decodeURI);
529     }
530     ret.putVstring(s);
531     return null;
532 }
533 
534 void* Dglobal_decodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
535 {
536     // ECMA v3 15.1.3.2
537     d_string s;
538 
539     s = arg0string(arglist);
540     try
541     {
542         s = std.uri.decodeComponent(s);
543     }
544     catch(URIException u)
545     {
546         ret.putVundefined();
547         return URI_error(TEXT_decodeURIComponent);
548     }
549     ret.putVstring(s);
550     return null;
551 }
552 
553 void* Dglobal_encodeURI(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
554 {
555     // ECMA v3 15.1.3.3
556     d_string s;
557 
558     s = arg0string(arglist);
559     try
560     {
561         s = std.uri.encode(s);
562     }
563     catch(URIException u)
564     {
565         ret.putVundefined();
566         return URI_error(TEXT_encodeURI);
567     }
568     ret.putVstring(s);
569     return null;
570 }
571 
572 void* Dglobal_encodeURIComponent(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
573 {
574     // ECMA v3 15.1.3.4
575     d_string s;
576 
577     s = arg0string(arglist);
578     try
579     {
580         s = std.uri.encodeComponent(s);
581     }
582     catch(URIException u)
583     {
584         ret.putVundefined();
585         return URI_error(TEXT_encodeURIComponent);
586     }
587     ret.putVstring(s);
588     return null;
589 }
590 
591 /* ====================== Dglobal_print ================ */
592 
593 static void dglobal_print(CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
594 {
595     // Our own extension
596     if(arglist.length)
597     {
598         uint i;
599 
600         for(i = 0; i < arglist.length; i++)
601         {
602             d_string s = arglist[i].toString();
603 
604             writef("%s", s);
605         }
606     }
607 
608     ret.putVundefined();
609 }
610 
611 void* Dglobal_print(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
612 {
613     // Our own extension
614     dglobal_print(cc, othis, ret, arglist);
615     return null;
616 }
617 
618 /* ====================== Dglobal_println ================ */
619 
620 void* Dglobal_println(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
621 {
622     // Our own extension
623     dglobal_print(cc, othis, ret, arglist);
624     writef("\n");
625     return null;
626 }
627 
628 /* ====================== Dglobal_readln ================ */
629 
630 void* Dglobal_readln(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
631 {
632     // Our own extension
633     dchar c;
634     d_string s;
635 
636     for(;; )
637     {
638         version(linux)
639         {
640             c = std.c.stdio.getchar();
641             if(c == EOF)
642                 break;
643         }
644         else version(Windows)
645         {
646             c = std.c.stdio.getchar();
647             if(c == EOF)
648                 break;
649         }
650         else version(OSX)
651         {
652             c = std.c.stdio.getchar();
653             if(c == EOF)
654                 break;
655         }
656         else version(FreeBSD)
657         {
658             c = std.c.stdio.getchar();
659             if(c == EOF)
660                 break;
661         }
662         else
663         {
664             static assert(0);
665         }
666         if(c == '\n')
667             break;
668         dmdscript.utf.encode(s, c);
669     }
670     ret.putVstring(s);
671     return null;
672 }
673 
674 /* ====================== Dglobal_getenv ================ */
675 
676 void* Dglobal_getenv(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
677 {
678     // Our own extension
679     ret.putVundefined();
680     if(arglist.length)
681     {
682         d_string s = arglist[0].toString();
683         char* p = getenv(std..string.toStringz(s));
684         if(p)
685             ret.putVstring(p[0 .. strlen(p)].idup);
686         else
687             ret.putVnull();
688     }
689     return null;
690 }
691 
692 
693 /* ====================== Dglobal_ScriptEngine ================ */
694 
695 void* Dglobal_ScriptEngine(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
696 {
697     ret.putVstring(TEXT_DMDScript);
698     return null;
699 }
700 
701 void* Dglobal_ScriptEngineBuildVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
702 {
703     ret.putVnumber(BUILD_VERSION);
704     return null;
705 }
706 
707 void* Dglobal_ScriptEngineMajorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
708 {
709     ret.putVnumber(MAJOR_VERSION);
710     return null;
711 }
712 
713 void* Dglobal_ScriptEngineMinorVersion(Dobject pthis, CallContext *cc, Dobject othis, Value* ret, Value[] arglist)
714 {
715     ret.putVnumber(MINOR_VERSION);
716     return null;
717 }
718 
719 /* ====================== Dglobal =========================== */
720 
721 class Dglobal : Dobject
722 {
723     this(tchar[][] argv)
724     {
725         super(Dobject.getPrototype());  // Dglobal.prototype is implementation-dependent
726 
727         //writef("Dglobal.Dglobal(%x)\n", this);
728 
729         Dobject f = Dfunction.getPrototype();
730 
731         classname = TEXT_global;
732 
733         // ECMA 15.1
734         // Add in built-in objects which have attribute { DontEnum }
735 
736         // Value properties
737 
738         Put(TEXT_NaN, d_number.nan, DontEnum | DontDelete);
739         Put(TEXT_Infinity, d_number.infinity, DontEnum| DontDelete);
740 		Put(TEXT_undefined, &vundefined, DontEnum| DontDelete);
741         static enum NativeFunctionData nfd[] =
742         [
743             // Function properties
744             { TEXT_eval, &Dglobal_eval, 1 },
745             { TEXT_parseInt, &Dglobal_parseInt, 2 },
746             { TEXT_parseFloat, &Dglobal_parseFloat, 1 },
747             { TEXT_escape, &Dglobal_escape, 1 },
748             { TEXT_unescape, &Dglobal_unescape, 1 },
749             { TEXT_isNaN, &Dglobal_isNaN, 1 },
750             { TEXT_isFinite, &Dglobal_isFinite, 1 },
751             { TEXT_decodeURI, &Dglobal_decodeURI, 1 },
752             { TEXT_decodeURIComponent, &Dglobal_decodeURIComponent, 1 },
753             { TEXT_encodeURI, &Dglobal_encodeURI, 1 },
754             { TEXT_encodeURIComponent, &Dglobal_encodeURIComponent, 1 },
755 
756             // Dscript unique function properties
757             { TEXT_print, &Dglobal_print, 1 },
758             { TEXT_println, &Dglobal_println, 1 },
759             { TEXT_readln, &Dglobal_readln, 0 },
760             { TEXT_getenv, &Dglobal_getenv, 1 },
761 
762             // Jscript compatible extensions
763             { TEXT_ScriptEngine, &Dglobal_ScriptEngine, 0 },
764             { TEXT_ScriptEngineBuildVersion, &Dglobal_ScriptEngineBuildVersion, 0 },
765             { TEXT_ScriptEngineMajorVersion, &Dglobal_ScriptEngineMajorVersion, 0 },
766             { TEXT_ScriptEngineMinorVersion, &Dglobal_ScriptEngineMinorVersion, 0 },
767         ];
768 
769         DnativeFunction.initialize(this, nfd, DontEnum);
770 
771         // Now handled by AssertExp()
772         // Put(TEXT_assert, Dglobal_assert(), DontEnum);
773 
774         // Constructor properties
775 
776         Put(TEXT_Object, Dobject_constructor, DontEnum);
777         Put(TEXT_Function, Dfunction_constructor, DontEnum);
778         Put(TEXT_Array, Darray_constructor, DontEnum);
779         Put(TEXT_String, Dstring_constructor, DontEnum);
780         Put(TEXT_Boolean, Dboolean_constructor, DontEnum);
781         Put(TEXT_Number, Dnumber_constructor, DontEnum);
782         Put(TEXT_Date, Ddate_constructor, DontEnum);
783         Put(TEXT_RegExp, Dregexp_constructor, DontEnum);
784         Put(TEXT_Error, Derror_constructor, DontEnum);
785 
786         foreach(d_string key, Dfunction ctor; ctorTable)
787         {
788             Put(key, ctor, DontEnum);
789         }
790 
791         // Other properties
792 
793         assert(Dmath_object);
794         Put(TEXT_Math, Dmath_object, DontEnum);
795 
796         // Build an "arguments" property out of argv[],
797         // and add it to the global object.
798         Darray arguments;
799 
800         arguments = new Darray();
801         Put(TEXT_arguments, arguments, DontDelete);
802         arguments.length.putVnumber(argv.length);
803         for(int i = 0; i < argv.length; i++)
804         {
805             arguments.Put(i, argv[i].idup, DontEnum);
806         }
807         arguments.Put(TEXT_callee, &vnull, DontEnum);
808     }
809 }