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