forked from Mirror/wren
Use a growable buffer for globals.
Also allows more than 256.
This commit is contained in:
@ -49,8 +49,13 @@
|
||||
// Set this to true to print out the compiled bytecode of each function.
|
||||
#define WREN_DUMP_COMPILED_CODE false
|
||||
|
||||
// The maximum number of globals that may be defined at one time. This
|
||||
// limitation comes from the 16 bits used for the arguments to
|
||||
// `CODE_LOAD_GLOBAL` and `CODE_STORE_GLOBAL`.
|
||||
#define MAX_GLOBALS (65536)
|
||||
|
||||
// The maximum number of arguments that can be passed to a method. Note that
|
||||
// this limtation is hardcoded in other places in the VM, in particular, the
|
||||
// this limitation is hardcoded in other places in the VM, in particular, the
|
||||
// `CODE_CALL_XX` instructions assume a certain maximum number.
|
||||
#define MAX_PARAMETERS (16)
|
||||
|
||||
|
||||
@ -946,14 +946,17 @@ static int declareVariable(Compiler* compiler)
|
||||
// Top-level global scope.
|
||||
if (compiler->scopeDepth == -1)
|
||||
{
|
||||
SymbolTable* symbols = &compiler->parser->vm->globalSymbols;
|
||||
int symbol = wrenDefineGlobal(compiler->parser->vm,
|
||||
token->start, token->length, NULL_VAL);
|
||||
|
||||
int symbol = wrenSymbolTableAdd(compiler->parser->vm, symbols,
|
||||
token->start, token->length);
|
||||
if (symbol == -1)
|
||||
{
|
||||
error(compiler, "Global variable is already defined.");
|
||||
}
|
||||
else if (symbol == -2)
|
||||
{
|
||||
error(compiler, "Too many global variables defined.");
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
@ -1002,8 +1005,7 @@ static void defineVariable(Compiler* compiler, int symbol)
|
||||
|
||||
// It's a global variable, so store the value in the global slot and then
|
||||
// discard the temporary for the initializer.
|
||||
emitByte(compiler, CODE_STORE_GLOBAL, symbol);
|
||||
// TODO: Allow more than 256 globals.
|
||||
emitShort(compiler, CODE_STORE_GLOBAL, symbol);
|
||||
emit(compiler, CODE_POP);
|
||||
}
|
||||
|
||||
@ -1505,6 +1507,8 @@ static void loadThis(Compiler* compiler)
|
||||
{
|
||||
Code loadInstruction;
|
||||
int index = resolveName(compiler, "this", 4, &loadInstruction);
|
||||
ASSERT(index == -1 || loadInstruction != CODE_LOAD_GLOBAL,
|
||||
"'this' should not be global.");
|
||||
emitByte(compiler, loadInstruction, index);
|
||||
}
|
||||
|
||||
@ -1655,12 +1659,16 @@ static void variable(Compiler* compiler, bool allowAssignment, int index,
|
||||
emitByte(compiler, CODE_STORE_UPVALUE, index);
|
||||
break;
|
||||
case CODE_LOAD_GLOBAL:
|
||||
emitByte(compiler, CODE_STORE_GLOBAL, index);
|
||||
emitShort(compiler, CODE_STORE_GLOBAL, index);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
else if (loadInstruction == CODE_LOAD_GLOBAL)
|
||||
{
|
||||
emitShort(compiler, loadInstruction, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
emitByte(compiler, loadInstruction, index);
|
||||
@ -2164,8 +2172,6 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_STORE_LOCAL:
|
||||
case CODE_LOAD_UPVALUE:
|
||||
case CODE_STORE_UPVALUE:
|
||||
case CODE_LOAD_GLOBAL:
|
||||
case CODE_STORE_GLOBAL:
|
||||
case CODE_LOAD_FIELD_THIS:
|
||||
case CODE_STORE_FIELD_THIS:
|
||||
case CODE_LOAD_FIELD:
|
||||
@ -2175,6 +2181,8 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
return 1;
|
||||
|
||||
case CODE_CONSTANT:
|
||||
case CODE_LOAD_GLOBAL:
|
||||
case CODE_STORE_GLOBAL:
|
||||
case CODE_CALL_0:
|
||||
case CODE_CALL_1:
|
||||
case CODE_CALL_2:
|
||||
@ -2591,7 +2599,14 @@ static void classDefinition(Compiler* compiler)
|
||||
signature);
|
||||
|
||||
// Load the class.
|
||||
emitByte(compiler, isGlobal ? CODE_LOAD_GLOBAL : CODE_LOAD_LOCAL, symbol);
|
||||
if (isGlobal)
|
||||
{
|
||||
emitShort(compiler, CODE_LOAD_GLOBAL, symbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
emitByte(compiler, CODE_LOAD_LOCAL, symbol);
|
||||
}
|
||||
|
||||
// Define the method.
|
||||
emitShort(compiler, instruction, methodSymbol);
|
||||
|
||||
@ -883,34 +883,26 @@ DEF_NATIVE(string_subscript)
|
||||
static ObjClass* defineSingleClass(WrenVM* vm, const char* name)
|
||||
{
|
||||
size_t length = strlen(name);
|
||||
int symbol = wrenSymbolTableAdd(vm, &vm->globalSymbols, name, length);
|
||||
|
||||
ObjString* nameString = AS_STRING(wrenNewString(vm, name, length));
|
||||
WREN_PIN(vm, nameString);
|
||||
|
||||
ObjClass* classObj = wrenNewSingleClass(vm, 0, nameString);
|
||||
vm->globals[symbol] = OBJ_VAL(classObj);
|
||||
wrenDefineGlobal(vm, name, length, OBJ_VAL(classObj));
|
||||
|
||||
WREN_UNPIN(vm);
|
||||
|
||||
return classObj;
|
||||
}
|
||||
|
||||
static ObjClass* defineClass(WrenVM* vm, const char* name)
|
||||
{
|
||||
size_t length = strlen(name);
|
||||
|
||||
// Add the symbol first since it can trigger a GC.
|
||||
int symbol = wrenSymbolTableAdd(vm, &vm->globalSymbols, name, length);
|
||||
|
||||
ObjString* nameString = AS_STRING(wrenNewString(vm, name, length));
|
||||
WREN_PIN(vm, nameString);
|
||||
|
||||
ObjClass* classObj = wrenNewClass(vm, vm->objectClass, 0, nameString);
|
||||
wrenDefineGlobal(vm, name, length, OBJ_VAL(classObj));
|
||||
|
||||
WREN_UNPIN(vm);
|
||||
|
||||
vm->globals[symbol] = OBJ_VAL(classObj);
|
||||
return classObj;
|
||||
}
|
||||
|
||||
@ -918,7 +910,7 @@ static ObjClass* defineClass(WrenVM* vm, const char* name)
|
||||
static Value findGlobal(WrenVM* vm, const char* name)
|
||||
{
|
||||
int symbol = wrenSymbolTableFind(&vm->globalSymbols, name, strlen(name));
|
||||
return vm->globals[symbol];
|
||||
return vm->globals.data[symbol];
|
||||
}
|
||||
|
||||
void wrenInitializeCore(WrenVM* vm)
|
||||
|
||||
@ -74,7 +74,7 @@ static int debugPrintInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
|
||||
case CODE_LOAD_GLOBAL:
|
||||
{
|
||||
int global = READ_BYTE();
|
||||
int global = READ_SHORT();
|
||||
printf("%-16s %5d '%s'\n", "LOAD_GLOBAL", global,
|
||||
vm->globalSymbols.names.data[global]);
|
||||
break;
|
||||
@ -82,7 +82,7 @@ static int debugPrintInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
|
||||
case CODE_STORE_GLOBAL:
|
||||
{
|
||||
int global = READ_BYTE();
|
||||
int global = READ_SHORT();
|
||||
printf("%-16s %5d '%s'\n", "STORE_GLOBAL", global,
|
||||
vm->globalSymbols.names.data[global]);
|
||||
break;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
// to a list has O(1) amortized complexity.
|
||||
#define LIST_GROW_FACTOR (2)
|
||||
|
||||
DEFINE_BUFFER(Value, Value)
|
||||
DEFINE_BUFFER(Method, Method)
|
||||
|
||||
static void* allocate(WrenVM* vm, size_t size)
|
||||
|
||||
@ -92,6 +92,8 @@ typedef struct
|
||||
|
||||
#endif
|
||||
|
||||
DECLARE_BUFFER(Value, Value);
|
||||
|
||||
// The dynamically allocated data structure for a variable that has been used
|
||||
// by a closure. Whenever a function accesses a variable declared in an
|
||||
// enclosing function, it will get to it through this.
|
||||
|
||||
@ -38,6 +38,7 @@ WrenVM* wrenNewVM(WrenConfiguration* configuration)
|
||||
|
||||
wrenSymbolTableInit(vm, &vm->methods);
|
||||
wrenSymbolTableInit(vm, &vm->globalSymbols);
|
||||
wrenValueBufferInit(vm, &vm->globals);
|
||||
|
||||
vm->bytesAllocated = 0;
|
||||
|
||||
@ -67,13 +68,6 @@ WrenVM* wrenNewVM(WrenConfiguration* configuration)
|
||||
vm->first = NULL;
|
||||
vm->pinned = NULL;
|
||||
|
||||
// Clear out the global variables. This ensures they are NULL before being
|
||||
// initialized in case we do a garbage collection before one gets initialized.
|
||||
for (int i = 0; i < MAX_GLOBALS; i++)
|
||||
{
|
||||
vm->globals[i] = NULL_VAL;
|
||||
}
|
||||
|
||||
vm->foreignCallSlot = NULL;
|
||||
vm->foreignCallNumArgs = 0;
|
||||
|
||||
@ -85,10 +79,6 @@ WrenVM* wrenNewVM(WrenConfiguration* configuration)
|
||||
|
||||
void wrenFreeVM(WrenVM* vm)
|
||||
{
|
||||
wrenSymbolTableClear(vm, &vm->methods);
|
||||
wrenSymbolTableClear(vm, &vm->globalSymbols);
|
||||
wrenReallocate(vm, vm, 0, 0);
|
||||
|
||||
// Free all of the GC objects.
|
||||
Obj* obj = vm->first;
|
||||
while (obj != NULL)
|
||||
@ -97,6 +87,12 @@ void wrenFreeVM(WrenVM* vm)
|
||||
wrenFreeObj(vm, obj);
|
||||
obj = next;
|
||||
}
|
||||
|
||||
wrenSymbolTableClear(vm, &vm->methods);
|
||||
wrenSymbolTableClear(vm, &vm->globalSymbols);
|
||||
wrenValueBufferClear(vm, &vm->globals);
|
||||
|
||||
wrenReallocate(vm, vm, 0, 0);
|
||||
}
|
||||
|
||||
static void collectGarbage(WrenVM* vm);
|
||||
@ -128,11 +124,9 @@ static void collectGarbage(WrenVM* vm)
|
||||
vm->bytesAllocated = 0;
|
||||
|
||||
// Global variables.
|
||||
for (int i = 0; i < vm->globalSymbols.names.count; i++)
|
||||
for (int i = 0; i < vm->globals.count; i++)
|
||||
{
|
||||
// Check for NULL to handle globals that have been defined (at compile time)
|
||||
// but not yet initialized.
|
||||
if (!IS_NULL(vm->globals[i])) wrenMarkValue(vm, vm->globals[i]);
|
||||
wrenMarkValue(vm, vm->globals.data[i]);
|
||||
}
|
||||
|
||||
// Pinned objects.
|
||||
@ -715,11 +709,11 @@ static bool runInterpreter(WrenVM* vm)
|
||||
DISPATCH();
|
||||
|
||||
CASE_CODE(LOAD_GLOBAL):
|
||||
PUSH(vm->globals[READ_BYTE()]);
|
||||
PUSH(vm->globals.data[READ_SHORT()]);
|
||||
DISPATCH();
|
||||
|
||||
CASE_CODE(STORE_GLOBAL):
|
||||
vm->globals[READ_BYTE()] = PEEK();
|
||||
vm->globals.data[READ_SHORT()] = PEEK();
|
||||
DISPATCH();
|
||||
|
||||
CASE_CODE(LOAD_FIELD_THIS):
|
||||
@ -1025,6 +1019,20 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* sourcePath,
|
||||
}
|
||||
}
|
||||
|
||||
int wrenDefineGlobal(WrenVM* vm, const char* name, size_t length, Value value)
|
||||
{
|
||||
if (vm->globals.count == MAX_GLOBALS) return -2;
|
||||
|
||||
if (IS_OBJ(value)) WREN_PIN(vm, AS_OBJ(value));
|
||||
|
||||
int symbol = wrenSymbolTableAdd(vm, &vm->globalSymbols, name, length);
|
||||
if (symbol != -1) wrenValueBufferWrite(vm, &vm->globals, value);
|
||||
|
||||
if (IS_OBJ(value)) WREN_UNPIN(vm);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
void wrenPinObj(WrenVM* vm, Obj* obj, PinnedObj* pinned)
|
||||
{
|
||||
pinned->obj = obj;
|
||||
@ -1060,7 +1068,7 @@ static void defineMethod(WrenVM* vm, const char* className,
|
||||
if (classSymbol != -1)
|
||||
{
|
||||
// TODO: Handle name is not class.
|
||||
classObj = AS_CLASS(vm->globals[classSymbol]);
|
||||
classObj = AS_CLASS(vm->globals.data[classSymbol]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1072,12 +1080,9 @@ static void defineMethod(WrenVM* vm, const char* className,
|
||||
|
||||
// TODO: Allow passing in name for superclass?
|
||||
classObj = wrenNewClass(vm, vm->objectClass, 0, nameString);
|
||||
classSymbol = wrenSymbolTableAdd(vm, &vm->globalSymbols,
|
||||
className, length);
|
||||
wrenDefineGlobal(vm, className, length, OBJ_VAL(classObj));
|
||||
|
||||
WREN_UNPIN(vm);
|
||||
|
||||
vm->globals[classSymbol] = OBJ_VAL(classObj);
|
||||
}
|
||||
|
||||
// Create a name for the method, including its arity.
|
||||
|
||||
@ -6,9 +6,6 @@
|
||||
#include "wren_value.h"
|
||||
#include "wren_utils.h"
|
||||
|
||||
// TODO: Get rid of this.
|
||||
#define MAX_GLOBALS 256
|
||||
|
||||
// In order to token paste __LINE__, you need two weird levels of indirection
|
||||
// since __LINE__ isn't expanded when used in a token paste.
|
||||
// See: http://stackoverflow.com/a/1597129/9457
|
||||
@ -219,10 +216,10 @@ struct WrenVM
|
||||
ObjClass* rangeClass;
|
||||
ObjClass* stringClass;
|
||||
|
||||
// TODO: Move to end of struct since less frequently accessed?
|
||||
SymbolTable globalSymbols;
|
||||
|
||||
// TODO: Using a fixed array is gross here.
|
||||
Value globals[MAX_GLOBALS];
|
||||
ValueBuffer globals;
|
||||
|
||||
// The compiler that is currently compiling code. This is used so that heap
|
||||
// allocated objects used by the compiler can be found if a GC is kicked off
|
||||
@ -288,6 +285,12 @@ struct WrenVM
|
||||
// [oldSize] will be zero. It should return NULL.
|
||||
void* wrenReallocate(WrenVM* vm, void* memory, size_t oldSize, size_t newSize);
|
||||
|
||||
// Adds a new global named [name] to the global namespace.
|
||||
//
|
||||
// Returns the symbol for the new global, -1 if a global with the given name
|
||||
// is already defined, or -2 if there are too many globals defined.
|
||||
int wrenDefineGlobal(WrenVM* vm, const char* name, size_t length, Value value);
|
||||
|
||||
// Sets the current Compiler being run to [compiler].
|
||||
void wrenSetCompiler(WrenVM* vm, Compiler* compiler);
|
||||
|
||||
|
||||
8200
test/limit/many_globals.wren
Normal file
8200
test/limit/many_globals.wren
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user