1 module wren.value; 2 import wren.common; 3 import wren.math; 4 import wren.utils; 5 6 @nogc: 7 // This defines the built-in types and their core representations in memory. 8 // Since Wren is dynamically typed, any variable can hold a value of any type, 9 // and the type can change at runtime. Implementing this efficiently is 10 // critical for performance. 11 // 12 // The main type exposed by this is [Value]. A C variable of that type is a 13 // storage location that can hold any Wren value. The stack, module variables, 14 // and instance fields are all implemented in C as variables of type Value. 15 // 16 // The built-in types for booleans, numbers, and null are unboxed: their value 17 // is stored directly in the Value, and copying a Value copies the value. Other 18 // types--classes, instances of classes, functions, lists, and strings--are all 19 // reference types. They are stored on the heap and the Value just stores a 20 // pointer to it. Copying the Value copies a reference to the same object. The 21 // Wren implementation calls these "Obj", or objects, though to a user, all 22 // values are objects. 23 // 24 // There is also a special singleton value "undefined". It is used internally 25 // but never appears as a real value to a user. It has two uses: 26 // 27 // - It is used to identify module variables that have been implicitly declared 28 // by use in a forward reference but not yet explicitly declared. These only 29 // exist during compilation and do not appear at runtime. 30 // 31 // - It is used to represent unused map entries in an ObjMap. 32 // 33 // There are two supported Value representations. The main one uses a technique 34 // called "NaN tagging" (explained in detail below) to store a number, any of 35 // the value types, or a pointer, all inside one double-precision floating 36 // point number. A larger, slower, Value type that uses a struct to store these 37 // is also supported, and is useful for debugging the VM. 38 // 39 // The representation is controlled by the `WREN_NAN_TAGGING` define. If that's 40 // defined, Nan tagging is used. 41 42 // Identifies which specific type a heap-allocated object is. 43 enum ObjType { 44 OBJ_CLASS, 45 OBJ_CLOSURE, 46 OBJ_FIBER, 47 OBJ_FN, 48 OBJ_FOREIGN, 49 OBJ_INSTANCE, 50 OBJ_LIST, 51 OBJ_MAP, 52 OBJ_MODULE, 53 OBJ_RANGE, 54 OBJ_STRING, 55 OBJ_UPVALUE 56 } 57 58 // Base struct for all heap-allocated objects. 59 struct Obj 60 { 61 ObjType type; 62 bool isDark; 63 64 // The object's class. 65 ObjClass* classObj; 66 67 // The next object in the linked list of all currently allocated objects. 68 Obj* next; 69 } 70 71 static if (WREN_NAN_TAGGING) 72 { 73 alias Value = ulong; 74 } 75 else 76 { 77 enum ValueType { 78 VAL_FALSE, 79 VAL_NULL, 80 VAL_NUM, 81 VAL_TRUE, 82 VAL_UNDEFINED, 83 VAL_OBJ 84 } 85 86 struct Value { 87 ValueType type; 88 union AsValue { 89 double num; 90 Obj* obj; 91 } 92 AsValue as; 93 } 94 } 95 96 mixin(DECLARE_BUFFER("Value", "Value")); 97 98 // These macros cast a Value to one of the specific object types. These do *not* 99 // perform any validation, so must only be used after the Value has been 100 // ensured to be the right type. 101 ObjClass* AS_CLASS(Value value) 102 { 103 return cast(ObjClass*)AS_OBJ(value); 104 } 105 106 ObjClosure* AS_CLOSURE(Value value) 107 { 108 return cast(ObjClosure*)AS_OBJ(value); 109 } 110 111 ObjFiber* AS_FIBER(Value value) 112 { 113 return cast(ObjFiber*)AS_OBJ(value); 114 } 115 116 ObjFn* AS_FN(Value value) 117 { 118 return cast(ObjFn*)AS_OBJ(value); 119 } 120 121 ObjForeign* AS_FOREIGN(Value value) 122 { 123 return cast(ObjForeign*)AS_OBJ(value); 124 } 125 126 ObjInstance* AS_INSTANCE(Value value) 127 { 128 return cast(ObjInstance*)AS_OBJ(value); 129 } 130 131 ObjList* AS_LIST(Value value) 132 { 133 return cast(ObjList*)AS_OBJ(value); 134 } 135 136 ObjMap* AS_MAP(Value value) 137 { 138 return cast(ObjMap*)AS_OBJ(value); 139 } 140 141 ObjModule* AS_MODULE(Value value) 142 { 143 return cast(ObjModule*)AS_OBJ(value); 144 } 145 146 double AS_NUM(Value value) 147 { 148 return wrenValueToNum(value); 149 } 150 151 ObjRange* AS_RANGE(Value value) 152 { 153 return cast(ObjRange*)AS_OBJ(value); 154 } 155 156 ObjString* AS_STRING(Value value) 157 { 158 return cast(ObjString*)AS_OBJ(value); 159 } 160 161 const(char)* AS_CSTRING(Value value) 162 { 163 return (AS_STRING(value).value.ptr); 164 } 165 166 // These macros promote a primitive C value to a full Wren Value. There are 167 // more defined below that are specific to the Nan tagged or other 168 // representation. 169 Value BOOL_VAL(bool value) 170 { 171 return value ? TRUE_VAL : FALSE_VAL; 172 } 173 174 Value NUM_VAL(double num) 175 { 176 return wrenNumToValue(num); 177 } 178 179 Value OBJ_VAL(T)(T* obj) 180 { 181 return wrenObjectToValue(cast(Obj*)obj); 182 } 183 184 // These perform type tests on a Value, returning `true` if the Value is of the 185 // given type. 186 bool IS_BOOL(Value value) { 187 return wrenIsBool(value); 188 } 189 190 bool IS_CLASS(Value value) { 191 return wrenIsObjType(value, ObjType.OBJ_CLASS); 192 } 193 194 bool IS_CLOSURE(Value value) { 195 return wrenIsObjType(value, ObjType.OBJ_CLOSURE); 196 } 197 198 bool IS_FIBER(Value value) { 199 return wrenIsObjType(value, ObjType.OBJ_FIBER); 200 } 201 202 bool IS_FN(Value value) { 203 return wrenIsObjType(value, ObjType.OBJ_FN); 204 } 205 206 bool IS_FOREIGN(Value value) { 207 return wrenIsObjType(value, ObjType.OBJ_FOREIGN); 208 } 209 210 bool IS_INSTANCE(Value value) { 211 return wrenIsObjType(value, ObjType.OBJ_INSTANCE); 212 } 213 214 bool IS_LIST(Value value) { 215 return wrenIsObjType(value, ObjType.OBJ_LIST); 216 } 217 218 bool IS_MAP(Value value) { 219 return wrenIsObjType(value, ObjType.OBJ_MAP); 220 } 221 222 bool IS_RANGE(Value value) { 223 return wrenIsObjType(value, ObjType.OBJ_RANGE); 224 } 225 226 bool IS_STRING(Value value) { 227 return wrenIsObjType(value, ObjType.OBJ_STRING); 228 } 229 230 // A heap-allocated string object. 231 struct ObjString 232 { 233 Obj obj; 234 235 // Number of bytes in the string, not including the null terminator. 236 uint length; 237 238 // The hash value of the string's contents. 239 uint hash; 240 241 // Inline array of the string's bytes followed by a null terminator. 242 char[] value; 243 } 244 245 mixin(DECLARE_BUFFER("String", "ObjString*")); 246 247 alias SymbolTable = StringBuffer; 248 249 // This import has to be here. Otherwise, we run into weird undefined identifier problems. 250 import wren.vm; 251 252 // Initializes the symbol table. 253 void wrenSymbolTableInit(SymbolTable* symbols) @nogc 254 { 255 wrenStringBufferInit(symbols); 256 } 257 258 // Frees all dynamically allocated memory used by the symbol table, but not the 259 // SymbolTable itself. 260 void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols) @nogc 261 { 262 wrenStringBufferClear(vm, symbols); 263 } 264 265 // Adds name to the symbol table. Returns the index of it in the table. 266 int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols, 267 const(char)* name, size_t length) @nogc 268 { 269 ObjString* symbol = AS_STRING(wrenNewStringLength(vm, name, length)); 270 271 wrenPushRoot(vm, &symbol.obj); 272 wrenStringBufferWrite(vm, symbols, symbol); 273 wrenPopRoot(vm); 274 275 return symbols.count - 1; 276 } 277 278 // Adds name to the symbol table. Returns the index of it in the table. Will 279 // use an existing symbol if already present. 280 int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols, 281 const(char)* name, size_t length) @nogc 282 { 283 // See if the symbol is already defined. 284 int existing = wrenSymbolTableFind(symbols, name, length); 285 if (existing != -1) return existing; 286 287 // New symbol, so add it. 288 return wrenSymbolTableAdd(vm, symbols, name, length); 289 } 290 291 // Looks up name in the symbol table. Returns its index if found or -1 if not. 292 int wrenSymbolTableFind(const SymbolTable* symbols, 293 const(char)* name, size_t length) @nogc 294 { 295 // See if the symbol is already defined. 296 // TODO: O(n). Do something better. 297 for (int i = 0; i < symbols.count; i++) 298 { 299 if (wrenStringEqualsCString(cast(ObjString*)symbols.data[i], name, length)) return i; 300 } 301 302 return -1; 303 } 304 305 void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable) @nogc 306 { 307 for (int i = 0; i < symbolTable.count; i++) 308 { 309 wrenGrayObj(vm, &symbolTable.data[i].obj); 310 } 311 312 // Keep track of how much memory is still in use. 313 vm.bytesAllocated += symbolTable.capacity * (*symbolTable.data).sizeof; 314 } 315 316 // The dynamically allocated data structure for a variable that has been used 317 // by a closure. Whenever a function accesses a variable declared in an 318 // enclosing function, it will get to it through this. 319 // 320 // An upvalue can be either "closed" or "open". An open upvalue points directly 321 // to a [Value] that is still stored on the fiber's stack because the local 322 // variable is still in scope in the function where it's declared. 323 // 324 // When that local variable goes out of scope, the upvalue pointing to it will 325 // be closed. When that happens, the value gets copied off the stack into the 326 // upvalue itself. That way, it can have a longer lifetime than the stack 327 // variable. 328 struct ObjUpvalue 329 { 330 // The object header. Note that upvalues have this because they are garbage 331 // collected, but they are not first class Wren objects. 332 Obj obj; 333 334 // Pointer to the variable this upvalue is referencing. 335 Value* value; 336 337 // If the upvalue is closed (i.e. the local variable it was pointing to has 338 // been popped off the stack) then the closed-over value will be hoisted out 339 // of the stack into here. [value] will then be changed to point to this. 340 Value closed; 341 342 // Open upvalues are stored in a linked list by the fiber. This points to the 343 // next upvalue in that list. 344 ObjUpvalue* next; 345 } 346 347 348 349 // TODO: See if it's actually a perf improvement to have this in a separate 350 // struct instead of in ObjFn. 351 // Stores debugging information for a function used for things like stack 352 // traces. 353 struct FnDebug 354 { 355 // The name of the function. Heap allocated and owned by the FnDebug. 356 char* name; 357 358 // An array of line numbers. There is one element in this array for each 359 // bytecode in the function's bytecode array. The value of that element is 360 // the line in the source code that generated that instruction. 361 IntBuffer sourceLines; 362 } 363 364 // A loaded module and the top-level variables it defines. 365 // 366 // While this is an Obj and is managed by the GC, it never appears as a 367 // first-class object in Wren. 368 struct ObjModule 369 { 370 Obj obj; 371 372 // The currently defined top-level variables. 373 ValueBuffer variables; 374 375 // Symbol table for the names of all module variables. Indexes here directly 376 // correspond to entries in [variables]. 377 SymbolTable variableNames; 378 379 // The name of the module. 380 ObjString* name; 381 } 382 383 // A function object. It wraps and owns the bytecode and other debug information 384 // for a callable chunk of code. 385 // 386 // Function objects are not passed around and invoked directly. Instead, they 387 // are always referenced by an [ObjClosure] which is the real first-class 388 // representation of a function. This isn't strictly necessary if they function 389 // has no upvalues, but lets the rest of the VM assume all called objects will 390 // be closures. 391 struct ObjFn 392 { 393 Obj obj; 394 395 ByteBuffer code; 396 ValueBuffer constants; 397 398 // The module where this function was defined. 399 ObjModule* module_; 400 401 // The maximum number of stack slots this function may use. 402 int maxSlots; 403 404 // The number of upvalues this function closes over. 405 int numUpvalues; 406 407 // The number of parameters this function expects. Used to ensure that .call 408 // handles a mismatch between number of parameters and arguments. This will 409 // only be set for fns, and not ObjFns that represent methods or scripts. 410 int arity; 411 FnDebug* debug_; 412 } 413 414 // An instance of a first-class function and the environment it has closed over. 415 // Unlike [ObjFn], this has captured the upvalues that the function accesses. 416 struct ObjClosure 417 { 418 Obj obj; 419 420 // The function that this closure is an instance of. 421 ObjFn* fn; 422 423 // The upvalues this function has closed over. 424 ObjUpvalue*[] upvalues; 425 } 426 427 struct CallFrame 428 { 429 // Pointer to the current (really next-to-be-executed) instruction in the 430 // function's bytecode. 431 ubyte* ip; 432 433 // The closure being executed. 434 ObjClosure* closure; 435 436 // Pointer to the first stack slot used by this call frame. This will contain 437 // the receiver, followed by the function's parameters, then local variables 438 // and temporaries. 439 Value* stackStart; 440 } 441 442 // Tracks how this fiber has been invoked, aside from the ways that can be 443 // detected from the state of other fields in the fiber. 444 enum FiberState 445 { 446 // The fiber is being run from another fiber using a call to `try()`. 447 FIBER_TRY, 448 449 // The fiber was directly invoked by `runInterpreter()`. This means it's the 450 // initial fiber used by a call to `wrenCall()` or `wrenInterpret()`. 451 FIBER_ROOT, 452 453 // The fiber is invoked some other way. If [caller] is `NULL` then the fiber 454 // was invoked using `call()`. If [numFrames] is zero, then the fiber has 455 // finished running and is done. If [numFrames] is one and that frame's `ip` 456 // points to the first byte of code, the fiber has not been started yet. 457 FIBER_OTHER, 458 } 459 460 struct ObjFiber 461 { 462 Obj obj; 463 464 // The stack of value slots. This is used for holding local variables and 465 // temporaries while the fiber is executing. It is heap-allocated and grown 466 // as needed. 467 Value* stack; 468 469 // A pointer to one past the top-most value on the stack. 470 Value* stackTop; 471 472 // The number of allocated slots in the stack array. 473 int stackCapacity; 474 475 // The stack of call frames. This is a dynamic array that grows as needed but 476 // never shrinks. 477 CallFrame* frames; 478 479 // The number of frames currently in use in [frames]. 480 int numFrames; 481 482 // The number of [frames] allocated. 483 int frameCapacity; 484 485 // Pointer to the first node in the linked list of open upvalues that are 486 // pointing to values still on the stack. The head of the list will be the 487 // upvalue closest to the top of the stack, and then the list works downwards. 488 ObjUpvalue* openUpvalues; 489 490 // The fiber that ran this one. If this fiber is yielded, control will resume 491 // to this one. May be `NULL`. 492 ObjFiber* caller; 493 494 // If the fiber failed because of a runtime error, this will contain the 495 // error object. Otherwise, it will be null. 496 Value error; 497 498 FiberState state; 499 } 500 501 enum MethodType 502 { 503 // A primitive method implemented in C in the VM. Unlike foreign methods, 504 // this can directly manipulate the fiber's stack. 505 METHOD_PRIMITIVE, 506 507 // A primitive that handles .call on Fn. 508 METHOD_FUNCTION_CALL, 509 510 // A externally-defined C method. 511 METHOD_FOREIGN, 512 513 // A normal user-defined method. 514 METHOD_BLOCK, 515 516 // No method for the given symbol. 517 METHOD_NONE 518 } 519 520 struct Method 521 { 522 MethodType type; 523 524 // The method function itself. The [type] determines which field of the union 525 // is used. 526 union AsType 527 { 528 Primitive primitive; 529 WrenForeignMethodFn foreign; 530 ObjClosure* closure; 531 } 532 533 AsType as; 534 } 535 536 mixin(DECLARE_BUFFER("Method", "Method")); 537 538 struct ObjClass 539 { 540 Obj obj; 541 ObjClass* superclass; 542 543 // The number of fields needed for an instance of this class, including all 544 // of its superclass fields. 545 int numFields; 546 547 // The table of methods that are defined in or inherited by this class. 548 // Methods are called by symbol, and the symbol directly maps to an index in 549 // this table. This makes method calls fast at the expense of empty cells in 550 // the list for methods the class doesn't support. 551 // 552 // You can think of it as a hash table that never has collisions but has a 553 // really low load factor. Since methods are pretty small (just a type and a 554 // pointer), this should be a worthwhile trade-off. 555 MethodBuffer methods; 556 557 // The name of the class. 558 ObjString* name; 559 560 // The ClassAttribute for the class, if any 561 Value attributes; 562 } 563 564 struct ObjForeign 565 { 566 Obj obj; 567 ubyte[] data; 568 } 569 570 struct ObjInstance 571 { 572 Obj obj; 573 Value[] fields; 574 } 575 576 struct ObjList 577 { 578 Obj obj; 579 580 // The elements in the list. 581 ValueBuffer elements; 582 } 583 584 struct MapEntry 585 { 586 // The entry's key, or UNDEFINED_VAL if the entry is not in use. 587 Value key; 588 589 // The value associated with the key. If the key is UNDEFINED_VAL, this will 590 // be false to indicate an open available entry or true to indicate a 591 // tombstone -- an entry that was previously in use but was then deleted. 592 Value value; 593 } 594 595 // A hash table mapping keys to values. 596 // 597 // We use something very simple: open addressing with linear probing. The hash 598 // table is an array of entries. Each entry is a key-value pair. If the key is 599 // the special UNDEFINED_VAL, it indicates no value is currently in that slot. 600 // Otherwise, it's a valid key, and the value is the value associated with it. 601 // 602 // When entries are added, the array is dynamically scaled by GROW_FACTOR to 603 // keep the number of filled slots under MAP_LOAD_PERCENT. Likewise, if the map 604 // gets empty enough, it will be resized to a smaller array. When this happens, 605 // all existing entries are rehashed and re-added to the new array. 606 // 607 // When an entry is removed, its slot is replaced with a "tombstone". This is an 608 // entry whose key is UNDEFINED_VAL and whose value is TRUE_VAL. When probing 609 // for a key, we will continue past tombstones, because the desired key may be 610 // found after them if the key that was removed was part of a prior collision. 611 // When the array gets resized, all tombstones are discarded. 612 struct ObjMap 613 { 614 Obj obj; 615 616 // The number of entries allocated. 617 uint capacity; 618 619 // The number of entries in the map. 620 uint count; 621 622 // Pointer to a contiguous array of [capacity] entries. 623 MapEntry* entries; 624 } 625 626 struct ObjRange 627 { 628 Obj obj; 629 630 // The beginning of the range. 631 double from; 632 633 // The end of the range. May be greater or less than [from]. 634 double to; 635 636 // True if [to] is included in the range. 637 bool isInclusive; 638 } 639 640 struct WrenHandle 641 { 642 Value value; 643 644 WrenHandle* prev; 645 WrenHandle* next; 646 }; 647 648 // An IEEE 754 double-precision float is a 64-bit value with bits laid out like: 649 // 650 // 1 Sign bit 651 // | 11 Exponent bits 652 // | | 52 Mantissa (i.e. fraction) bits 653 // | | | 654 // S[Exponent-][Mantissa------------------------------------------] 655 // 656 // The details of how these are used to represent numbers aren't really 657 // relevant here as long we don't interfere with them. The important bit is NaN. 658 // 659 // An IEEE double can represent a few magical values like NaN ("not a number"), 660 // Infinity, and -Infinity. A NaN is any value where all exponent bits are set: 661 // 662 // v--NaN bits 663 // -11111111111---------------------------------------------------- 664 // 665 // Here, "-" means "doesn't matter". Any bit sequence that matches the above is 666 // a NaN. With all of those "-", it obvious there are a *lot* of different 667 // bit patterns that all mean the same thing. NaN tagging takes advantage of 668 // this. We'll use those available bit patterns to represent things other than 669 // numbers without giving up any valid numeric values. 670 // 671 // NaN values come in two flavors: "signalling" and "quiet". The former are 672 // intended to halt execution, while the latter just flow through arithmetic 673 // operations silently. We want the latter. Quiet NaNs are indicated by setting 674 // the highest mantissa bit: 675 // 676 // v--Highest mantissa bit 677 // -[NaN ]1--------------------------------------------------- 678 // 679 // If all of the NaN bits are set, it's not a number. Otherwise, it is. 680 // That leaves all of the remaining bits as available for us to play with. We 681 // stuff a few different kinds of things here: special singleton values like 682 // "true", "false", and "null", and pointers to objects allocated on the heap. 683 // We'll use the sign bit to distinguish singleton values from pointers. If 684 // it's set, it's a pointer. 685 // 686 // v--Pointer or singleton? 687 // S[NaN ]1--------------------------------------------------- 688 // 689 // For singleton values, we just enumerate the different values. We'll use the 690 // low bits of the mantissa for that, and only need a few: 691 // 692 // 3 Type bits--v 693 // 0[NaN ]1------------------------------------------------[T] 694 // 695 // For pointers, we are left with 51 bits of mantissa to store an address. 696 // That's more than enough room for a 32-bit address. Even 64-bit machines 697 // only actually use 48 bits for addresses, so we've got plenty. We just stuff 698 // the address right into the mantissa. 699 // 700 // Ta-da, double precision numbers, pointers, and a bunch of singleton values, 701 // all stuffed into a single 64-bit sequence. Even better, we don't have to 702 // do any masking or work to extract number values: they are unmodified. This 703 // means math on numbers is fast. 704 static if (WREN_NAN_TAGGING) 705 { 706 // A mask that selects the sign bit. 707 enum SIGN_BIT = cast(ulong)1 << 63; 708 709 // The bits that must be set to indicate a quiet NaN. 710 enum QNAN = cast(ulong)0x7ffc000000000000; 711 712 // If the NaN bits are set, it's not a number. 713 bool IS_NUM(Value value) 714 { 715 return (((value) & QNAN) != QNAN); 716 } 717 718 // An object pointer is a NaN with a set sign bit. 719 bool IS_OBJ(Value value) 720 { 721 return (((value) & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT)); 722 } 723 724 bool IS_FALSE(Value value) 725 { 726 return ((value) == FALSE_VAL); 727 } 728 729 bool IS_NULL(Value value) 730 { 731 return ((value) == NULL_VAL); 732 } 733 734 bool IS_UNDEFINED(Value value) 735 { 736 return ((value) == UNDEFINED_VAL); 737 } 738 739 // Masks out the tag bits used to identify the singleton value. 740 enum MASK_TAG = 7; 741 742 // Tag values for the different singleton values. 743 enum TAG_NAN = 0; 744 enum TAG_NULL = 1; 745 enum TAG_FALSE = 2; 746 enum TAG_TRUE = 3; 747 enum TAG_UNDEFINED = 4; 748 enum TAG_UNUSED2 = 5; 749 enum TAG_UNUSED3 = 6; 750 enum TAG_UNUSED4 = 7; 751 752 // Value . 0 or 1. 753 bool AS_BOOL(Value value) 754 { 755 return ((value) == TRUE_VAL); 756 } 757 758 // Value . Obj*. 759 Obj* AS_OBJ(Value value) 760 { 761 return (cast(Obj*)cast(ulong)((value) & ~(SIGN_BIT | QNAN))); 762 } 763 764 // Singleton values. 765 enum NULL_VAL = (cast(Value)cast(ulong)(QNAN | TAG_NULL)); 766 enum FALSE_VAL = (cast(Value)cast(ulong)(QNAN | TAG_FALSE)); 767 enum TRUE_VAL = (cast(Value)cast(ulong)(QNAN | TAG_TRUE)); 768 enum UNDEFINED_VAL = (cast(Value)cast(ulong)(QNAN | TAG_UNDEFINED)); 769 770 int GET_TAG(Value value) 771 { 772 return (cast(int)((value) & MASK_TAG)); 773 } 774 } 775 else 776 { 777 // Value . 0 or 1. 778 bool AS_BOOL(Value value) 779 { 780 return ((value).type == ValueType.VAL_TRUE); 781 } 782 783 // Value . Obj*. 784 Obj* AS_OBJ(Value value) 785 { 786 return ((value).as.obj); 787 } 788 789 // Determines if [value] is a garbage-collected object or not. 790 bool IS_OBJ(Value value) 791 { 792 return ((value).type == ValueType.VAL_OBJ); 793 } 794 795 bool IS_FALSE(Value value) 796 { 797 return ((value).type == ValueType.VAL_FALSE); 798 } 799 800 bool IS_NULL(Value value) 801 { 802 return ((value).type == ValueType.VAL_NULL); 803 } 804 805 bool IS_NUM(Value value) 806 { 807 return ((value).type == ValueType.VAL_NUM); 808 } 809 810 bool IS_UNDEFINED(Value value) 811 { 812 return ((value).type == ValueType.VAL_UNDEFINED); 813 } 814 815 // Singleton values. 816 enum FALSE_VAL = Value(ValueType.VAL_FALSE); 817 enum NULL_VAL = Value(ValueType.VAL_NULL); 818 enum TRUE_VAL = Value(ValueType.VAL_TRUE); 819 enum UNDEFINED_VAL = Value(ValueType.VAL_UNDEFINED); 820 } 821 822 // Returns true if [a] and [b] are strictly the same value. This is identity 823 // for object values, and value equality for unboxed values. 824 static bool wrenValuesSame(Value a, Value b) 825 { 826 static if (WREN_NAN_TAGGING) 827 { 828 return a == b; 829 } 830 else 831 { 832 if (a.type != b.type) return false; 833 if (a.type == ValueType.VAL_NUM) return a.as.num == b.as.num; 834 return a.as.obj == b.as.obj; 835 } 836 } 837 838 // Returns true if [value] is a bool. Do not call this directly, instead use 839 // [IS_BOOL]. 840 static bool wrenIsBool(Value value) 841 { 842 static if (WREN_NAN_TAGGING) 843 { 844 return value == TRUE_VAL || value == FALSE_VAL; 845 } 846 else 847 { 848 return value.type == ValueType.VAL_FALSE || value.type == ValueType.VAL_TRUE; 849 } 850 } 851 852 // Returns true if [value] is an object of type [type]. Do not call this 853 // directly, instead use the [IS___] macro for the type in question. 854 static bool wrenIsObjType(Value value, ObjType type) 855 { 856 return IS_OBJ(value) && AS_OBJ(value).type == type; 857 } 858 859 // Converts the raw object pointer [obj] to a [Value]. 860 static Value wrenObjectToValue(Obj* obj) 861 { 862 static if (WREN_NAN_TAGGING) 863 { 864 return cast(Value)(SIGN_BIT | QNAN | cast(ulong)(obj)); 865 } 866 else 867 { 868 Value value; 869 value.type = ValueType.VAL_OBJ; 870 value.as.obj = obj; 871 return value; 872 } 873 } 874 875 // Interprets [value] as a [double]. 876 static double wrenValueToNum(Value value) 877 { 878 static if (WREN_NAN_TAGGING) 879 { 880 return wrenDoubleFromBits(value); 881 } 882 else 883 { 884 return value.as.num; 885 } 886 } 887 888 // Converts [num] to a [Value]. 889 static Value wrenNumToValue(double num) 890 { 891 static if (WREN_NAN_TAGGING) 892 { 893 return wrenDoubleToBits(num); 894 } 895 else 896 { 897 Value value; 898 value.type = ValueType.VAL_NUM; 899 value.as.num = num; 900 return value; 901 } 902 } 903 904 // Validates that [arg] is a valid object for use as a map key. Returns true if 905 // it is and returns false otherwise. Use validateKey usually, for a runtime error. 906 // This separation exists to aid the API in surfacing errors to the developer as well. 907 static bool wrenMapIsValidKey(Value arg) 908 { 909 return IS_BOOL(arg) 910 || IS_CLASS(arg) 911 || IS_NULL(arg) 912 || IS_NUM(arg) 913 || IS_RANGE(arg) 914 || IS_STRING(arg); 915 } 916 917 // TODO: Tune these. 918 // The initial (and minimum) capacity of a non-empty list or map object. 919 enum MIN_CAPACITY = 16; 920 921 // The rate at which a collection's capacity grows when the size exceeds the 922 // current capacity. The new capacity will be determined by *multiplying* the 923 // old capacity by this. Growing geometrically is necessary to ensure that 924 // adding to a collection has O(1) amortized complexity. 925 enum GROW_FACTOR = 2; 926 927 // The maximum percentage of map entries that can be filled before the map is 928 // grown. A lower load takes more memory but reduces collisions which makes 929 // lookup faster. 930 enum MAP_LOAD_PERCENT = 75; 931 932 // The number of call frames initially allocated when a fiber is created. Making 933 // this smaller makes fibers use less memory (at first) but spends more time 934 // reallocating when the call stack grows. 935 enum INITIAL_CALL_FRAMES = 4; 936 937 static void initObj(WrenVM* vm, Obj* obj, ObjType type, ObjClass* classObj) 938 { 939 obj.type = type; 940 obj.isDark = false; 941 obj.classObj = classObj; 942 obj.next = vm.first; 943 vm.first = obj; 944 } 945 946 // Creates a new "raw" class. It has no metaclass or superclass whatsoever. 947 // This is only used for bootstrapping the initial Object and Class classes, 948 // which are a little special. 949 ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name) 950 { 951 ObjClass* classObj = ALLOCATE!(WrenVM, ObjClass)(vm); 952 initObj(vm, &classObj.obj, ObjType.OBJ_CLASS, null); 953 classObj.superclass = null; 954 classObj.numFields = numFields; 955 classObj.name = name; 956 classObj.attributes = NULL_VAL; 957 958 wrenPushRoot(vm, cast(Obj*)classObj); 959 wrenMethodBufferInit(&classObj.methods); 960 wrenPopRoot(vm); 961 962 return classObj; 963 } 964 965 // Makes [superclass] the superclass of [subclass], and causes subclass to 966 // inherit its methods. This should be called before any methods are defined 967 // on subclass. 968 void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass) 969 { 970 assert(superclass != null, "Must have superclass"); 971 972 subclass.superclass = superclass; 973 974 // Include the superclass in the total number of fields. 975 if (subclass.numFields != -1) 976 { 977 subclass.numFields += superclass.numFields; 978 } 979 else 980 { 981 assert(superclass.numFields == 0, 982 "A foreign class cannot inherit from a class with fields."); 983 } 984 985 // Inherit methods from its superclass. 986 for (int i = 0; i < superclass.methods.count; i++) 987 { 988 wrenBindMethod(vm, subclass, i, superclass.methods.data[i]); 989 } 990 } 991 992 // Creates a new class object as well as its associated metaclass. 993 ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields, 994 ObjString* name) 995 { 996 // Create the metaclass. 997 Value metaclassName = wrenStringFormat(vm, "@ metaclass".ptr, OBJ_VAL(name)); 998 wrenPushRoot(vm, AS_OBJ(metaclassName)); 999 1000 ObjClass* metaclass = wrenNewSingleClass(vm, 0, AS_STRING(metaclassName)); 1001 metaclass.obj.classObj = vm.classClass; 1002 1003 wrenPopRoot(vm); 1004 1005 // Make sure the metaclass isn't collected when we allocate the class. 1006 wrenPushRoot(vm, cast(Obj*)metaclass); 1007 1008 // Metaclasses always inherit Class and do not parallel the non-metaclass 1009 // hierarchy. 1010 wrenBindSuperclass(vm, metaclass, vm.classClass); 1011 1012 ObjClass* classObj = wrenNewSingleClass(vm, numFields, name); 1013 1014 // Make sure the class isn't collected while the inherited methods are being 1015 // bound. 1016 wrenPushRoot(vm, cast(Obj*)classObj); 1017 1018 classObj.obj.classObj = metaclass; 1019 wrenBindSuperclass(vm, classObj, superclass); 1020 1021 wrenPopRoot(vm); 1022 wrenPopRoot(vm); 1023 1024 return classObj; 1025 } 1026 1027 void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method) 1028 { 1029 // Make sure the buffer is big enough to contain the symbol's index. 1030 if (symbol >= classObj.methods.count) 1031 { 1032 Method noMethod; 1033 noMethod.type = MethodType.METHOD_NONE; 1034 wrenMethodBufferFill(vm, &classObj.methods, noMethod, 1035 symbol - classObj.methods.count + 1); 1036 } 1037 1038 classObj.methods.data[symbol] = method; 1039 } 1040 1041 // Creates a new closure object that invokes [fn]. Allocates room for its 1042 // upvalues, but assumes outside code will populate it. 1043 ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn) 1044 { 1045 ObjClosure* closure = ALLOCATE_FLEX!(WrenVM, ObjClosure, ObjUpvalue*)(vm, fn.numUpvalues); 1046 1047 initObj(vm, &closure.obj, ObjType.OBJ_CLOSURE, vm.fnClass); 1048 1049 closure.fn = fn; 1050 1051 // Clear the upvalue array. We need to do this in case a GC is triggered 1052 // after the closure is created but before the upvalue array is populated. 1053 for (int i = 0; i < fn.numUpvalues; i++) closure.upvalues[i] = null; 1054 1055 return closure; 1056 } 1057 1058 // Adds a new [CallFrame] to [fiber] invoking [closure] whose stack starts at 1059 // [stackStart]. 1060 void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure, Value* stackStart) 1061 { 1062 assert(fiber.frameCapacity > fiber.numFrames, "No memory for call frame"); 1063 1064 CallFrame* frame = &fiber.frames[fiber.numFrames++]; 1065 frame.stackStart = stackStart; 1066 frame.closure = closure; 1067 frame.ip = closure.fn.code.data; 1068 } 1069 1070 // Creates a new fiber object that will invoke [closure]. 1071 ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure) 1072 { 1073 // Allocate the arrays before the fiber in case it triggers a GC. 1074 CallFrame* frames = ALLOCATE_ARRAY!(WrenVM, CallFrame)(vm, INITIAL_CALL_FRAMES); 1075 1076 // Add one slot for the unused implicit receiver slot that the compiler 1077 // assumes all functions have. 1078 int stackCapacity = closure == null 1079 ? 1 1080 : wrenPowerOf2Ceil(closure.fn.maxSlots + 1); 1081 Value* stack = ALLOCATE_ARRAY!(WrenVM, Value)(vm, stackCapacity); 1082 1083 ObjFiber* fiber = ALLOCATE!(WrenVM, ObjFiber)(vm); 1084 initObj(vm, &fiber.obj, ObjType.OBJ_FIBER, vm.fiberClass); 1085 1086 fiber.stack = stack; 1087 fiber.stackTop = fiber.stack; 1088 fiber.stackCapacity = stackCapacity; 1089 1090 fiber.frames = frames; 1091 fiber.frameCapacity = INITIAL_CALL_FRAMES; 1092 fiber.numFrames = 0; 1093 1094 fiber.openUpvalues = null; 1095 fiber.caller = null; 1096 fiber.error = NULL_VAL; 1097 fiber.state = FiberState.FIBER_OTHER; 1098 1099 if (closure != null) 1100 { 1101 // Initialize the first call frame. 1102 wrenAppendCallFrame(vm, fiber, closure, fiber.stack); 1103 1104 // The first slot always holds the closure. 1105 fiber.stackTop[0] = OBJ_VAL(closure); 1106 fiber.stackTop++; 1107 } 1108 1109 return fiber; 1110 } 1111 1112 // Ensures [fiber]'s stack has at least [needed] slots. 1113 void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed) 1114 { 1115 if (fiber.stackCapacity >= needed) return; 1116 1117 int capacity = wrenPowerOf2Ceil(needed); 1118 1119 Value* oldStack = fiber.stack; 1120 fiber.stack = cast(Value*)wrenReallocate(vm, fiber.stack, 1121 Value.sizeof * fiber.stackCapacity, 1122 Value.sizeof * capacity); 1123 fiber.stackCapacity = capacity; 1124 1125 // If the reallocation moves the stack, then we need to recalculate every 1126 // pointer that points into the old stack to into the same relative distance 1127 // in the new stack. We have to be a little careful about how these are 1128 // calculated because pointer subtraction is only well-defined within a 1129 // single array, hence the slightly redundant-looking arithmetic below. 1130 if (fiber.stack != oldStack) 1131 { 1132 // Top of the stack. 1133 if (vm.apiStack >= oldStack && vm.apiStack <= fiber.stackTop) 1134 { 1135 vm.apiStack = fiber.stack + (vm.apiStack - oldStack); 1136 } 1137 1138 // Stack pointer for each call frame. 1139 for (int i = 0; i < fiber.numFrames; i++) 1140 { 1141 CallFrame* frame = &fiber.frames[i]; 1142 frame.stackStart = fiber.stack + (frame.stackStart - oldStack); 1143 } 1144 1145 // Open upvalues. 1146 for (ObjUpvalue* upvalue = fiber.openUpvalues; 1147 upvalue != null; 1148 upvalue = upvalue.next) 1149 { 1150 upvalue.value = fiber.stack + (upvalue.value - oldStack); 1151 } 1152 1153 fiber.stackTop = fiber.stack + (fiber.stackTop - oldStack); 1154 } 1155 } 1156 1157 static bool wrenHasError(ObjFiber* fiber) 1158 { 1159 return !IS_NULL(fiber.error); 1160 } 1161 1162 ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size) 1163 { 1164 import core.stdc.string : memset; 1165 ObjForeign* object = ALLOCATE_FLEX!(WrenVM, ObjForeign, ubyte)(vm, size); 1166 initObj(vm, &object.obj, ObjType.OBJ_FOREIGN, classObj); 1167 1168 // Zero out the bytes. 1169 memset(object.data.ptr, 0, size); 1170 return object; 1171 } 1172 1173 // Creates a new empty function. Before being used, it must have code, 1174 // constants, etc. added to it. 1175 ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module_, int maxSlots) 1176 { 1177 FnDebug* debug_ = ALLOCATE!(WrenVM, FnDebug)(vm); 1178 debug_.name = null; 1179 wrenIntBufferInit(&debug_.sourceLines); 1180 1181 ObjFn* fn = ALLOCATE!(WrenVM, ObjFn)(vm); 1182 initObj(vm, &fn.obj, ObjType.OBJ_FN, vm.fnClass); 1183 1184 wrenValueBufferInit(&fn.constants); 1185 wrenByteBufferInit(&fn.code); 1186 fn.module_ = module_; 1187 fn.maxSlots = maxSlots; 1188 fn.numUpvalues = 0; 1189 fn.arity = 0; 1190 fn.debug_ = debug_; 1191 1192 return fn; 1193 } 1194 1195 void wrenFunctionBindName(WrenVM* vm, ObjFn* fn, const(char)* name, int length) 1196 { 1197 import core.stdc.string : memcpy; 1198 fn.debug_.name = ALLOCATE_ARRAY!(WrenVM, char)(vm, length + 1); 1199 memcpy(fn.debug_.name, name, length); 1200 fn.debug_.name[length] = '\0'; 1201 } 1202 1203 // Creates a new instance of the given [classObj]. 1204 Value wrenNewInstance(WrenVM* vm, ObjClass* classObj) 1205 { 1206 ObjInstance* instance = ALLOCATE_FLEX!(WrenVM, ObjInstance, Value)(vm, classObj.numFields); 1207 initObj(vm, &instance.obj, ObjType.OBJ_INSTANCE, classObj); 1208 1209 // Initialize fields to null. 1210 for (int i = 0; i < classObj.numFields; i++) 1211 { 1212 instance.fields[i] = NULL_VAL; 1213 } 1214 1215 return OBJ_VAL(instance); 1216 } 1217 1218 // Creates a new list with [numElements] elements (which are left 1219 // uninitialized.) 1220 ObjList* wrenNewList(WrenVM* vm, uint numElements) 1221 { 1222 // Allocate this before the list object in case it triggers a GC which would 1223 // free the list. 1224 Value* elements = null; 1225 if (numElements > 0) 1226 { 1227 elements = ALLOCATE_ARRAY!(WrenVM, Value)(vm, numElements); 1228 } 1229 1230 ObjList* list = ALLOCATE!(WrenVM, ObjList)(vm); 1231 initObj(vm, &list.obj, ObjType.OBJ_LIST, vm.listClass); 1232 list.elements.capacity = numElements; 1233 list.elements.count = numElements; 1234 list.elements.data = elements; 1235 return list; 1236 } 1237 1238 // Inserts [value] in [list] at [index], shifting down the other elements. 1239 void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint index) 1240 { 1241 if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value)); 1242 1243 // Add a slot at the end of the list. 1244 wrenValueBufferWrite(vm, &list.elements, NULL_VAL); 1245 1246 if (IS_OBJ(value)) wrenPopRoot(vm); 1247 1248 // Shift the existing elements down. 1249 for (uint i = list.elements.count - 1; i > index; i--) 1250 { 1251 list.elements.data[i] = list.elements.data[i - 1]; 1252 } 1253 1254 // Store the new element. 1255 list.elements.data[index] = value; 1256 } 1257 1258 // Searches for [value] in [list], returns the index or -1 if not found. 1259 int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value) 1260 { 1261 int count = list.elements.count; 1262 for (int i = 0; i < count; i++) 1263 { 1264 Value item = list.elements.data[i]; 1265 if(wrenValuesEqual(item, value)) { 1266 return i; 1267 } 1268 } 1269 return -1; 1270 } 1271 1272 Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint index) 1273 { 1274 Value removed = list.elements.data[index]; 1275 1276 if (IS_OBJ(removed)) wrenPushRoot(vm, AS_OBJ(removed)); 1277 1278 // Shift items up. 1279 for (int i = index; i < list.elements.count - 1; i++) 1280 { 1281 list.elements.data[i] = list.elements.data[i + 1]; 1282 } 1283 1284 // If we have too much excess capacity, shrink it. 1285 if (list.elements.capacity / GROW_FACTOR >= list.elements.count) 1286 { 1287 list.elements.data = cast(Value*)wrenReallocate(vm, list.elements.data, 1288 Value.sizeof * list.elements.capacity, 1289 Value.sizeof * (list.elements.capacity / GROW_FACTOR)); 1290 list.elements.capacity /= GROW_FACTOR; 1291 } 1292 1293 if (IS_OBJ(removed)) wrenPopRoot(vm); 1294 1295 list.elements.count--; 1296 return removed; 1297 } 1298 1299 // Creates a new empty map. 1300 ObjMap* wrenNewMap(WrenVM* vm) 1301 { 1302 ObjMap* map = ALLOCATE!(WrenVM, ObjMap)(vm); 1303 initObj(vm, &map.obj, ObjType.OBJ_MAP, vm.mapClass); 1304 map.capacity = 0; 1305 map.count = 0; 1306 map.entries = null; 1307 return map; 1308 } 1309 1310 static uint hashBits(ulong hash) 1311 { 1312 // From v8's ComputeLongHash() which in turn cites: 1313 // Thomas Wang, Integer Hash Functions. 1314 // http://www.concentric.net/~Ttwang/tech/inthash.htm 1315 hash = ~hash + (hash << 18); // hash = (hash << 18) - hash - 1; 1316 hash = hash ^ (hash >> 31); 1317 hash = hash * 21; // hash = (hash + (hash << 2)) + (hash << 4); 1318 hash = hash ^ (hash >> 11); 1319 hash = hash + (hash << 6); 1320 hash = hash ^ (hash >> 22); 1321 return cast(uint)(hash & 0x3fffffff); 1322 } 1323 1324 // Generates a hash code for [num]. 1325 static uint hashNumber(double num) 1326 { 1327 // Hash the raw bits of the value. 1328 return hashBits(wrenDoubleToBits(num)); 1329 } 1330 1331 // Generates a hash code for [object]. 1332 static uint hashObject(Obj* object) 1333 { 1334 switch (object.type) 1335 { 1336 case ObjType.OBJ_CLASS: 1337 // Classes just use their name. 1338 return hashObject(cast(Obj*)(cast(ObjClass*)object).name); 1339 1340 // Allow bare (non-closure) functions so that we can use a map to find 1341 // existing constants in a function's constant table. This is only used 1342 // internally. Since user code never sees a non-closure function, they 1343 // cannot use them as map keys. 1344 case ObjType.OBJ_FN: 1345 { 1346 ObjFn* fn = cast(ObjFn*)object; 1347 return hashNumber(fn.arity) ^ hashNumber(fn.code.count); 1348 } 1349 1350 case ObjType.OBJ_RANGE: 1351 { 1352 ObjRange* range = cast(ObjRange*)object; 1353 return hashNumber(range.from) ^ hashNumber(range.to); 1354 } 1355 1356 case ObjType.OBJ_STRING: 1357 return (cast(ObjString*)object).hash; 1358 1359 default: 1360 assert(false, "Only immutable objects can be hashed."); 1361 } 1362 } 1363 1364 // Generates a hash code for [value], which must be one of the built-in 1365 // immutable types: null, bool, class, num, range, or string. 1366 static uint hashValue(Value value) 1367 { 1368 // TODO: We'll probably want to randomize this at some point. 1369 static if (WREN_NAN_TAGGING) 1370 { 1371 if (IS_OBJ(value)) return hashObject(AS_OBJ(value)); 1372 1373 // Hash the raw bits of the unboxed value. 1374 return hashBits(value); 1375 } 1376 else 1377 { 1378 switch (value.type) with(ValueType) 1379 { 1380 case VAL_FALSE: return 0; 1381 case VAL_NULL: return 1; 1382 case VAL_NUM: return hashNumber(AS_NUM(value)); 1383 case VAL_TRUE: return 2; 1384 case VAL_OBJ: return hashObject(AS_OBJ(value)); 1385 default: assert(0, "Unreachable?"); 1386 } 1387 } 1388 } 1389 1390 // Looks for an entry with [key] in an array of [capacity] [entries]. 1391 // 1392 // If found, sets [result] to point to it and returns `true`. Otherwise, 1393 // returns `false` and points [result] to the entry where the key/value pair 1394 // should be inserted. 1395 static bool findEntry(MapEntry* entries, uint capacity, Value key, 1396 MapEntry** result) 1397 { 1398 // If there is no entry array (an empty map), we definitely won't find it. 1399 if (capacity == 0) return false; 1400 1401 // Figure out where to insert it in the table. Use open addressing and 1402 // basic linear probing. 1403 uint startIndex = hashValue(key) % capacity; 1404 uint index = startIndex; 1405 1406 // If we pass a tombstone and don't end up finding the key, its entry will 1407 // be re-used for the insert. 1408 MapEntry* tombstone = null; 1409 1410 // Walk the probe sequence until we've tried every slot. 1411 do 1412 { 1413 MapEntry* entry = &entries[index]; 1414 1415 if (IS_UNDEFINED(entry.key)) 1416 { 1417 // If we found an empty slot, the key is not in the table. If we found a 1418 // slot that contains a deleted key, we have to keep looking. 1419 if (IS_FALSE(entry.value)) 1420 { 1421 // We found an empty slot, so we've reached the end of the probe 1422 // sequence without finding the key. If we passed a tombstone, then 1423 // that's where we should insert the item, otherwise, put it here at 1424 // the end of the sequence. 1425 *result = tombstone != null ? tombstone : entry; 1426 return false; 1427 } 1428 else 1429 { 1430 // We found a tombstone. We need to keep looking in case the key is 1431 // after it, but we'll use this entry as the insertion point if the 1432 // key ends up not being found. 1433 if (tombstone == null) tombstone = entry; 1434 } 1435 } 1436 else if (wrenValuesEqual(entry.key, key)) 1437 { 1438 // We found the key. 1439 *result = entry; 1440 return true; 1441 } 1442 1443 // Try the next slot. 1444 index = (index + 1) % capacity; 1445 } 1446 while (index != startIndex); 1447 1448 // If we get here, the table is full of tombstones. Return the first one we 1449 // found. 1450 assert(tombstone != null, "Map should have tombstones or empty entries."); 1451 *result = tombstone; 1452 return false; 1453 } 1454 1455 // Inserts [key] and [value] in the array of [entries] with the given 1456 // [capacity]. 1457 // 1458 // Returns `true` if this is the first time [key] was added to the map. 1459 static bool insertEntry(MapEntry* entries, uint capacity, 1460 Value key, Value value) 1461 { 1462 assert(entries != null, "Should ensure capacity before inserting."); 1463 1464 MapEntry* entry; 1465 if (findEntry(entries, capacity, key, &entry)) 1466 { 1467 // Already present, so just replace the value. 1468 entry.value = value; 1469 return false; 1470 } 1471 else 1472 { 1473 entry.key = key; 1474 entry.value = value; 1475 return true; 1476 } 1477 } 1478 1479 // Updates [map]'s entry array to [capacity]. 1480 static void resizeMap(WrenVM* vm, ObjMap* map, uint capacity) 1481 { 1482 // Create the new empty hash table. 1483 MapEntry* entries = ALLOCATE_ARRAY!(WrenVM, MapEntry)(vm, capacity); 1484 for (uint i = 0; i < capacity; i++) 1485 { 1486 entries[i].key = UNDEFINED_VAL; 1487 entries[i].value = FALSE_VAL; 1488 } 1489 1490 // Re-add the existing entries. 1491 if (map.capacity > 0) 1492 { 1493 for (uint i = 0; i < map.capacity; i++) 1494 { 1495 MapEntry* entry = &map.entries[i]; 1496 1497 // Don't copy empty entries or tombstones. 1498 if (IS_UNDEFINED(entry.key)) continue; 1499 1500 insertEntry(entries, capacity, entry.key, entry.value); 1501 } 1502 } 1503 1504 // Replace the array. 1505 DEALLOCATE(vm, map.entries); 1506 map.entries = entries; 1507 map.capacity = capacity; 1508 } 1509 1510 // Looks up [key] in [map]. If found, returns the value. Otherwise, returns 1511 // `UNDEFINED_VAL`. 1512 Value wrenMapGet(ObjMap* map, Value key) 1513 { 1514 MapEntry* entry; 1515 if (findEntry(map.entries, map.capacity, key, &entry)) return entry.value; 1516 1517 return UNDEFINED_VAL; 1518 } 1519 1520 // Associates [key] with [value] in [map]. 1521 void wrenMapSet(WrenVM* vm, ObjMap* map, Value key, Value value) 1522 { 1523 // If the map is getting too full, make room first. 1524 if (map.count + 1 > map.capacity * MAP_LOAD_PERCENT / 100) 1525 { 1526 // Figure out the new hash table size. 1527 uint capacity = map.capacity * GROW_FACTOR; 1528 if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; 1529 1530 resizeMap(vm, map, capacity); 1531 } 1532 1533 if (insertEntry(map.entries, map.capacity, key, value)) 1534 { 1535 // A new key was added. 1536 map.count++; 1537 } 1538 } 1539 1540 void wrenMapClear(WrenVM* vm, ObjMap* map) 1541 { 1542 DEALLOCATE(vm, map.entries); 1543 map.entries = null; 1544 map.capacity = 0; 1545 map.count = 0; 1546 } 1547 1548 // Removes [key] from [map], if present. Returns the value for the key if found 1549 // or `NULL_VAL` otherwise. 1550 Value wrenMapRemoveKey(WrenVM* vm, ObjMap* map, Value key) 1551 { 1552 MapEntry* entry; 1553 if (!findEntry(map.entries, map.capacity, key, &entry)) return NULL_VAL; 1554 1555 // Remove the entry from the map. Set this value to true, which marks it as a 1556 // deleted slot. When searching for a key, we will stop on empty slots, but 1557 // continue past deleted slots. 1558 Value value = entry.value; 1559 entry.key = UNDEFINED_VAL; 1560 entry.value = TRUE_VAL; 1561 1562 if (IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value)); 1563 1564 map.count--; 1565 1566 if (map.count == 0) 1567 { 1568 // Removed the last item, so free the array. 1569 wrenMapClear(vm, map); 1570 } 1571 else if (map.capacity > MIN_CAPACITY && 1572 map.count < map.capacity / GROW_FACTOR * MAP_LOAD_PERCENT / 100) 1573 { 1574 uint capacity = map.capacity / GROW_FACTOR; 1575 if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; 1576 1577 // The map is getting empty, so shrink the entry array back down. 1578 // TODO: Should we do this less aggressively than we grow? 1579 resizeMap(vm, map, capacity); 1580 } 1581 1582 if (IS_OBJ(value)) wrenPopRoot(vm); 1583 return value; 1584 } 1585 1586 // Creates a new module. 1587 ObjModule* wrenNewModule(WrenVM* vm, ObjString* name) 1588 { 1589 ObjModule* module_ = ALLOCATE!(WrenVM, ObjModule)(vm); 1590 1591 // Modules are never used as first-class objects, so don't need a class. 1592 initObj(vm, cast(Obj*)module_, ObjType.OBJ_MODULE, null); 1593 1594 wrenPushRoot(vm, cast(Obj*)module_); 1595 1596 wrenSymbolTableInit(&module_.variableNames); 1597 wrenValueBufferInit(&module_.variables); 1598 1599 module_.name = name; 1600 1601 wrenPopRoot(vm); 1602 return module_; 1603 } 1604 1605 // Creates a new range from [from] to [to]. 1606 Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive) 1607 { 1608 ObjRange* range = ALLOCATE!(WrenVM, ObjRange)(vm); 1609 initObj(vm, &range.obj, ObjType.OBJ_RANGE, vm.rangeClass); 1610 range.from = from; 1611 range.to = to; 1612 range.isInclusive = isInclusive; 1613 1614 return OBJ_VAL(range); 1615 } 1616 1617 // Creates a new string object with a null-terminated buffer large enough to 1618 // hold a string of [length] but does not fill in the bytes. 1619 // 1620 // The caller is expected to fill in the buffer and then calculate the string's 1621 // hash. 1622 static ObjString* allocateString(WrenVM* vm, size_t length) 1623 { 1624 ObjString* str = ALLOCATE_FLEX!(WrenVM, ObjString, char)(vm, length + 1); 1625 initObj(vm, &str.obj, ObjType.OBJ_STRING, vm.stringClass); 1626 str.length = cast(int)length; 1627 str.value[length] = '\0'; 1628 1629 return str; 1630 } 1631 1632 static void hashString(ObjString* str) 1633 { 1634 // FNV-1a hash. See: http://www.isthe.com/chongo/tech/comp/fnv/ 1635 uint hash = 2166136261u; 1636 1637 // This is O(n) on the length of the string, but we only call this when a new 1638 // string is created. Since the creation is also O(n) (to copy/initialize all 1639 // the bytes), we allow this here. 1640 for (uint i = 0; i < str.length; i++) 1641 { 1642 hash ^= str.value[i]; 1643 hash *= 16777619; 1644 } 1645 1646 str.hash = hash; 1647 } 1648 1649 // Creates a new string object and copies [text] into it. 1650 // 1651 // [text] must be non-NULL. 1652 Value wrenNewString(WrenVM* vm, const(char)* text) 1653 { 1654 import core.stdc.string : strlen; 1655 return wrenNewStringLength(vm, text, strlen(text)); 1656 } 1657 1658 // Creates a new string object of [length] and copies [text] into it. 1659 // 1660 // [text] may be NULL if [length] is zero. 1661 Value wrenNewStringLength(WrenVM* vm, const(char)* text, size_t length) 1662 { 1663 import core.stdc.string : memcpy; 1664 // Allow NULL if the string is empty since byte buffers don't allocate any 1665 // characters for a zero-length string. 1666 assert(length == 0 || text != null, "Unexpected null string."); 1667 1668 ObjString* str = allocateString(vm, length); 1669 1670 // Copy the string (if given one). 1671 if (length > 0 && text != null) memcpy(str.value.ptr, text, length); 1672 1673 hashString(str); 1674 return OBJ_VAL(str); 1675 } 1676 1677 // Creates a new string object from [text], which should be a bare C string 1678 // literal. This determines the length of the string automatically at compile 1679 // time based on the size of the character array (-1 for the terminating '\0'). 1680 Value CONST_STRING(WrenVM* vm, const(char)[] text) 1681 { 1682 return wrenNewStringLength(vm, text.ptr, text.length); 1683 } 1684 1685 // Creates a new string object by taking a range of characters from [source]. 1686 // The range starts at [start], contains [count] bytes, and increments by 1687 // [step]. 1688 Value wrenNewStringFromRange(WrenVM* vm, ObjString* source, int start, 1689 uint count, int step) 1690 { 1691 ubyte* from = cast(ubyte*)source.value; 1692 int length = 0; 1693 for (uint i = 0; i < count; i++) 1694 { 1695 length += wrenUtf8DecodeNumBytes(from[start + i * step]); 1696 } 1697 1698 ObjString* result = allocateString(vm, length); 1699 result.value[length] = '\0'; 1700 1701 ubyte* to = cast(ubyte*)result.value; 1702 for (uint i = 0; i < count; i++) 1703 { 1704 int index = start + i * step; 1705 int codePoint = wrenUtf8Decode(from + index, source.length - index); 1706 1707 if (codePoint != -1) 1708 { 1709 to += wrenUtf8Encode(codePoint, to); 1710 } 1711 } 1712 1713 hashString(result); 1714 return OBJ_VAL(result); 1715 } 1716 1717 // Produces a string representation of [value]. 1718 Value wrenNumToString(WrenVM* vm, double value) 1719 { 1720 import std.math : isNaN, isInfinity; 1721 import core.stdc.stdio : sprintf; 1722 if (isNaN(value)) 1723 { 1724 return CONST_STRING(vm, "nan"); 1725 } 1726 1727 if (isInfinity(value)) 1728 { 1729 if (value > 0.0) 1730 { 1731 return CONST_STRING(vm, "infinity"); 1732 } 1733 else 1734 { 1735 return CONST_STRING(vm, "-infinity"); 1736 } 1737 } 1738 1739 // This is large enough to hold any double converted to a string using 1740 // "%.14g". Example: 1741 // 1742 // -1.12345678901234e-1022 1743 // 1744 // So we have: 1745 // 1746 // + 1 char for sign 1747 // + 1 char for digit 1748 // + 1 char for "." 1749 // + 14 chars for decimal digits 1750 // + 1 char for "e" 1751 // + 1 char for "-" or "+" 1752 // + 4 chars for exponent 1753 // + 1 char for "\0" 1754 // = 24 1755 char[24] buffer; 1756 int length = sprintf(buffer.ptr, "%.14g", value); 1757 return wrenNewStringLength(vm, buffer.ptr, length); 1758 } 1759 1760 // Creates a new string containing the UTF-8 encoding of [value]. 1761 Value wrenStringFromCodePoint(WrenVM* vm, int value) 1762 { 1763 int length = wrenUtf8EncodeNumBytes(value); 1764 assert(length != 0, "Value out of range"); 1765 1766 ObjString* str = allocateString(vm, length); 1767 1768 wrenUtf8Encode(value, cast(ubyte*)str.value); 1769 hashString(str); 1770 1771 return OBJ_VAL(str); 1772 } 1773 1774 // Creates a new string from the integer representation of a byte 1775 Value wrenStringFromByte(WrenVM *vm, ubyte value) 1776 { 1777 int length = 1; 1778 ObjString* str = allocateString(vm, length); 1779 str.value[0] = value; 1780 hashString(str); 1781 return OBJ_VAL(str); 1782 } 1783 1784 // Creates a new formatted string from [format] and any additional arguments 1785 // used in the format string. 1786 // 1787 // This is a very restricted flavor of formatting, intended only for internal 1788 // use by the VM. Two formatting characters are supported, each of which reads 1789 // the next argument as a certain type: 1790 // 1791 // $ - A C string. 1792 // @ - A Wren string object. 1793 extern(C) Value wrenStringFormat(WrenVM* vm, const(char)* format, ...) 1794 { 1795 import core.stdc.stdarg : va_start, va_arg, va_end, va_list, va_copy; 1796 import core.stdc.string : strlen, memcpy; 1797 1798 // Calculate the length of the result string. Do this up front so we can 1799 // create the final string with a single allocation. 1800 // Work-around nasty compiler bug here. Apparently va_args doesn't get reset. 1801 va_list v1; 1802 va_start(v1, format); 1803 va_list v2; 1804 va_copy(v2, v1); 1805 size_t totalLength = 0; 1806 1807 for (const(char)* c = format; *c != '\0'; c++) 1808 { 1809 switch (*c) 1810 { 1811 case '$': 1812 totalLength += strlen(va_arg!(const(char*))(v2)); 1813 break; 1814 1815 case '@': 1816 totalLength += AS_STRING(va_arg!(Value)(v2)).length; 1817 break; 1818 1819 default: 1820 // Any other character is interpreted literally. 1821 totalLength++; 1822 } 1823 } 1824 va_end(v2); 1825 1826 // Concatenate the string. 1827 ObjString* result = allocateString(vm, totalLength); 1828 1829 //va_start(_argptr, format); 1830 char* start = result.value.ptr; 1831 for (const(char)* c = format; *c != '\0'; c++) 1832 { 1833 switch (*c) 1834 { 1835 case '$': 1836 { 1837 const(char)* str = va_arg!(const(char)*)(v1); 1838 size_t length = strlen(str); 1839 memcpy(start, str, length); 1840 start += length; 1841 break; 1842 } 1843 1844 case '@': 1845 { 1846 Value v = va_arg!(Value)(v1); 1847 ObjString* str = AS_STRING(v); 1848 memcpy(start, str.value.ptr, str.length); 1849 start += str.length; 1850 break; 1851 } 1852 1853 default: 1854 // Any other character is interpreted literally. 1855 *start++ = *c; 1856 } 1857 } 1858 va_end(v1); 1859 1860 hashString(result); 1861 1862 return OBJ_VAL(result); 1863 } 1864 1865 // Creates a new string containing the code point in [string] starting at byte 1866 // [index]. If [index] points into the middle of a UTF-8 sequence, returns an 1867 // empty string. 1868 Value wrenStringCodePointAt(WrenVM* vm, ObjString* string_, uint index) 1869 { 1870 assert(index < string_.length, "Index out of bounds."); 1871 1872 int codePoint = wrenUtf8Decode(cast(ubyte*)string_.value + index, 1873 string_.length - index); 1874 if (codePoint == -1) 1875 { 1876 // If it isn't a valid UTF-8 sequence, treat it as a single raw byte. 1877 char[2] bytes; 1878 bytes[0] = string_.value[index]; 1879 bytes[1] = '\0'; 1880 return wrenNewStringLength(vm, bytes.ptr, 1); 1881 } 1882 1883 return wrenStringFromCodePoint(vm, codePoint); 1884 } 1885 1886 // Uses the Boyer-Moore-Horspool string matching algorithm. 1887 // Search for the first occurence of [needle] within [haystack] and returns its 1888 // zero-based offset. Returns `UINT32_MAX` if [haystack] does not contain 1889 // [needle]. 1890 uint wrenStringFind(ObjString* haystack, ObjString* needle, uint start) 1891 { 1892 // Edge case: An empty needle is always found. 1893 if (needle.length == 0) return start; 1894 1895 // If the needle goes past the haystack it won't be found. 1896 if (start + needle.length > haystack.length) return uint.max; 1897 1898 // If the startIndex is too far it also won't be found. 1899 if (start >= haystack.length) return uint.max; 1900 1901 // Pre-calculate the shift table. For each character (8-bit value), we 1902 // determine how far the search window can be advanced if that character is 1903 // the last character in the haystack where we are searching for the needle 1904 // and the needle doesn't match there. 1905 uint[ubyte.max] shift; 1906 uint needleEnd = needle.length - 1; 1907 1908 // By default, we assume the character is not the needle at all. In that case 1909 // case, if a match fails on that character, we can advance one whole needle 1910 // width since. 1911 for (uint index = 0; index < ubyte.max; index++) 1912 { 1913 shift[index] = needle.length; 1914 } 1915 1916 // Then, for every character in the needle, determine how far it is from the 1917 // end. If a match fails on that character, we can advance the window such 1918 // that it the last character in it lines up with the last place we could 1919 // find it in the needle. 1920 for (uint index = 0; index < needleEnd; index++) 1921 { 1922 char c = needle.value[index]; 1923 shift[cast(ubyte)c] = needleEnd - index; 1924 } 1925 1926 // Slide the needle across the haystack, looking for the first match or 1927 // stopping if the needle goes off the end. 1928 char lastChar = needle.value[needleEnd]; 1929 uint range = haystack.length - needle.length; 1930 1931 for (uint index = start; index <= range; ) 1932 { 1933 import core.stdc.string : memcmp; 1934 // Compare the last character in the haystack's window to the last character 1935 // in the needle. If it matches, see if the whole needle matches. 1936 char c = haystack.value[index + needleEnd]; 1937 if (lastChar == c && 1938 memcmp(haystack.value.ptr + index, needle.value.ptr, needleEnd) == 0) 1939 { 1940 // Found a match. 1941 return index; 1942 } 1943 1944 // Otherwise, slide the needle forward. 1945 index += shift[cast(ubyte)c]; 1946 } 1947 1948 // Not found. 1949 return uint.max; 1950 } 1951 1952 // Returns true if [a] and [b] represent the same string. 1953 static bool wrenStringEqualsCString(ObjString* a, const(char)* b, size_t length) 1954 { 1955 import core.stdc.string : memcmp; 1956 return a.length == length && memcmp(a.value.ptr, b, length) == 0; 1957 } 1958 1959 // Creates a new open upvalue pointing to [value] on the stack. 1960 ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value) 1961 { 1962 ObjUpvalue* upvalue = ALLOCATE!(WrenVM, ObjUpvalue)(vm); 1963 1964 // Upvalues are never used as first-class objects, so don't need a class. 1965 initObj(vm, &upvalue.obj, ObjType.OBJ_UPVALUE, null); 1966 1967 upvalue.value = value; 1968 upvalue.closed = NULL_VAL; 1969 upvalue.next = null; 1970 1971 return upvalue; 1972 } 1973 1974 // Mark [obj] as reachable and still in use. This should only be called 1975 // during the sweep phase of a garbage collection. 1976 void wrenGrayObj(WrenVM* vm, Obj* obj) 1977 { 1978 if (obj == null) return; 1979 1980 // Stop if the object is already darkened so we don't get stuck in a cycle. 1981 if (obj.isDark) return; 1982 1983 // It's been reached. 1984 obj.isDark = true; 1985 1986 // Add it to the gray list so it can be recursively explored for 1987 // more marks later. 1988 if (vm.grayCount >= vm.grayCapacity) 1989 { 1990 vm.grayCapacity = vm.grayCount * 2; 1991 vm.gray = cast(Obj**)vm.config.reallocateFn(vm.gray, 1992 vm.grayCapacity * (Obj*).sizeof, 1993 vm.config.userData); 1994 } 1995 1996 vm.gray[vm.grayCount++] = obj; 1997 } 1998 1999 // Mark [value] as reachable and still in use. This should only be called 2000 // during the sweep phase of a garbage collection. 2001 void wrenGrayValue(WrenVM* vm, Value value) 2002 { 2003 if (!IS_OBJ(value)) return; 2004 wrenGrayObj(vm, AS_OBJ(value)); 2005 } 2006 2007 // Mark the values in [buffer] as reachable and still in use. This should only 2008 // be called during the sweep phase of a garbage collection. 2009 void wrenGrayBuffer(WrenVM* vm, ValueBuffer* buffer) 2010 { 2011 for (int i = 0; i < buffer.count; i++) 2012 { 2013 wrenGrayValue(vm, buffer.data[i]); 2014 } 2015 } 2016 2017 static void blackenClass(WrenVM* vm, ObjClass* classObj) 2018 { 2019 // The metaclass. 2020 wrenGrayObj(vm, cast(Obj*)classObj.obj.classObj); 2021 2022 // The superclass. 2023 wrenGrayObj(vm, cast(Obj*)classObj.superclass); 2024 2025 // Method function objects. 2026 for (int i = 0; i < classObj.methods.count; i++) 2027 { 2028 if (classObj.methods.data[i].type == MethodType.METHOD_BLOCK) 2029 { 2030 wrenGrayObj(vm, cast(Obj*)classObj.methods.data[i].as.closure); 2031 } 2032 } 2033 2034 wrenGrayObj(vm, cast(Obj*)classObj.name); 2035 2036 if(!IS_NULL(classObj.attributes)) wrenGrayObj(vm, AS_OBJ(classObj.attributes)); 2037 2038 // Keep track of how much memory is still in use. 2039 vm.bytesAllocated += ObjClass.sizeof; 2040 vm.bytesAllocated += classObj.methods.capacity * Method.sizeof; 2041 } 2042 2043 static void blackenClosure(WrenVM* vm, ObjClosure* closure) 2044 { 2045 // Mark the function. 2046 wrenGrayObj(vm, cast(Obj*)closure.fn); 2047 2048 // Mark the upvalues. 2049 for (int i = 0; i < closure.fn.numUpvalues; i++) 2050 { 2051 wrenGrayObj(vm, cast(Obj*)closure.upvalues[i]); 2052 } 2053 2054 // Keep track of how much memory is still in use. 2055 vm.bytesAllocated += ObjClosure.sizeof; 2056 vm.bytesAllocated += (ObjUpvalue*).sizeof * closure.fn.numUpvalues; 2057 } 2058 2059 static void blackenFiber(WrenVM* vm, ObjFiber* fiber) 2060 { 2061 // Stack functions. 2062 2063 for (int i = 0; i < fiber.numFrames; i++) 2064 { 2065 wrenGrayObj(vm, cast(Obj*)fiber.frames[i].closure); 2066 } 2067 2068 // Stack variables. 2069 for (Value* slot = fiber.stack; slot < fiber.stackTop; slot++) 2070 { 2071 wrenGrayValue(vm, *slot); 2072 } 2073 2074 // Open upvalues. 2075 ObjUpvalue* upvalue = fiber.openUpvalues; 2076 while (upvalue != null) 2077 { 2078 wrenGrayObj(vm, cast(Obj*)upvalue); 2079 upvalue = upvalue.next; 2080 } 2081 2082 // The caller. 2083 wrenGrayObj(vm, cast(Obj*)fiber.caller); 2084 wrenGrayValue(vm, fiber.error); 2085 2086 // Keep track of how much memory is still in use. 2087 vm.bytesAllocated += ObjFiber.sizeof; 2088 vm.bytesAllocated += fiber.frameCapacity * CallFrame.sizeof; 2089 vm.bytesAllocated += fiber.stackCapacity * Value.sizeof; 2090 } 2091 2092 static void blackenFn(WrenVM* vm, ObjFn* fn) 2093 { 2094 // Mark the constants. 2095 wrenGrayBuffer(vm, &fn.constants); 2096 2097 // Keep track of how much memory is still in use. 2098 vm.bytesAllocated += ObjFn.sizeof; 2099 vm.bytesAllocated += ubyte.sizeof * fn.code.capacity; 2100 vm.bytesAllocated += Value.sizeof * fn.constants.capacity; 2101 2102 // The debug line number buffer. 2103 vm.bytesAllocated += int.sizeof * fn.code.capacity; 2104 // TODO: What about the function name? 2105 } 2106 2107 static void blackenForeign(WrenVM* vm, ObjForeign* foreign) 2108 { 2109 // TODO: Keep track of how much memory the foreign object uses. We can store 2110 // this in each foreign object, but it will balloon the size. We may not want 2111 // that much overhead. One option would be to let the foreign class register 2112 // a C function that returns a size for the object. That way the VM doesn't 2113 // always have to explicitly store it. 2114 } 2115 2116 static void blackenInstance(WrenVM* vm, ObjInstance* instance) 2117 { 2118 wrenGrayObj(vm, cast(Obj*)instance.obj.classObj); 2119 2120 // Mark the fields. 2121 for (int i = 0; i < instance.obj.classObj.numFields; i++) 2122 { 2123 wrenGrayValue(vm, instance.fields[i]); 2124 } 2125 2126 // Keep track of how much memory is still in use. 2127 vm.bytesAllocated += ObjInstance.sizeof; 2128 vm.bytesAllocated += Value.sizeof * instance.obj.classObj.numFields; 2129 } 2130 2131 static void blackenList(WrenVM* vm, ObjList* list) 2132 { 2133 // Mark the elements. 2134 wrenGrayBuffer(vm, &list.elements); 2135 2136 // Keep track of how much memory is still in use. 2137 vm.bytesAllocated += ObjList.sizeof; 2138 vm.bytesAllocated += Value.sizeof * list.elements.capacity; 2139 } 2140 2141 static void blackenMap(WrenVM* vm, ObjMap* map) 2142 { 2143 // Mark the entries. 2144 for (uint i = 0; i < map.capacity; i++) 2145 { 2146 MapEntry* entry = &map.entries[i]; 2147 if (IS_UNDEFINED(entry.key)) continue; 2148 2149 wrenGrayValue(vm, entry.key); 2150 wrenGrayValue(vm, entry.value); 2151 } 2152 2153 // Keep track of how much memory is still in use. 2154 vm.bytesAllocated += ObjMap.sizeof; 2155 vm.bytesAllocated += MapEntry.sizeof * map.capacity; 2156 } 2157 2158 static void blackenModule(WrenVM* vm, ObjModule* module_) 2159 { 2160 // Top-level variables. 2161 for (int i = 0; i < module_.variables.count; i++) 2162 { 2163 wrenGrayValue(vm, module_.variables.data[i]); 2164 } 2165 2166 wrenBlackenSymbolTable(vm, &module_.variableNames); 2167 2168 wrenGrayObj(vm, cast(Obj*)module_.name); 2169 2170 // Keep track of how much memory is still in use. 2171 vm.bytesAllocated += ObjModule.sizeof; 2172 } 2173 2174 static void blackenRange(WrenVM* vm, ObjRange* range) 2175 { 2176 // Keep track of how much memory is still in use. 2177 vm.bytesAllocated += ObjRange.sizeof; 2178 } 2179 2180 static void blackenString(WrenVM* vm, ObjString* str) 2181 { 2182 // Keep track of how much memory is still in use. 2183 vm.bytesAllocated += ObjString.sizeof + str.length + 1; 2184 } 2185 2186 static void blackenUpvalue(WrenVM* vm, ObjUpvalue* upvalue) 2187 { 2188 // Mark the closed-over object (in case it is closed). 2189 wrenGrayValue(vm, upvalue.closed); 2190 2191 // Keep track of how much memory is still in use. 2192 vm.bytesAllocated += ObjUpvalue.sizeof; 2193 } 2194 2195 static void blackenObject(WrenVM* vm, Obj* obj) 2196 { 2197 static if (WREN_DEBUG_TRACE_MEMORY) 2198 { 2199 import core.stdc.stdio : printf; 2200 import wren.dbg : wrenDumpValue; 2201 printf("mark "); 2202 wrenDumpValue(OBJ_VAL(obj)); 2203 printf(" @ %p\n", obj); 2204 } 2205 2206 // Traverse the object's fields. 2207 switch (obj.type) with(ObjType) 2208 { 2209 case OBJ_CLASS: blackenClass( vm, cast(ObjClass*) obj); break; 2210 case OBJ_CLOSURE: blackenClosure( vm, cast(ObjClosure*) obj); break; 2211 case OBJ_FIBER: blackenFiber( vm, cast(ObjFiber*) obj); break; 2212 case OBJ_FN: blackenFn( vm, cast(ObjFn*) obj); break; 2213 case OBJ_FOREIGN: blackenForeign( vm, cast(ObjForeign*) obj); break; 2214 case OBJ_INSTANCE: blackenInstance(vm, cast(ObjInstance*)obj); break; 2215 case OBJ_LIST: blackenList( vm, cast(ObjList*) obj); break; 2216 case OBJ_MAP: blackenMap( vm, cast(ObjMap*) obj); break; 2217 case OBJ_MODULE: blackenModule( vm, cast(ObjModule*) obj); break; 2218 case OBJ_RANGE: blackenRange( vm, cast(ObjRange*) obj); break; 2219 case OBJ_STRING: blackenString( vm, cast(ObjString*) obj); break; 2220 case OBJ_UPVALUE: blackenUpvalue( vm, cast(ObjUpvalue*) obj); break; 2221 default: assert(0, "Unexpected object type"); 2222 } 2223 } 2224 2225 // Processes every object in the gray stack until all reachable objects have 2226 // been marked. After that, all objects are either white (freeable) or black 2227 // (in use and fully traversed). 2228 void wrenBlackenObjects(WrenVM* vm) 2229 { 2230 while (vm.grayCount > 0) 2231 { 2232 // Pop an item from the gray stack. 2233 Obj* obj = vm.gray[--vm.grayCount]; 2234 blackenObject(vm, obj); 2235 } 2236 } 2237 2238 // Releases all memory owned by [obj], including [obj] itself. 2239 void wrenFreeObj(WrenVM* vm, Obj* obj) 2240 { 2241 static if (WREN_DEBUG_TRACE_MEMORY) 2242 { 2243 import core.stdc.stdio; 2244 import wren.dbg : wrenDumpValue; 2245 printf("free "); 2246 wrenDumpValue(OBJ_VAL(obj)); 2247 printf (" @ %p\n", obj); 2248 } 2249 2250 switch (obj.type) 2251 { 2252 case ObjType.OBJ_CLASS: { 2253 wrenMethodBufferClear(vm, &(cast(ObjClass*)obj).methods); 2254 break; 2255 } 2256 2257 case ObjType.OBJ_FIBER: { 2258 ObjFiber* fiber = cast(ObjFiber*)obj; 2259 DEALLOCATE(vm, fiber.frames); 2260 DEALLOCATE(vm, fiber.stack); 2261 break; 2262 } 2263 2264 case ObjType.OBJ_FN: { 2265 ObjFn* fn = cast(ObjFn*)obj; 2266 wrenValueBufferClear(vm, &fn.constants); 2267 wrenByteBufferClear(vm, &fn.code); 2268 wrenIntBufferClear(vm, &fn.debug_.sourceLines); 2269 DEALLOCATE(vm, fn.debug_.name); 2270 DEALLOCATE(vm, fn.debug_); 2271 break; 2272 } 2273 2274 case ObjType.OBJ_FOREIGN: 2275 assert(0, "stub"); 2276 2277 case ObjType.OBJ_LIST: 2278 wrenValueBufferClear(vm, &(cast(ObjList*)obj).elements); 2279 break; 2280 2281 case ObjType.OBJ_MAP: 2282 DEALLOCATE(vm, (cast(ObjMap*)obj).entries); 2283 break; 2284 2285 case ObjType.OBJ_MODULE: 2286 wrenSymbolTableClear(vm, &(cast(ObjModule*)obj).variableNames); 2287 wrenValueBufferClear(vm, &(cast(ObjModule*)obj).variables); 2288 break; 2289 2290 case ObjType.OBJ_CLOSURE: 2291 case ObjType.OBJ_INSTANCE: 2292 case ObjType.OBJ_RANGE: 2293 case ObjType.OBJ_STRING: 2294 case ObjType.OBJ_UPVALUE: 2295 break; 2296 2297 default: 2298 assert(0, "Unexpected object type"); 2299 } 2300 } 2301 2302 // Returns true if [a] and [b] are equivalent. Immutable values (null, bools, 2303 // numbers, ranges, and strings) are equal if they have the same data. All 2304 // other values are equal if they are identical objects. 2305 bool wrenValuesEqual(Value a, Value b) 2306 { 2307 if (wrenValuesSame(a, b)) return true; 2308 2309 // If we get here, it's only possible for two heap-allocated immutable objects 2310 // to be equal. 2311 if (!IS_OBJ(a) || !IS_OBJ(b)) return false; 2312 2313 Obj* aObj = AS_OBJ(a); 2314 Obj* bObj = AS_OBJ(b); 2315 2316 // Must be the same type. 2317 if (aObj.type != bObj.type) return false; 2318 2319 switch (aObj.type) 2320 { 2321 case ObjType.OBJ_RANGE: 2322 { 2323 ObjRange* aRange = cast(ObjRange*)aObj; 2324 ObjRange* bRange = cast(ObjRange*)bObj; 2325 return aRange.from == bRange.from && 2326 aRange.to == bRange.to && 2327 aRange.isInclusive == bRange.isInclusive; 2328 } 2329 2330 case ObjType.OBJ_STRING: 2331 { 2332 ObjString* aString = cast(ObjString*)aObj; 2333 ObjString* bString = cast(ObjString*)bObj; 2334 return aString.hash == bString.hash && 2335 wrenStringEqualsCString(aString, bString.value.ptr, bString.length); 2336 } 2337 2338 default: 2339 // All other types are only equal if they are same, which they aren't if 2340 // we get here. 2341 return false; 2342 } 2343 }