Merge branch 'master' into smarter-imports

# Conflicts:
#	src/module/io.c
#	src/vm/wren_vm.c
This commit is contained in:
Bob Nystrom
2018-07-15 21:01:14 -07:00
43 changed files with 502 additions and 217 deletions
+2 -1
View File
@@ -192,6 +192,7 @@ static ModuleRegistry modules[] =
#undef END_CLASS
#undef METHOD
#undef STATIC_METHOD
#undef FINALIZER
// Looks for a built-in module with [name].
//
@@ -241,7 +242,7 @@ char* readBuiltInModule(const char* name)
size_t length = strlen(*module->source);
char* copy = (char*)malloc(length + 1);
strncpy(copy, *module->source, length + 1);
memcpy(copy, *module->source, length + 1);
return copy;
}
+11 -1
View File
@@ -9,11 +9,21 @@
#include <sys\stat.h>
// Map to Windows permission flags.
#ifndef S_IRUSR
#define S_IRUSR _S_IREAD
#define S_IWUSR _S_IWRITE
#endif
#ifndef S_IWUSR
#define S_IWUSR _S_IWRITE
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
// Not supported on Windows.
#define O_SYNC 0
+7 -10
View File
@@ -11,11 +11,6 @@ class Repl {
_history = []
_historyIndex = 0
// Whether or not we allow ASCI escape sequences for showing colors and
// moving the cursor. When this is false, the REPL has reduced
// functionality.
_allowAnsi = false
}
cursor { _cursor }
@@ -78,7 +73,7 @@ class Repl {
insertChar(byte)
} else {
// TODO: Other shortcuts?
System.print("Unhandled byte: %(byte)")
System.print("Unhandled key-code [dec]: %(byte)")
}
return false
@@ -172,15 +167,17 @@ class Repl {
token.type == Token.varKeyword ||
token.type == Token.whileKeyword
var fiber
var closure
if (isStatement) {
fiber = Meta.compile(input)
closure = Meta.compile(input)
} else {
fiber = Meta.compileExpression(input)
closure = Meta.compileExpression(input)
}
// Stop if there was a compile error.
if (fiber == null) return
if (closure == null) return
var fiber = Fiber.new(closure)
var result = fiber.try()
if (fiber.error != null) {
+7 -10
View File
@@ -13,11 +13,6 @@ static const char* replModuleSource =
"\n"
" _history = []\n"
" _historyIndex = 0\n"
"\n"
" // Whether or not we allow ASCI escape sequences for showing colors and\n"
" // moving the cursor. When this is false, the REPL has reduced\n"
" // functionality.\n"
" _allowAnsi = false\n"
" }\n"
"\n"
" cursor { _cursor }\n"
@@ -80,7 +75,7 @@ static const char* replModuleSource =
" insertChar(byte)\n"
" } else {\n"
" // TODO: Other shortcuts?\n"
" System.print(\"Unhandled byte: %(byte)\")\n"
" System.print(\"Unhandled key-code [dec]: %(byte)\")\n"
" }\n"
"\n"
" return false\n"
@@ -174,15 +169,17 @@ static const char* replModuleSource =
" token.type == Token.varKeyword ||\n"
" token.type == Token.whileKeyword\n"
"\n"
" var fiber\n"
" var closure\n"
" if (isStatement) {\n"
" fiber = Meta.compile(input)\n"
" closure = Meta.compile(input)\n"
" } else {\n"
" fiber = Meta.compileExpression(input)\n"
" closure = Meta.compileExpression(input)\n"
" }\n"
"\n"
" // Stop if there was a compile error.\n"
" if (fiber == null) return\n"
" if (closure == null) return\n"
"\n"
" var fiber = Fiber.new(closure)\n"
"\n"
" var result = fiber.try()\n"
" if (fiber.error != null) {\n"
+1 -3
View File
@@ -61,9 +61,7 @@ void metaGetModuleVariables(WrenVM* vm)
for (int i = 0; i < names->elements.count; i++)
{
String* name = &module->variableNames.data[i];
names->elements.data[i] = wrenNewStringLength(vm,
name->buffer, name->length);
names->elements.data[i] = OBJ_VAL(module->variableNames.data[i]);
}
}
+57 -48
View File
@@ -48,7 +48,7 @@
// is kind of hairy, but fortunately we can control what the longest possible
// message is and handle that. Ideally, we'd use `snprintf()`, but that's not
// available in standard C++98.
#define ERROR_MESSAGE_SIZE (60 + MAX_VARIABLE_NAME + 15)
#define ERROR_MESSAGE_SIZE (80 + MAX_VARIABLE_NAME + 15)
typedef enum
{
@@ -498,7 +498,7 @@ static int addConstant(Compiler* compiler, Value constant)
// Initializes [compiler].
static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent,
bool isFunction)
bool isMethod)
{
compiler->parser = parser;
compiler->parent = parent;
@@ -512,41 +512,41 @@ static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent,
parser->vm->compiler = compiler;
// Declare a local slot for either the closure or method receiver so that we
// don't try to reuse that slot for a user-defined local variable. For
// methods, we name it "this", so that we can resolve references to that like
// a normal variable. For functions, they have no explicit "this", so we use
// an empty name. That way references to "this" inside a function walks up
// the parent chain to find a method enclosing the function whose "this" we
// can close over.
compiler->numLocals = 1;
compiler->numSlots = compiler->numLocals;
if (isMethod)
{
compiler->locals[0].name = "this";
compiler->locals[0].length = 4;
}
else
{
compiler->locals[0].name = NULL;
compiler->locals[0].length = 0;
}
compiler->locals[0].depth = -1;
compiler->locals[0].isUpvalue = false;
if (parent == NULL)
{
compiler->numLocals = 0;
// Compiling top-level code, so the initial scope is module-level.
compiler->scopeDepth = -1;
}
else
{
// Declare a fake local variable for the receiver so that it's slot in the
// stack is taken. For methods, we call this "this", so that we can resolve
// references to that like a normal variable. For functions, they have no
// explicit "this". So we pick a bogus name. That way references to "this"
// inside a function will try to walk up the parent chain to find a method
// enclosing the function whose "this" we can close over.
compiler->numLocals = 1;
if (isFunction)
{
compiler->locals[0].name = NULL;
compiler->locals[0].length = 0;
}
else
{
compiler->locals[0].name = "this";
compiler->locals[0].length = 4;
}
compiler->locals[0].depth = -1;
compiler->locals[0].isUpvalue = false;
// The initial scope for function or method is a local scope.
// The initial scope for functions and methods is local scope.
compiler->scopeDepth = 0;
}
compiler->numSlots = compiler->numLocals;
compiler->fn = wrenNewFunction(parser->vm, parser->module,
compiler->numLocals);
}
@@ -1700,7 +1700,11 @@ static void signatureParameterList(char name[MAX_METHOD_SIGNATURE], int* length,
int numParams, char leftBracket, char rightBracket)
{
name[(*length)++] = leftBracket;
for (int i = 0; i < numParams; i++)
// This function may be called with too many parameters. When that happens,
// a compile error has already been reported, but we need to make sure we
// don't overflow the string too, hence the MAX_PARAMETERS check.
for (int i = 0; i < numParams && i < MAX_PARAMETERS; i++)
{
if (i > 0) name[(*length)++] = ',';
name[(*length)++] = '_';
@@ -1863,7 +1867,7 @@ static void methodCall(Compiler* compiler, Code instruction,
called.arity++;
Compiler fnCompiler;
initCompiler(&fnCompiler, compiler->parser, compiler, true);
initCompiler(&fnCompiler, compiler->parser, compiler, false);
// Make a dummy signature to track the arity.
Signature fnSignature = { "", 0, SIG_METHOD, 0 };
@@ -2727,7 +2731,6 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_LOAD_FIELD:
case CODE_STORE_FIELD:
case CODE_CLASS:
case CODE_IMPORT_MODULE:
return 1;
case CODE_CONSTANT:
@@ -2757,7 +2760,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_OR:
case CODE_METHOD_INSTANCE:
case CODE_METHOD_STATIC:
case CODE_IMPORT_VARIABLE:
case CODE_IMPORT_MODULE:
return 2;
case CODE_SUPER_0:
@@ -2777,6 +2780,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_SUPER_14:
case CODE_SUPER_15:
case CODE_SUPER_16:
case CODE_IMPORT_VARIABLE:
return 4;
case CODE_CLOSURE:
@@ -3077,7 +3081,7 @@ static void createConstructor(Compiler* compiler, Signature* signature,
int initializerSymbol)
{
Compiler methodCompiler;
initCompiler(&methodCompiler, compiler->parser, compiler, false);
initCompiler(&methodCompiler, compiler->parser, compiler, true);
// Allocate the instance.
emitOp(&methodCompiler, compiler->enclosingClass->isForeign
@@ -3163,7 +3167,7 @@ static bool method(Compiler* compiler, Variable classVariable)
compiler->enclosingClass->signature = &signature;
Compiler methodCompiler;
initCompiler(&methodCompiler, compiler->parser, compiler, false);
initCompiler(&methodCompiler, compiler->parser, compiler, true);
// Compile the method signature.
signatureFn(&methodCompiler, &signature);
@@ -3416,6 +3420,9 @@ void definition(Compiler* compiler)
ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
bool isExpression, bool printErrors)
{
// Skip the UTF-8 BOM if there is one.
if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
Parser parser;
parser.vm = vm;
parser.module = module;
@@ -3443,14 +3450,15 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
nextToken(&parser);
int numExistingVariables = module->variables.count;
Compiler compiler;
initCompiler(&compiler, &parser, NULL, true);
initCompiler(&compiler, &parser, NULL, false);
ignoreNewlines(&compiler);
if (isExpression)
{
expression(&compiler);
consume(&compiler, TOKEN_EOF, "Expect end of expression.");
}
else
{
@@ -3458,7 +3466,7 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
{
definition(&compiler);
// If there is no newline, it must be the end of the block on the same line.
// If there is no newline, it must be the end of file on the same line.
if (!matchLine(&compiler))
{
consume(&compiler, TOKEN_EOF, "Expect end of file.");
@@ -3480,8 +3488,8 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* source,
{
// Synthesize a token for the original use site.
parser.previous.type = TOKEN_NAME;
parser.previous.start = parser.module->variableNames.data[i].buffer;
parser.previous.length = parser.module->variableNames.data[i].length;
parser.previous.start = parser.module->variableNames.data[i]->value;
parser.previous.length = parser.module->variableNames.data[i]->length;
parser.previous.line = (int)AS_NUM(parser.module->variables.data[i]);
error(&compiler, "Variable is used but not defined.");
}
@@ -3495,7 +3503,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
int ip = 0;
for (;;)
{
Code instruction = (Code)fn->code.data[ip++];
Code instruction = (Code)fn->code.data[ip];
switch (instruction)
{
case CODE_LOAD_FIELD:
@@ -3505,7 +3513,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
// Shift this class's fields down past the inherited ones. We don't
// check for overflow here because we'll see if the number of fields
// overflows when the subclass is created.
fn->code.data[ip++] += classObj->superclass->numFields;
fn->code.data[ip + 1] += classObj->superclass->numFields;
break;
case CODE_SUPER_0:
@@ -3526,11 +3534,8 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
case CODE_SUPER_15:
case CODE_SUPER_16:
{
// Skip over the symbol.
ip += 2;
// Fill in the constant slot with a reference to the superclass.
int constant = (fn->code.data[ip] << 8) | fn->code.data[ip + 1];
int constant = (fn->code.data[ip + 3] << 8) | fn->code.data[ip + 4];
fn->constants.data[constant] = OBJ_VAL(classObj->superclass);
break;
}
@@ -3538,10 +3543,8 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
case CODE_CLOSURE:
{
// Bind the nested closure too.
int constant = (fn->code.data[ip] << 8) | fn->code.data[ip + 1];
int constant = (fn->code.data[ip + 1] << 8) | fn->code.data[ip + 2];
wrenBindMethodCode(classObj, AS_FN(fn->constants.data[constant]));
ip += getNumArguments(fn->code.data, fn->constants.data, ip - 1);
break;
}
@@ -3550,9 +3553,9 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
default:
// Other instructions are unaffected, so just skip over them.
ip += getNumArguments(fn->code.data, fn->constants.data, ip - 1);
break;
}
ip += 1 + getNumArguments(fn->code.data, fn->constants.data, ip);
}
}
@@ -3567,6 +3570,12 @@ void wrenMarkCompiler(WrenVM* vm, Compiler* compiler)
{
wrenGrayObj(vm, (Obj*)compiler->fn);
wrenGrayObj(vm, (Obj*)compiler->constants);
if (compiler->enclosingClass != NULL)
{
wrenBlackenSymbolTable(vm, &compiler->enclosingClass->fields);
}
compiler = compiler->parent;
}
while (compiler != NULL);
+1 -9
View File
@@ -59,15 +59,7 @@ DEF_PRIMITIVE(fiber_new)
RETURN_ERROR("Function cannot take more than one parameter.");
}
ObjFiber* newFiber = wrenNewFiber(vm, closure);
// The compiler expects the first slot of a function to hold the receiver.
// Since a fiber's stack is invoked directly, it doesn't have one, so put it
// in here.
newFiber->stack[0] = NULL_VAL;
newFiber->stackTop++;
RETURN_OBJ(newFiber);
RETURN_OBJ(wrenNewFiber(vm, closure));
}
DEF_PRIMITIVE(fiber_abort)
+42
View File
@@ -236,6 +236,48 @@ class String is Sequence {
return result
}
trim() { trim_("\t\r\n ", true, true) }
trim(chars) { trim_(chars, true, true) }
trimEnd() { trim_("\t\r\n ", false, true) }
trimEnd(chars) { trim_(chars, false, true) }
trimStart() { trim_("\t\r\n ", true, false) }
trimStart(chars) { trim_(chars, true, false) }
trim_(chars, trimStart, trimEnd) {
if (!(chars is String)) {
Fiber.abort("Characters must be a string.")
}
var codePoints = chars.codePoints.toList
var start
if (trimStart) {
while (start = iterate(start)) {
if (!codePoints.contains(codePointAt_(start))) break
}
if (start == false) return ""
} else {
start = 0
}
var end
if (trimEnd) {
end = byteCount_ - 1
while (end >= start) {
var codePoint = codePointAt_(end)
if (codePoint != -1 && !codePoints.contains(codePoint)) break
end = end - 1
}
if (end < start) return ""
} else {
end = -1
}
return this[start..end]
}
*(count) {
if (!(count is Num) || !count.isInteger || count < 0) {
Fiber.abort("Count must be a non-negative integer.")
+50
View File
@@ -238,6 +238,56 @@ static const char* coreModuleSource =
" return result\n"
" }\n"
"\n"
" trim() { trim_(\"\t\r\n \", true, true) }\n"
"\n"
" trim(chars) { trim_(chars, true, true) }\n"
"\n"
" trimEnd() { trim_(\"\t\r\n \", false, true) }\n"
"\n"
" trimEnd(chars) { trim_(chars, false, true) }\n"
"\n"
" trimStart() { trim_(\"\t\r\n \", true, false) }\n"
"\n"
" trimStart(chars) { trim_(chars, true, false) }\n"
"\n"
" trim_(chars, trimStart, trimEnd) {\n"
" if (!(chars is String)) {\n"
" Fiber.abort(\"Characters must be a string.\")\n"
" }\n"
"\n"
" var codePoints = chars.codePoints.toList\n"
"// System.print(\"code points %(codePoints)\")\n"
"\n"
" var start\n"
" if (trimStart) {\n"
" while (start = iterate(start)) {\n"
" if (!codePoints.contains(codePointAt_(start))) break\n"
" }\n"
"\n"
" if (start == false) return \"\"\n"
" } else {\n"
" start = 0\n"
" }\n"
"\n"
" var end\n"
" if (trimEnd) {\n"
" end = byteCount_ - 1\n"
" while (end >= start) {\n"
" var codePoint = codePointAt_(end)\n"
"// System.print(\"test %(end) : %(codePoint)\")\n"
" if (codePoint != -1 && !codePoints.contains(codePoint)) break\n"
" end = end - 1\n"
" }\n"
"\n"
"// System.print(\"range %(start) %(end)\")\n"
" if (end < start) return \"\"\n"
" } else {\n"
" end = -1\n"
" }\n"
"\n"
" return this[start..end]\n"
" }\n"
"\n"
" *(count) {\n"
" if (!(count is Num) || !count.isInteger || count < 0) {\n"
" Fiber.abort(\"Count must be a non-negative integer.\")\n"
+6 -6
View File
@@ -159,7 +159,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int slot = READ_SHORT();
printf("%-16s %5d '%s'\n", "LOAD_MODULE_VAR", slot,
fn->module->variableNames.data[slot].buffer);
fn->module->variableNames.data[slot]->value);
break;
}
@@ -167,7 +167,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int slot = READ_SHORT();
printf("%-16s %5d '%s'\n", "STORE_MODULE_VAR", slot,
fn->module->variableNames.data[slot].buffer);
fn->module->variableNames.data[slot]->value);
break;
}
@@ -199,7 +199,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
int numArgs = bytecode[i - 1] - CODE_CALL_0;
int symbol = READ_SHORT();
printf("CALL_%-11d %5d '%s'\n", numArgs, symbol,
vm->methodNames.data[symbol].buffer);
vm->methodNames.data[symbol]->value);
break;
}
@@ -225,7 +225,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
int symbol = READ_SHORT();
int superclass = READ_SHORT();
printf("SUPER_%-10d %5d '%s' %5d\n", numArgs, symbol,
vm->methodNames.data[symbol].buffer, superclass);
vm->methodNames.data[symbol]->value, superclass);
break;
}
@@ -301,7 +301,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int symbol = READ_SHORT();
printf("%-16s %5d '%s'\n", "METHOD_INSTANCE", symbol,
vm->methodNames.data[symbol].buffer);
vm->methodNames.data[symbol]->value);
break;
}
@@ -309,7 +309,7 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
{
int symbol = READ_SHORT();
printf("%-16s %5d '%s'\n", "METHOD_STATIC", symbol,
vm->methodNames.data[symbol].buffer);
vm->methodNames.data[symbol]->value);
break;
}
+20 -16
View File
@@ -5,7 +5,7 @@
DEFINE_BUFFER(Byte, uint8_t);
DEFINE_BUFFER(Int, int);
DEFINE_BUFFER(String, String);
DEFINE_BUFFER(String, ObjString*);
void wrenSymbolTableInit(SymbolTable* symbols)
{
@@ -14,24 +14,18 @@ void wrenSymbolTableInit(SymbolTable* symbols)
void wrenSymbolTableClear(WrenVM* vm, SymbolTable* symbols)
{
for (int i = 0; i < symbols->count; i++)
{
DEALLOCATE(vm, symbols->data[i].buffer);
}
wrenStringBufferClear(vm, symbols);
}
int wrenSymbolTableAdd(WrenVM* vm, SymbolTable* symbols,
const char* name, size_t length)
{
String symbol;
symbol.buffer = ALLOCATE_ARRAY(vm, char, length + 1);
memcpy(symbol.buffer, name, length);
symbol.buffer[length] = '\0';
symbol.length = (int)length;
ObjString* symbol = AS_STRING(wrenNewStringLength(vm, name, length));
wrenPushRoot(vm, &symbol->obj);
wrenStringBufferWrite(vm, symbols, symbol);
wrenPopRoot(vm);
return symbols->count - 1;
}
@@ -46,20 +40,30 @@ int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
return wrenSymbolTableAdd(vm, symbols, name, length);
}
int wrenSymbolTableFind(const SymbolTable* symbols,
const char* name, size_t length)
int wrenSymbolTableFind(const SymbolTable* symbols,
const char* name, size_t length)
{
// See if the symbol is already defined.
// TODO: O(n). Do something better.
for (int i = 0; i < symbols->count; i++)
{
if (symbols->data[i].length == length &&
memcmp(symbols->data[i].buffer, name, length) == 0) return i;
if (wrenStringEqualsCString(symbols->data[i], name, length)) return i;
}
return -1;
}
void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable)
{
for (int i = 0; i < symbolTable->count; i++)
{
wrenGrayObj(vm, &symbolTable->data[i]->obj);
}
// Keep track of how much memory is still in use.
vm->bytesAllocated += symbolTable->capacity * sizeof(*symbolTable->data);
}
int wrenUtf8EncodeNumBytes(int value)
{
ASSERT(value >= 0, "Cannot encode a negative value.");
+8 -9
View File
@@ -6,12 +6,9 @@
// Reusable data structures and other utility functions.
// A simple structure to keep track of a string and its length (including the
// null-terminator).
typedef struct {
char* buffer;
uint32_t length;
} String;
// Forward declare this here to break a cycle between wren_utils.h and
// wren_value.h.
typedef struct sObjString ObjString;
// We need buffers of a few different types. To avoid lots of casting between
// void* and back, we'll use the preprocessor as a poor man's generics and let
@@ -68,7 +65,7 @@ typedef struct {
DECLARE_BUFFER(Byte, uint8_t);
DECLARE_BUFFER(Int, int);
DECLARE_BUFFER(String, String);
DECLARE_BUFFER(String, ObjString*);
// TODO: Change this to use a map.
typedef StringBuffer SymbolTable;
@@ -90,8 +87,10 @@ int wrenSymbolTableEnsure(WrenVM* vm, SymbolTable* symbols,
const char* name, size_t length);
// Looks up name in the symbol table. Returns its index if found or -1 if not.
int wrenSymbolTableFind(const SymbolTable* symbols,
const char* name, size_t length);
int wrenSymbolTableFind(const SymbolTable* symbols,
const char* name, size_t length);
void wrenBlackenSymbolTable(WrenVM* vm, SymbolTable* symbolTable);
// Returns the number of bytes needed to encode [value] in UTF-8.
//
+21 -17
View File
@@ -159,27 +159,31 @@ ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure)
ObjFiber* fiber = ALLOCATE(vm, ObjFiber);
initObj(vm, &fiber->obj, OBJ_FIBER, vm->fiberClass);
fiber->stack = stack;
fiber->stackTop = fiber->stack;
fiber->stackCapacity = stackCapacity;
fiber->frames = frames;
fiber->frameCapacity = INITIAL_CALL_FRAMES;
fiber->stack = stack;
fiber->stackCapacity = stackCapacity;
wrenResetFiber(vm, fiber, closure);
fiber->numFrames = 0;
return fiber;
}
void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure)
{
// Reset everything.
fiber->stackTop = fiber->stack;
fiber->openUpvalues = NULL;
fiber->caller = NULL;
fiber->error = NULL_VAL;
fiber->callerIsTrying = false;
fiber->numFrames = 0;
if (closure != NULL)
{
// Initialize the first call frame.
wrenAppendCallFrame(vm, fiber, closure, fiber->stack);
// Initialize the first call frame.
if (closure != NULL) wrenAppendCallFrame(vm, fiber, closure, fiber->stack);
// The first slot always holds the closure.
fiber->stackTop[0] = OBJ_VAL(closure);
fiber->stackTop++;
}
return fiber;
}
void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed)
@@ -1137,11 +1141,12 @@ static void blackenModule(WrenVM* vm, ObjModule* module)
wrenGrayValue(vm, module->variables.data[i]);
}
wrenBlackenSymbolTable(vm, &module->variableNames);
wrenGrayObj(vm, (Obj*)module->name);
// Keep track of how much memory is still in use.
vm->bytesAllocated += sizeof(ObjModule);
// TODO: Track memory for symbol table and buffer.
}
static void blackenRange(WrenVM* vm, ObjRange* range)
@@ -1296,9 +1301,8 @@ bool wrenValuesEqual(Value a, Value b)
{
ObjString* aString = (ObjString*)aObj;
ObjString* bString = (ObjString*)bObj;
return aString->length == bString->length &&
aString->hash == bString->hash &&
memcmp(aString->value, bString->value, aString->length) == 0;
return aString->hash == bString->hash &&
wrenStringEqualsCString(aString, bString->value, bString->length);
}
default:
+17 -11
View File
@@ -2,6 +2,7 @@
#define wren_value_h
#include <stdbool.h>
#include <string.h>
#include "wren_common.h"
#include "wren_utils.h"
@@ -102,7 +103,8 @@ typedef enum {
typedef struct sObjClass ObjClass;
// Base struct for all heap-allocated objects.
typedef struct sObj
typedef struct sObj Obj;
struct sObj
{
ObjType type;
bool isDark;
@@ -112,7 +114,7 @@ typedef struct sObj
// The next object in the linked list of all currently allocated objects.
struct sObj* next;
} Obj;
};
#if WREN_NAN_TAGGING
@@ -145,7 +147,7 @@ typedef struct
DECLARE_BUFFER(Value, Value);
// A heap-allocated string object.
typedef struct
struct sObjString
{
Obj obj;
@@ -157,7 +159,7 @@ typedef struct
// Inline array of the string's bytes followed by a null terminator.
char value[FLEXIBLE_ARRAY];
} ObjString;
};
// The dynamically allocated data structure for a variable that has been used
// by a closure. Whenever a function accesses a variable declared in an
@@ -171,7 +173,7 @@ typedef struct
// be closed. When that happens, the value gets copied off the stack into the
// upvalue itself. That way, it can have a longer lifetime than the stack
// variable.
typedef struct sUpvalue
typedef struct sObjUpvalue
{
// The object header. Note that upvalues have this because they are garbage
// collected, but they are not first class Wren objects.
@@ -187,7 +189,7 @@ typedef struct sUpvalue
// Open upvalues are stored in a linked list by the fiber. This points to the
// next upvalue in that list.
struct sUpvalue* next;
struct sObjUpvalue* next;
} ObjUpvalue;
// The type of a primitive function.
@@ -628,10 +630,6 @@ ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn);
// Creates a new fiber object that will invoke [closure].
ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure);
// Resets [fiber] back to an initial state where it is ready to invoke
// [closure].
void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, ObjClosure* closure);
// Adds a new [CallFrame] to [fiber] invoking [closure] whose stack starts at
// [stackStart].
static inline void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber,
@@ -733,7 +731,15 @@ Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index);
// Search for the first occurence of [needle] within [haystack] and returns its
// zero-based offset. Returns `UINT32_MAX` if [haystack] does not contain
// [needle].
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle, uint32_t startIndex);
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle,
uint32_t startIndex);
// Returns true if [a] and [b] represent the same string.
static inline bool wrenStringEqualsCString(const ObjString* a,
const char* b, size_t length)
{
return a->length == length && memcmp(a->value, b, length) == 0;
}
// Creates a new open upvalue pointing to [value] on the stack.
ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value);
+24 -17
View File
@@ -151,6 +151,9 @@ void wrenCollectGarbage(WrenVM* vm)
// Any object the compiler is using (if there is one).
if (vm->compiler != NULL) wrenMarkCompiler(vm, vm->compiler);
// Method names.
wrenBlackenSymbolTable(vm, &vm->methodNames);
// Now that we have grayed the roots, do a depth-first search over all of the
// reachable objects.
wrenBlackenObjects(vm);
@@ -421,7 +424,7 @@ static void runtimeError(WrenVM* vm)
static void methodNotFound(WrenVM* vm, ObjClass* classObj, int symbol)
{
vm->fiber->error = wrenStringFormat(vm, "@ does not implement '$'.",
OBJ_VAL(classObj->name), vm->methodNames.data[symbol].buffer);
OBJ_VAL(classObj->name), vm->methodNames.data[symbol]->value);
}
// Checks that [value], which must be a closure, does not require more
@@ -493,8 +496,8 @@ static ObjClosure* compileInModule(WrenVM* vm, Value name, const char* source,
for (int i = 0; i < coreModule->variables.count; i++)
{
wrenDefineVariable(vm, module,
coreModule->variableNames.data[i].buffer,
coreModule->variableNames.data[i].length,
coreModule->variableNames.data[i]->value,
coreModule->variableNames.data[i]->length,
coreModule->variables.data[i]);
}
}
@@ -730,7 +733,9 @@ static Value importModule(WrenVM* vm, Value name)
// If the module is already loaded, we don't need to do anything.
Value existing = wrenMapGet(vm->modules, name);
if (!IS_UNDEFINED(existing)) return existing;
wrenPushRoot(vm, AS_OBJ(name));
const char* source = NULL;
bool allocatedSource = true;
@@ -759,6 +764,7 @@ static Value importModule(WrenVM* vm, Value name)
if (source == NULL)
{
vm->fiber->error = wrenStringFormat(vm, "Could not load module '@'.", name);
wrenPopRoot(vm); // name.
return NULL_VAL;
}
@@ -773,9 +779,12 @@ static Value importModule(WrenVM* vm, Value name)
{
vm->fiber->error = wrenStringFormat(vm,
"Could not compile module '@'.", name);
wrenPopRoot(vm); // name.
return NULL_VAL;
}
wrenPopRoot(vm); // name.
// Return the closure that executes the module.
return OBJ_VAL(moduleClosure);
}
@@ -1306,20 +1315,18 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
CASE_CODE(IMPORT_MODULE):
{
Value name = fn->constants.data[READ_SHORT()];
Value result = importModule(vm, name);
// Make a slot on the stack for the module's fiber to place the return
// value. It will be popped after this fiber is resumed. Store the
// imported module's closure in the slot in case a GC happens when
// invoking the closure.
PUSH(importModule(vm, fn->constants.data[READ_SHORT()]));
if (!IS_NULL(fiber->error)) RUNTIME_ERROR();
// Make a slot on the stack for the module's closure to place the return
// value. It will be popped after the module body code returns.
PUSH(NULL_VAL);
// If we get a closure, call it to execute the module body.
if (IS_CLOSURE(result))
if (IS_CLOSURE(PEEK()))
{
STORE_FRAME();
ObjClosure* closure = AS_CLOSURE(result);
ObjClosure* closure = AS_CLOSURE(PEEK());
callFunction(vm, fiber, closure, 1);
LOAD_FRAME();
}
@@ -1327,7 +1334,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
{
// The module has already been loaded. Remember it so we can import
// variables from it if needed.
vm->lastModule = AS_MODULE(result);
vm->lastModule = AS_MODULE(PEEK());
}
DISPATCH();
@@ -1477,7 +1484,7 @@ WrenInterpretResult wrenInterpret(WrenVM* vm, const char* module,
wrenPushRoot(vm, (Obj*)closure);
ObjFiber* fiber = wrenNewFiber(vm, closure);
wrenPopRoot(vm); // closure.
return runInterpreter(vm, fiber);
}
@@ -1774,7 +1781,7 @@ void wrenGetVariable(WrenVM* vm, const char* module, const char* name,
int slot)
{
ASSERT(module != NULL, "Module cannot be NULL.");
ASSERT(module != NULL, "Variable name cannot be NULL.");
ASSERT(name != NULL, "Variable name cannot be NULL.");
validateApiSlot(vm, slot);
Value moduleName = wrenStringFormat(vm, "$", module);