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.property;
20 
21 import dmdscript.script;
22 import dmdscript.value;
23 import dmdscript.identifier;
24 
25 import dmdscript.RandAA;
26 
27 import std.c..string;
28 import std.stdio;
29 
30 // attribute flags
31 enum
32 {
33     ReadOnly       = 0x001,
34     DontEnum       = 0x002,
35     DontDelete     = 0x004,
36     Internal       = 0x008,
37     Deleted        = 0x010,
38     Locked         = 0x020,
39     DontOverride   = 0x040,
40     KeyWord        = 0x080,
41     DebugFree      = 0x100,       // for debugging help
42     Instantiate    = 0x200,       // For COM named item namespace support
43 }
44 
45 struct Property
46 {
47     uint  attributes;
48 
49     Value value;
50 }
51 
52 extern (C)
53 {
54 /* These functions are part of the internal implementation of Phobos
55  * associative arrays. It's faster to use them when we have precomputed
56  * values to use.
57  */
58 
59 struct Array
60 {
61     int   length;
62     void* ptr;
63 }
64 
65 struct aaA
66 {
67     aaA *  left;
68     aaA *  right;
69     hash_t hash;
70     /* key   */
71     /* value */
72 }
73 
74 struct BB
75 {
76     aaA*[] b;
77     size_t nodes;       // total number of aaA nodes
78 }
79 
80 struct AA
81 {
82     BB* a;
83     version(X86_64)
84     {
85     }
86     else
87     {
88         // This is here only to retain binary compatibility with the
89         // old way we did AA's. Should eventually be removed.
90         int reserved;
91     }
92 }
93 
94 long _aaRehash(AA* paa, TypeInfo keyti);
95 
96 /************************
97  * Alternate Get() version
98  */
99 
100 Property* _aaGetY(hash_t hash, Property[Value]* bb, Value* key)
101 {
102     aaA* e;
103     auto aa = cast(AA*)bb;
104 
105     if(!aa.a)
106         aa.a = new BB();
107 
108     auto aalen = aa.a.b.length;
109     if(!aalen)
110     {
111         alias aaA *pa;
112 
113         aalen = 97;
114         aa.a.b = new pa[aalen];
115     }
116 
117     //printf("hash = %d\n", hash);
118     size_t i = hash % aalen;
119     auto pe = &aa.a.b[i];
120     size_t nodes;
121     while((e = *pe) != null)
122     {
123         if(hash == e.hash)
124         {
125             Value* v = cast(Value*)(e + 1);
126             if(key.vtype == V_NUMBER)
127             {
128                 if(v.vtype == V_NUMBER && key.number == v.number)
129                     goto Lret;
130             }
131             else if(key.vtype == V_STRING)
132             {
133                 if(v.vtype == V_STRING && key..string is v..string)
134                     goto Lret;
135             }
136             auto c = key.opCmp(*v);
137             if(c == 0)
138                 goto Lret;
139             pe = (c < 0) ? &e.left : &e.right;
140         }
141         else
142             pe = (hash < e.hash) ? &e.left : &e.right;
143     }
144 
145     // Not found, create new elem
146     //printf("\tcreate new one\n");
147     e = cast(aaA *)cast(void*)new void[aaA.sizeof + Value.sizeof + Property.sizeof];
148     std.c..string.memcpy(e + 1, key, Value.sizeof);
149     e.hash = hash;
150     *pe = e;
151 
152     nodes = ++aa.a.nodes;
153     //printf("length = %d, nodes = %d\n", (*aa).length, nodes);
154     if(nodes > aalen * 4)
155     {
156         _aaRehash(aa, typeid(Value));
157     }
158 
159     Lret:
160     return cast(Property*)(cast(void *)(e + 1) + Value.sizeof);
161 }
162 
163 /************************************
164  * Alternate In() with precomputed values.
165  */
166 
167 Property* _aaInY(hash_t hash, Property[Value] bb, Value* key)
168 {
169     size_t i;
170     AA aa = *cast(AA*)&bb;
171 
172     //printf("_aaIn(), aa.length = %d, .ptr = %x\n", aa.length, cast(uint)aa.ptr);
173     if(aa.a && aa.a.b.length)
174     {
175         //printf("hash = %d\n", hash);
176         i = hash % aa.a.b.length;
177         auto e = aa.a.b[i];
178         while(e != null)
179         {
180             if(hash == e.hash)
181             {
182                 Value* v = cast(Value*)(e + 1);
183                 int c;
184                 if(key.vtype == V_NUMBER && v.vtype == V_NUMBER &&
185                    key.number == v.number)
186                     goto Lfound;
187                 c = key.opCmp(*v);
188                 if(c == 0)
189                 {
190                     Lfound:
191                     return cast(Property*)(cast(void *)(e + 1) + Value.sizeof);
192                 }
193                 else
194                     e = (c < 0) ? e.left : e.right;
195             }
196             else
197                 e = (hash < e.hash) ? e.left : e.right;
198         }
199     }
200 
201     // Not found
202     return null;
203 }
204 }
205 
206 /*********************************** PropTable *********************/
207 
208 struct PropTable
209 {
210     //Property[Value] table;
211     RandAA!(Value, Property) table;
212     PropTable* previous;
213 
214     int        opApply(int delegate(ref Property) dg)
215     {
216         initialize();
217         int result;
218         foreach(ref Property p; table)
219         {
220             result = dg(p);
221             if(result)
222                 break;
223         }
224         return result;
225     }
226 
227     int opApply(int delegate(ref Value, ref Property) dg)
228     {
229         initialize();
230         int result;
231 
232         foreach(Value key, ref Property p; table)
233         {
234             result = dg(key, p);
235             if(result)
236                 break;
237         }
238         return result;
239     }
240 
241     /*******************************
242      * Look up name and get its corresponding Property.
243      * Return null if not found.
244      */
245 
246     Property *getProperty(d_string name)
247     {
248         Value* v;
249         Property *p;
250 
251         v = get(name, Value.calcHash(name));
252         if(!v)
253             return null;
254 
255         // Work backwards from &p->value to p
256         p = cast(Property *)(cast(char *)v - uint.sizeof /*Property.value.offsetof*/);
257 
258         return p;
259     }
260 
261     Value* get(Value* key, hash_t hash)
262     {
263         uint i;
264         Property *p;
265         PropTable *t;
266 
267         //writefln("get(key = '%s', hash = x%x)", key.toString(), hash);
268         assert(key.toHash() == hash);
269         t = &this;
270         do
271         {
272             //writefln("\tt = %x", cast(uint)t);
273             t.initialize();
274             //p = *key in t.table;
275             p = t.table.findExistingAlt(*key,hash);
276 
277             if(p)
278             {
279                 //TODO: what's that assert for? -- seems to run OK without it
280                 //bombs with range violation otherwise!
281                 /*try{
282                         assert(t.table[*key] == p);
283                    }catch(Error e){
284                         writef("get(key = '%s', hash = x%x)", key.toString(), hash);
285                         //writefln("\tfound");
286                         p.value.dump();
287                    }*/
288                 //p.value.dump();
289                 return &p.value;
290             }
291             t = t.previous;
292         } while(t);
293         //writefln("\tnot found");
294         return null;                    // not found
295     }
296 
297     Value* get(d_uint32 index)
298     {
299         //writefln("get(index = %d)", index);
300         Value key;
301 
302         key.putVnumber(index);
303         return get(&key, Value.calcHash(index));
304     }
305 
306     Value* get(Identifier* id)
307     {
308         //writefln("get('%s', hash = x%x)", name, hash);
309         return get(&id.value, id.value.hash);
310         //return get(id.value.string, id.value.hash);
311     }
312 
313     Value* get(d_string name, hash_t hash)
314     {
315         //writefln("get('%s', hash = x%x)", name, hash);
316         Value key;
317 
318         key.putVstring(name);
319         return get(&key, hash);
320     }
321 
322     /*******************************
323      * Determine if property exists for this object.
324      * The enumerable flag means the DontEnum attribute cannot be set.
325      */
326 
327     int hasownproperty(Value* key, int enumerable)
328     {
329         initialize();
330         Property* p;
331 
332         p = *key in table;
333         return p && (!enumerable || !(p.attributes & DontEnum));
334     }
335 
336     int hasproperty(Value* key)
337     {
338         initialize();
339         return (*key in table) != null || (previous && previous.hasproperty(key));
340     }
341 
342     int hasproperty(d_string name)
343     {
344         Value v;
345 
346         v.putVstring(name);
347         return hasproperty(&v);
348     }
349 
350     Value* put(Value* key, hash_t hash, Value* value, uint attributes)
351     {
352         initialize();
353         Property* p;
354         //writefln("table contains %d properties",table.length);
355         //writefln("put(key = %s, hash = x%x, value = %s, attributes = x%x)", key.toString(), hash, value.toString(), attributes);
356         //writefln("put(key = %s)", key.toString());
357 
358         //p = &table[*key];
359         //version(none){
360         //writeln(cast(void*)table);
361         //p = *key in table;
362         p = table.findExistingAlt(*key,hash);
363         
364  
365         if(p)
366         {
367             Lx:
368             if(attributes & DontOverride && p.value.vtype != V_REF_ERROR ||
369                p.attributes & ReadOnly)
370             {
371                 if(p.attributes & KeyWord)
372                     return null;
373                 return &vundefined;
374             }
375 
376             PropTable* t = previous;
377             if(t)
378             {
379                 do
380                 {
381                     Property* q;
382                     t.initialize();
383                     //q = *key in t.table;
384                     q = t.table.findExistingAlt(*key,hash);
385                     if(q)
386                     {
387                         if(q.attributes & ReadOnly)
388                         {
389                             p.attributes |= ReadOnly;
390                             return &vundefined;
391                         }
392                         break;
393                     }
394                     t = t.previous;
395                 } while(t);
396             }
397 
398             // Overwrite property with new value
399             Value.copy(&p.value, value);
400             p.attributes = (attributes & ~DontOverride) | (p.attributes & (DontDelete | DontEnum));
401             return null;
402         }
403 		else{		
404             //table[*key] = Property(attributes & ~DontOverride,*value);
405             auto v = Property(attributes & ~DontOverride,*value);
406 			table.insertAlt(*key, v, hash);
407 			return null; // success
408 		}
409     }
410 
411     Value* put(d_string name, Value* value, uint attributes)
412     {
413         Value key;
414 
415         key.putVstring(name);
416 
417         //writef("PropTable::put(%p, '%ls', hash = x%x)\n", this, d_string_ptr(name), key.toHash());
418         return put(&key, Value.calcHash(name), value, attributes);
419     }
420 
421     Value* put(d_uint32 index, Value* value, uint attributes)
422     {
423         Value key;
424 
425         key.putVnumber(index);
426 
427         //writef("PropTable::put(%d)\n", index);
428         return put(&key, Value.calcHash(index), value, attributes);
429     }
430 
431     Value* put(d_uint32 index, d_string string, uint attributes)
432     {
433         Value key;
434         Value value;
435 
436         key.putVnumber(index);
437         value.putVstring(string);
438 
439         return put(&key, Value.calcHash(index), &value, attributes);
440     }
441 
442     int canput(Value* key, hash_t hash)
443     {
444         initialize();
445         Property *p;
446         PropTable *t;
447 
448         t = &this;
449         do
450         {
451             //p = *key in t.table;
452              p = t.table.findExistingAlt(*key,hash);
453             if(p)
454             {
455                 return (p.attributes & ReadOnly)
456                        ? false : true;
457             }
458             t = t.previous;
459         } while(t);
460         return true;                    // success
461     }
462 
463     int canput(d_string name)
464     {
465         Value v;
466 
467         v.putVstring(name);
468 
469         return canput(&v, v.toHash());
470     }
471 
472     int del(Value* key)
473     {
474         initialize();
475         Property *p;
476 
477         //writef("PropTable::del('%ls')\n", d_string_ptr(key.toString()));
478         p = *key in table;
479         if(p)
480         {
481             if(p.attributes & DontDelete)
482                 return false;
483             table.remove(*key);
484         }
485         return true;                    // not found
486     }
487 
488     int del(d_string name)
489     {
490         Value v;
491 
492         v.putVstring(name);
493 
494         //writef("PropTable::del('%ls')\n", d_string_ptr(name));
495         return del(&v);
496     }
497 
498     int del(d_uint32 index)
499     {
500         Value v;
501 
502         v.putVnumber(index);
503 
504         //writef("PropTable::del(%d)\n", index);
505         return del(&v);
506     }
507     void initialize()
508     {
509         if(!table)
510             table = new RandAA!(Value, Property);
511     }
512 }
513 
514