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.program;
20 
21 import std.stdio;
22 import core.stdc.stdlib;
23 
24 import dmdscript.script;
25 import dmdscript.dobject;
26 import dmdscript.dglobal;
27 import dmdscript.functiondefinition;
28 import dmdscript.statement;
29 import dmdscript.threadcontext;
30 import dmdscript.value;
31 import dmdscript.opcodes;
32 import dmdscript.darray;
33 import dmdscript.parse;
34 import dmdscript.scopex;
35 import dmdscript.text;
36 import dmdscript.property;
37 
38 class Program
39 {
40     uint errors;        // if any errors in file
41     CallContext *callcontext;
42     FunctionDefinition globalfunction;
43     static Program program;//per thread global associated data
44 
45     // Locale info
46     uint lcid;          // current locale
47     d_string slist;     // list separator
48 
49     this()
50     {
51         initContext();
52     }
53 
54     void initContext()
55     {
56         //writefln("Program.initContext()");
57         if(callcontext)                 // if already done
58             return;
59 
60         callcontext = new CallContext();
61 
62         CallContext *cc = callcontext;
63 
64         // Do object inits
65         dobject_init();
66 
67         cc.prog = this;
68 
69         // Create global object
70         cc.global = new Dglobal(null);
71 
72         Dobject[] scopex;
73         scopex ~= cc.global;
74 
75         cc.variable = cc.global;
76         cc.scopex = scopex;
77         cc.scoperoot++;
78         cc.globalroot++;
79 
80         assert(Ddate_prototype.proptable.table.length != 0);
81     }
82 
83     /**************************************************
84      * Two ways of calling this:
85      * 1. with text representing group of topstatements (pfd == null)
86      * 2. with text representing a function name & body (pfd != null)
87      */
88 
89     void compile(d_string progIdentifier, d_string srctext, FunctionDefinition *pfd)
90     {
91         TopStatement[] topstatements;
92         d_string msg;
93 
94         //writef("parse_common()\n");
95         Parser p = new Parser(progIdentifier, srctext, 1);
96 
97         ErrInfo errinfo;
98         if(p.parseProgram(topstatements, &errinfo))
99         {
100             topstatements[] = null;
101             throw new ScriptException(&errinfo);
102         }
103 
104         if(pfd)
105         {   // If we are expecting a function, we should have parsed one
106             assert(p.lastnamedfunc);
107             *pfd = p.lastnamedfunc;
108         }
109 
110         // Build empty function definition array
111         // Make globalfunction an anonymous one (by passing in null for name) so
112         // it won't get instantiated as a property
113         globalfunction = new FunctionDefinition(0, 1, null, null, null);
114 
115         // Any functions parsed in topstatements wind up in the global
116         // object (cc.global), where they are found by normal property lookups.
117         // Any global new top statements only get executed once, and so although
118         // the previous group of topstatements gets lost, it does not matter.
119 
120         // In essence, globalfunction encapsulates the *last* group of topstatements
121         // passed to script, and any previous version of globalfunction, along with
122         // previous topstatements, gets discarded.
123 
124         globalfunction.topstatements = topstatements;
125 
126         // If pfd, it is not really necessary to create a global function just
127         // so we can do the semantic analysis, we could use p.lastnamedfunc
128         // instead if we're careful to insure that p.lastnamedfunc winds up
129         // as a property of the global object.
130 
131         Scope sc;
132         sc.ctor(this, globalfunction);  // create global scope
133         sc.src = srctext;
134         globalfunction.semantic(&sc);
135 
136         msg = sc.errinfo.message;
137         if(msg)                         // if semantic() failed
138         {
139             globalfunction.topstatements[] = null;
140             globalfunction.topstatements = null;
141             globalfunction = null;
142             throw new ScriptException(&sc.errinfo);
143         }
144 
145         if(pfd)
146             // If expecting a function, that is the only topstatement we should
147             // have had
148             (*pfd).toIR(null);
149         else
150         {
151             globalfunction.toIR(null);
152         }
153 
154         // Don't need parse trees anymore, so null'ing the pointer allows
155         // the garbage collector to find & free them.
156         globalfunction.topstatements[] = null;
157         globalfunction.topstatements = null;
158     }
159 
160     /*******************************
161      * Execute program.
162      * Throw ScriptException on error.
163      */
164 
165     void execute(d_string[] args)
166     {
167         // ECMA 10.2.1
168         //writef("Program.execute(argc = %d, argv = %p)\n", argc, argv);
169         //writef("Program.execute()\n");
170 
171         initContext();
172 
173         Value[] locals;
174         Value ret;
175         Value* result;
176         CallContext *cc = callcontext;
177         Darray arguments;
178         Dobject dglobal = cc.global;
179         //Program program_save;
180 
181         // Set argv and argc for execute
182         arguments = new Darray();
183         dglobal.Put(TEXT_arguments, arguments, DontDelete | DontEnum);
184         arguments.length.putVnumber(args.length);
185         for(int i = 0; i < args.length; i++)
186         {
187             arguments.Put(i, args[i], DontEnum);
188         }
189 
190         Value[] p1;
191         Value* v;
192         version(Win32)          // eh and alloca() not working under linux
193         {
194             if(globalfunction.nlocals < 128)
195                 v = cast(Value*)alloca(globalfunction.nlocals * Value.sizeof);
196         }
197         if(v)
198             locals = v[0 .. globalfunction.nlocals];
199         else
200         {
201             p1 = new Value[globalfunction.nlocals];
202             locals = p1;
203         }
204 
205         // Instantiate global variables as properties of global
206         // object with 0 attributes
207         globalfunction.instantiate(cc.scopex, cc.variable, DontDelete);
208 
209 //	cc.scopex.reserve(globalfunction.withdepth + 1);
210 
211         // The 'this' value is the global object
212         //FIXED: NOT any longer in D 2.0, any global data is actually thread-local, so stripped all this 'saving global object' crap
213         //printf("cc.scopex.ptr = %x, cc.scopex.length = %d\n", cc.scopex.ptr, cc.scopex.length);
214         //program_save = getProgram();
215 
216         setProgram(this);
217         ret.putVundefined();
218         result = cast(Value*)IR.call(cc, cc.global, globalfunction.code, &ret, locals.ptr);
219         if(result)
220         {
221             ErrInfo errinfo;
222 
223             result.getErrInfo(&errinfo, cc.linnum);
224             cc.linnum = 0;
225             delete p1;
226             throw new ScriptException(&errinfo);
227         }
228         //writef("-Program.execute()\n");
229 
230 
231         delete p1;
232     }
233 
234     void toBuffer(ref tchar[] buf)
235     {
236         if(globalfunction)
237             globalfunction.toBuffer(buf);
238     }
239 
240     /***********************************************
241      * Get/Set Program associated with this thread.
242      * This enables multiple scripts (Programs) running simultaneously
243      * in different threads.
244      * It is needed because which Program is being run is essentially
245      * global data - and this makes it thread local data.
246      */
247 
248     static Program getProgram()
249     {
250         return program;
251     }
252 
253     static void setProgram(Program p)
254     {
255         program = p;
256     }
257 }