Merge branch 'master' into libuv

# Conflicts:
#	project/xcode/wren.xcodeproj/project.pbxproj
#	script/wren.mk
#	src/cli/main.c
#	src/cli/vm.c
#	src/cli/vm.h
#	test/api/main.c
This commit is contained in:
Bob Nystrom
2015-08-28 23:13:56 -07:00
66 changed files with 1042 additions and 487 deletions
+2 -2
View File
@@ -12,14 +12,14 @@ int main(int argc, const char* argv[])
fprintf(stderr, "Usage: wren [file]\n");
return 64; // EX_USAGE.
}
if (argc == 1)
{
runRepl();
}
else if (argc == 2)
{
runFile(argv[1], NULL);
runFile(argv[1]);
}
return 0;
+51 -33
View File
@@ -10,46 +10,59 @@
// The single VM instance that the CLI uses.
WrenVM* vm;
WrenBindForeignMethodFn externalBindForeign;
static WrenBindForeignMethodFn bindMethodFn = NULL;
static WrenBindForeignClassFn bindClassFn = NULL;
uv_loop_t* loop;
static WrenForeignMethodFn bindForeignMethod(WrenVM* vm,
const char* module,
const char* className,
bool isStatic,
const char* signature)
/// Binds foreign methods declared in either built in modules, or the injected
/// API test modules.
static WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module,
const char* className, bool isStatic, const char* signature)
{
if (strcmp(module, "timer") == 0)
{
return timerBindForeign(vm, className, isStatic, signature);
}
if (externalBindForeign != NULL)
if (bindMethodFn != NULL)
{
return externalBindForeign(vm, module, className, isStatic, signature);
return bindMethodFn(vm, module, className, isStatic, signature);
}
return NULL;
}
/// Binds foreign classes declared in either built in modules, or the injected
/// API test modules.
static WrenForeignClassMethods bindForeignClass(
WrenVM* vm, const char* module, const char* className)
{
WrenForeignClassMethods methods = { NULL, NULL };
// TODO: Bind classes for built-in modules here.
if (bindClassFn != NULL)
{
return bindClassFn(vm, module, className);
}
return methods;
}
static void initVM()
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.bindForeignMethodFn = bindForeignMethod;
config.bindForeignClassFn = bindForeignClass;
config.loadModuleFn = readModule;
// Since we're running in a standalone process, be generous with memory.
config.initialHeapSize = 1024 * 1024 * 100;
// Use defaults for these.
config.reallocateFn = NULL;
config.minHeapSize = 0;
config.heapGrowthPercent = 0;
vm = wrenNewVM(&config);
// Initialize the event loop.
loop = (uv_loop_t*)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
@@ -58,17 +71,15 @@ static void initVM()
static void freeVM()
{
timerReleaseMethods();
uv_loop_close(loop);
free(loop);
wrenFreeVM(vm);
}
void runFile(const char* path, WrenBindForeignMethodFn bindForeign)
void runFile(const char* path)
{
externalBindForeign = bindForeign;
// Use the directory where the file is as the root to resolve imports
// relative to.
char* root = NULL;
@@ -87,18 +98,18 @@ void runFile(const char* path, WrenBindForeignMethodFn bindForeign)
fprintf(stderr, "Could not find file \"%s\".\n", path);
exit(66);
}
initVM();
WrenInterpretResult result = wrenInterpret(vm, path, source);
if (result == WREN_RESULT_SUCCESS)
{
uv_run(loop, UV_RUN_DEFAULT);
}
freeVM();
free(source);
free(root);
@@ -110,30 +121,30 @@ void runFile(const char* path, WrenBindForeignMethodFn bindForeign)
int runRepl()
{
initVM();
printf("\\\\/\"-\n");
printf(" \\_/ wren v0.0.0\n");
char line[MAX_LINE_LENGTH];
for (;;)
{
printf("> ");
if (!fgets(line, MAX_LINE_LENGTH, stdin))
{
printf("\n");
break;
}
// TODO: Handle failure.
wrenInterpret(vm, "Prompt", line);
// TODO: Automatically print the result of expressions.
}
freeVM();
return 0;
}
@@ -146,3 +157,10 @@ uv_loop_t* getLoop()
{
return loop;
}
void setForeignCallbacks(
WrenBindForeignMethodFn bindMethod, WrenBindForeignClassFn bindClass)
{
bindMethodFn = bindMethod;
bindClassFn = bindClass;
}
+9 -3
View File
@@ -6,9 +6,8 @@
// Executes the Wren script at [path] in a new VM.
//
// If [bindForeign] is not `NULL`, it will be called to register any foreign
// methods that the CLI itself doesn't handle.
void runFile(const char* path, WrenBindForeignMethodFn bindForeign);
// Exits if the script failed or could not be loaded.
void runFile(const char* path);
// Runs the Wren interactive REPL.
int runRepl();
@@ -19,4 +18,11 @@ WrenVM* getVM();
// Gets the event loop the VM is using.
uv_loop_t* getLoop();
// Adds additional callbacks to use when binding foreign members from Wren.
//
// Used by the API test executable to let it wire up its own foreign functions.
// This must be called before calling [createVM()].
void setForeignCallbacks(WrenBindForeignMethodFn bindMethod,
WrenBindForeignClassFn bindClass);
#endif
+48
View File
@@ -53,6 +53,26 @@ typedef WrenForeignMethodFn (*WrenBindForeignMethodFn)(WrenVM* vm,
bool isStatic,
const char* signature);
typedef struct
{
// The callback invoked when the foreign object is created.
//
// This must be provided. Inside the body of this, it must call
// [wrenAllocateForeign] exactly once.
WrenForeignMethodFn allocate;
// The callback invoked when the garbage collector is about to collecto a
// foreign object's memory.
//
// This may be `NULL` if the foreign class does not need to finalize.
WrenForeignMethodFn finalize;
} WrenForeignClassMethods;
// Returns a pair of pointers to the foreign methods used to allocate and
// finalize the data for instances of [className] in [module].
typedef WrenForeignClassMethods (*WrenBindForeignClassFn)(
WrenVM* vm, const char* module, const char* className);
typedef struct
{
// The callback Wren will use to allocate, reallocate, and deallocate memory.
@@ -88,6 +108,14 @@ typedef struct
// Wren will report it as runtime error.
WrenBindForeignMethodFn bindForeignMethodFn;
// The callback Wren uses to find a foreign class and get its foreign methods.
//
// When a foreign class is declared, this will be called with the class's
// module and name when the class body is executed. It should return the
// foreign functions uses to allocate and (optionally) finalize the bytes
// stored in the foreign object when an instance is created.
WrenBindForeignClassFn bindForeignClassFn;
// The number of bytes Wren will allocate before triggering the first garbage
// collection.
//
@@ -129,6 +157,11 @@ typedef enum {
WREN_RESULT_RUNTIME_ERROR
} WrenInterpretResult;
// Initializes [configuration] with all of its default values.
//
// Call this before setting the particular fields you care about.
void wrenInitConfiguration(WrenConfiguration* configuration);
// Creates a new Wren virtual machine using the given [configuration]. Wren
// will copy the configuration data, so the argument passed to this can be
// freed after calling this. If [configuration] is `NULL`, uses a default
@@ -186,6 +219,16 @@ void wrenReleaseMethod(WrenVM* vm, WrenMethod* method);
// longer be used.
void wrenReleaseValue(WrenVM* vm, WrenValue* value);
// This must be called once inside a foreign class's allocator function.
//
// It tells Wren how many bytes of raw data need to be stored in the foreign
// object and creates the new object with that size. It returns a pointer to
// the foreign object's data.
void* wrenAllocateForeign(WrenVM* vm, size_t size);
// Returns the number of arguments available to the current foreign method.
int wrenGetArgumentCount(WrenVM* vm);
// The following functions read one of the arguments passed to a foreign call.
// They may only be called while within a function provided to
// [wrenDefineMethod] or [wrenDefineStaticMethod] that Wren has invoked.
@@ -214,6 +257,11 @@ bool wrenGetArgumentBool(WrenVM* vm, int index);
// a number.
double wrenGetArgumentDouble(WrenVM* vm, int index);
// Reads a foreign object argument for a foreign call and returns a pointer to
// the foreign data stored with it. Returns NULL if the argument is not a
// foreign object.
void* wrenGetArgumentForeign(WrenVM* vm, int index);
// Reads an string argument for a foreign call. Returns NULL if the argument is
// not a string.
//
+2 -2
View File
@@ -99,10 +99,10 @@
// foo() // No-argument method.
// foo(_) // One-argument method.
// foo(_,_) // Two-argument method.
// this foo() // Constructor initializer.
// init foo() // Constructor initializer.
//
// The maximum signature length takes into account the longest method name, the
// maximum number of parameters with separators between them, "this ", and "()".
// maximum number of parameters with separators between them, "init ", and "()".
#define MAX_METHOD_SIGNATURE (MAX_METHOD_NAME + (MAX_PARAMETERS * 2) + 6)
// The maximum length of an identifier. The only real reason for this limitation
+150 -161
View File
@@ -154,14 +154,9 @@ typedef struct
// If a syntax or compile error has occurred.
bool hasError;
// A buffer for the unescaped text of the current token if it's a string
// literal. Unlike the raw token, this will have escape sequences translated
// to their literal equivalent.
ByteBuffer string;
// If a number literal is currently being parsed this will hold its value.
double number;
// The parsed value if the current token is a literal.
Value value;
} Parser;
typedef struct
@@ -252,6 +247,9 @@ typedef struct
// Symbol table for the fields of the class.
SymbolTable* fields;
// True if the class being compiled is a foreign class.
bool isForeign;
// True if the current method being compiled is static.
bool inStatic;
@@ -369,6 +367,8 @@ static void error(Compiler* compiler, const char* format, ...)
// Adds [constant] to the constant pool and returns its index.
static int addConstant(Compiler* compiler, Value constant)
{
if (compiler->parser->hasError) return -1;
if (compiler->constants.count < MAX_CONSTANTS)
{
if (IS_OBJ(constant)) wrenPushRoot(compiler->parser->vm, AS_OBJ(constant));
@@ -476,6 +476,14 @@ static char nextChar(Parser* parser)
return c;
}
// If the current character is [c], consumes it and returns `true`.
static bool matchChar(Parser* parser, char c)
{
if (peekChar(parser) != c) return false;
nextChar(parser);
return true;
}
// Sets the parser's current token to the given [type] and current character
// range.
static void makeToken(Parser* parser, TokenType type)
@@ -493,14 +501,7 @@ static void makeToken(Parser* parser, TokenType type)
// [two]. Otherwise makes a token of type [one].
static void twoCharToken(Parser* parser, char c, TokenType two, TokenType one)
{
if (peekChar(parser) == c)
{
nextChar(parser);
makeToken(parser, two);
return;
}
makeToken(parser, one);
makeToken(parser, matchChar(parser, c) ? two : one);
}
// Skips the rest of the current line.
@@ -515,8 +516,6 @@ static void skipLineComment(Parser* parser)
// Skips the rest of a block comment.
static void skipBlockComment(Parser* parser)
{
nextChar(parser); // The opening "*".
int nesting = 1;
while (nesting > 0)
{
@@ -578,13 +577,13 @@ static void makeNumber(Parser* parser, bool isHex)
// We don't check that the entire token is consumed because we've already
// scanned it ourselves and know it's valid.
parser->number = isHex ? strtol(parser->tokenStart, NULL, 16)
: strtod(parser->tokenStart, NULL);
parser->value = NUM_VAL(isHex ? strtol(parser->tokenStart, NULL, 16)
: strtod(parser->tokenStart, NULL));
if (errno == ERANGE)
{
lexError(parser, "Number literal was too large.");
parser->number = 0;
parser->value = NUM_VAL(0);
}
makeToken(parser, TOKEN_NUMBER);
@@ -605,7 +604,6 @@ static void readHexNumber(Parser* parser)
// Finishes lexing a number literal.
static void readNumber(Parser* parser)
{
// TODO: scientific, etc.
while (isDigit(peekChar(parser))) nextChar(parser);
// See if it has a floating point. Make sure there is a digit after the "."
@@ -615,6 +613,20 @@ static void readNumber(Parser* parser)
nextChar(parser);
while (isDigit(peekChar(parser))) nextChar(parser);
}
// See if the number is in scientific notation.
if (matchChar(parser, 'e') || matchChar(parser, 'E'))
{
// Allow a negative exponent.
matchChar(parser, '-');
if (!isDigit(peekChar(parser)))
{
lexError(parser, "Unterminated scientific notation.");
}
while (isDigit(peekChar(parser))) nextChar(parser);
}
makeNumber(parser, false);
}
@@ -650,12 +662,6 @@ static void readName(Parser* parser, TokenType type)
makeToken(parser, type);
}
// Adds [c] to the current string literal being tokenized.
static void addStringChar(Parser* parser, char c)
{
wrenByteBufferWrite(parser->vm, &parser->string, c);
}
// Reads [digits] hex digits in a string literal and returns their number value.
static int readHexEscape(Parser* parser, int digits, const char* description)
{
@@ -686,7 +692,7 @@ static int readHexEscape(Parser* parser, int digits, const char* description)
}
// Reads a four hex digit Unicode escape sequence in a string literal.
static void readUnicodeEscape(Parser* parser)
static void readUnicodeEscape(Parser* parser, ByteBuffer* string)
{
int value = readHexEscape(parser, 4, "Unicode");
@@ -694,16 +700,16 @@ static void readUnicodeEscape(Parser* parser)
int numBytes = wrenUtf8NumBytes(value);
if (numBytes != 0)
{
wrenByteBufferFill(parser->vm, &parser->string, 0, numBytes);
wrenUtf8Encode(value,
parser->string.data + parser->string.count - numBytes);
wrenByteBufferFill(parser->vm, string, 0, numBytes);
wrenUtf8Encode(value, string->data + string->count - numBytes);
}
}
// Finishes lexing a string literal.
static void readString(Parser* parser)
{
wrenByteBufferClear(parser->vm, &parser->string);
ByteBuffer string;
wrenByteBufferInit(&string);
for (;;)
{
@@ -724,20 +730,21 @@ static void readString(Parser* parser)
{
switch (nextChar(parser))
{
case '"': addStringChar(parser, '"'); break;
case '\\': addStringChar(parser, '\\'); break;
case '0': addStringChar(parser, '\0'); break;
case 'a': addStringChar(parser, '\a'); break;
case 'b': addStringChar(parser, '\b'); break;
case 'f': addStringChar(parser, '\f'); break;
case 'n': addStringChar(parser, '\n'); break;
case 'r': addStringChar(parser, '\r'); break;
case 't': addStringChar(parser, '\t'); break;
case 'u': readUnicodeEscape(parser); break;
case '"': wrenByteBufferWrite(parser->vm, &string, '"'); break;
case '\\': wrenByteBufferWrite(parser->vm, &string, '\\'); break;
case '0': wrenByteBufferWrite(parser->vm, &string, '\0'); break;
case 'a': wrenByteBufferWrite(parser->vm, &string, '\a'); break;
case 'b': wrenByteBufferWrite(parser->vm, &string, '\b'); break;
case 'f': wrenByteBufferWrite(parser->vm, &string, '\f'); break;
case 'n': wrenByteBufferWrite(parser->vm, &string, '\n'); break;
case 'r': wrenByteBufferWrite(parser->vm, &string, '\r'); break;
case 't': wrenByteBufferWrite(parser->vm, &string, '\t'); break;
case 'u': readUnicodeEscape(parser, &string); break;
// TODO: 'U' for 8 octet Unicode escapes.
case 'v': addStringChar(parser, '\v'); break;
case 'v': wrenByteBufferWrite(parser->vm, &string, '\v'); break;
case 'x':
addStringChar(parser, (uint8_t)readHexEscape(parser, 2, "byte"));
wrenByteBufferWrite(parser->vm, &string,
(uint8_t)readHexEscape(parser, 2, "byte"));
break;
default:
@@ -748,10 +755,12 @@ static void readString(Parser* parser)
}
else
{
addStringChar(parser, c);
wrenByteBufferWrite(parser->vm, &string, c);
}
}
parser->value = wrenNewString(parser->vm, (char*)string.data, string.count);
wrenByteBufferClear(parser->vm, &string);
makeToken(parser, TOKEN_STRING);
}
@@ -779,38 +788,38 @@ static void nextToken(Parser* parser)
case '{': makeToken(parser, TOKEN_LEFT_BRACE); return;
case '}': makeToken(parser, TOKEN_RIGHT_BRACE); return;
case ':': makeToken(parser, TOKEN_COLON); return;
case '.':
if (peekChar(parser) == '.')
{
nextChar(parser);
if (peekChar(parser) == '.')
{
nextChar(parser);
makeToken(parser, TOKEN_DOTDOTDOT);
return;
}
makeToken(parser, TOKEN_DOTDOT);
return;
}
makeToken(parser, TOKEN_DOT);
return;
case ',': makeToken(parser, TOKEN_COMMA); return;
case '*': makeToken(parser, TOKEN_STAR); return;
case '%': makeToken(parser, TOKEN_PERCENT); return;
case '^': makeToken(parser, TOKEN_CARET); return;
case '+': makeToken(parser, TOKEN_PLUS); return;
case '-': makeToken(parser, TOKEN_MINUS); return;
case '~': makeToken(parser, TOKEN_TILDE); return;
case '?': makeToken(parser, TOKEN_QUESTION); return;
case '|': twoCharToken(parser, '|', TOKEN_PIPEPIPE, TOKEN_PIPE); return;
case '&': twoCharToken(parser, '&', TOKEN_AMPAMP, TOKEN_AMP); return;
case '=': twoCharToken(parser, '=', TOKEN_EQEQ, TOKEN_EQ); return;
case '!': twoCharToken(parser, '=', TOKEN_BANGEQ, TOKEN_BANG); return;
case '.':
if (matchChar(parser, '.'))
{
twoCharToken(parser, '.', TOKEN_DOTDOTDOT, TOKEN_DOTDOT);
return;
}
makeToken(parser, TOKEN_DOT);
return;
case '/':
if (peekChar(parser) == '/')
if (matchChar(parser, '/'))
{
skipLineComment(parser);
break;
}
if (peekChar(parser) == '*')
if (matchChar(parser, '*'))
{
skipBlockComment(parser);
break;
@@ -819,30 +828,9 @@ static void nextToken(Parser* parser)
makeToken(parser, TOKEN_SLASH);
return;
case '-':
makeToken(parser, TOKEN_MINUS);
return;
case '|':
twoCharToken(parser, '|', TOKEN_PIPEPIPE, TOKEN_PIPE);
return;
case '&':
twoCharToken(parser, '&', TOKEN_AMPAMP, TOKEN_AMP);
return;
case '^':
makeToken(parser, TOKEN_CARET);
return;
case '=':
twoCharToken(parser, '=', TOKEN_EQEQ, TOKEN_EQ);
return;
case '<':
if (peekChar(parser) == '<')
if (matchChar(parser, '<'))
{
nextChar(parser);
makeToken(parser, TOKEN_LTLT);
}
else
@@ -852,9 +840,8 @@ static void nextToken(Parser* parser)
return;
case '>':
if (peekChar(parser) == '>')
if (matchChar(parser, '>'))
{
nextChar(parser);
makeToken(parser, TOKEN_GTGT);
}
else
@@ -863,10 +850,6 @@ static void nextToken(Parser* parser)
}
return;
case '!':
twoCharToken(parser, '=', TOKEN_BANGEQ, TOKEN_BANG);
return;
case '\n':
makeToken(parser, TOKEN_LINE);
return;
@@ -974,9 +957,7 @@ static bool matchLine(Compiler* compiler)
return true;
}
// Consumes the current token if its type is [expected]. Returns true if a
// token was consumed. Since [expected] is known to be in the middle of an
// expression, any newlines following it are consumed and discarded.
// Discards any newlines starting at the current token.
static void ignoreNewlines(Compiler* compiler)
{
matchLine(compiler);
@@ -1037,9 +1018,19 @@ static int emitJump(Compiler* compiler, Code instruction)
return emit(compiler, 0xff) - 1;
}
// Creates a new constant for the current value and emits the bytecode to load
// it from the constant table.
static void emitConstant(Compiler* compiler)
{
int constant = addConstant(compiler, compiler->parser->value);
// Compile the code to load the constant.
emitShortArg(compiler, CODE_CONSTANT, constant);
}
// Create a new local variable with [name]. Assumes the current scope is local
// and the name is unique.
static int defineLocal(Compiler* compiler, const char* name, int length)
static int addLocal(Compiler* compiler, const char* name, int length)
{
Local* local = &compiler->locals[compiler->numLocals];
local->name = name;
@@ -1105,7 +1096,7 @@ static int declareVariable(Compiler* compiler, Token* token)
return -1;
}
return defineLocal(compiler, token->start, token->length);
return addLocal(compiler, token->start, token->length);
}
// Parses a name token and declares a variable in the current scope with that
@@ -1595,7 +1586,7 @@ static void signatureToString(Signature* signature,
break;
case SIG_INITIALIZER:
memcpy(name, "this ", 5);
memcpy(name, "init ", 5);
memcpy(name + 5, signature->name, signature->length);
*length = 5 + signature->length;
signatureParameterList(name, length, signature->arity, '(', ')');
@@ -1671,8 +1662,7 @@ static void callSignature(Compiler* compiler, Code instruction,
// superclass in a constant. So, here, we create a slot in the constant
// table and store NULL in it. When the method is bound, we'll look up the
// superclass then and store it in the constant slot.
int constant = addConstant(compiler, NULL_VAL);
emitShort(compiler, constant);
emitShort(compiler, addConstant(compiler, NULL_VAL));
}
}
@@ -1938,6 +1928,10 @@ static void field(Compiler* compiler, bool allowAssignment)
{
error(compiler, "Cannot reference a field outside of a class definition.");
}
else if (enclosingClass->isForeign)
{
error(compiler, "Cannot define fields in a foreign class.");
}
else if (enclosingClass->inStatic)
{
error(compiler, "Cannot use an instance field in a static method.");
@@ -2128,32 +2122,10 @@ static void null(Compiler* compiler, bool allowAssignment)
emit(compiler, CODE_NULL);
}
static void number(Compiler* compiler, bool allowAssignment)
// A number or string literal.
static void literal(Compiler* compiler, bool allowAssignment)
{
int constant = addConstant(compiler, NUM_VAL(compiler->parser->number));
// Compile the code to load the constant.
emitShortArg(compiler, CODE_CONSTANT, constant);
}
// Parses a string literal and adds it to the constant table.
static int stringConstant(Compiler* compiler)
{
// Define a constant for the literal.
int constant = addConstant(compiler, wrenNewString(compiler->parser->vm,
(char*)compiler->parser->string.data, compiler->parser->string.count));
wrenByteBufferClear(compiler->parser->vm, &compiler->parser->string);
return constant;
}
static void string(Compiler* compiler, bool allowAssignment)
{
int constant = stringConstant(compiler);
// Compile the code to load the constant.
emitShortArg(compiler, CODE_CONSTANT, constant);
emitConstant(compiler);
}
static void super_(Compiler* compiler, bool allowAssignment)
@@ -2409,7 +2381,7 @@ void namedSignature(Compiler* compiler, Signature* signature)
// Compiles a method signature for a constructor.
void constructorSignature(Compiler* compiler, Signature* signature)
{
consume(compiler, TOKEN_NAME, "Expect constructor name after 'this'.");
consume(compiler, TOKEN_NAME, "Expect constructor name after 'construct'.");
// Capture the name.
*signature = signatureFromToken(compiler, SIG_INITIALIZER);
@@ -2500,8 +2472,8 @@ GrammarRule rules[] =
/* TOKEN_FIELD */ PREFIX(field),
/* TOKEN_STATIC_FIELD */ PREFIX(staticField),
/* TOKEN_NAME */ { name, NULL, namedSignature, PREC_NONE, NULL },
/* TOKEN_NUMBER */ PREFIX(number),
/* TOKEN_STRING */ PREFIX(string),
/* TOKEN_NUMBER */ PREFIX(literal),
/* TOKEN_STRING */ PREFIX(literal),
/* TOKEN_LINE */ UNUSED,
/* TOKEN_ERROR */ UNUSED,
/* TOKEN_EOF */ UNUSED
@@ -2591,6 +2563,8 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
case CODE_LOAD_LOCAL_7:
case CODE_LOAD_LOCAL_8:
case CODE_CONSTRUCT:
case CODE_FOREIGN_CONSTRUCT:
case CODE_FOREIGN_CLASS:
return 0;
case CODE_LOAD_LOCAL:
@@ -2773,11 +2747,11 @@ static void forStatement(Compiler* compiler)
// The space in the variable name ensures it won't collide with a user-defined
// variable.
expression(compiler);
int seqSlot = defineLocal(compiler, "seq ", 4);
int seqSlot = addLocal(compiler, "seq ", 4);
// Create another hidden local for the iterator object.
null(compiler, false);
int iterSlot = defineLocal(compiler, "iter ", 5);
int iterSlot = addLocal(compiler, "iter ", 5);
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after loop expression.");
@@ -2805,7 +2779,7 @@ static void forStatement(Compiler* compiler)
// Bind the loop variable in its own scope. This ensures we get a fresh
// variable each iteration so that closures for it don't all see the same one.
pushScope(compiler);
defineLocal(compiler, name, length);
addLocal(compiler, name, length);
loopBody(compiler);
@@ -2945,7 +2919,8 @@ static void createConstructor(Compiler* compiler, Signature* signature,
initCompiler(&methodCompiler, compiler->parser, compiler, false);
// Allocate the instance.
emit(&methodCompiler, CODE_CONSTRUCT);
emit(&methodCompiler, compiler->enclosingClass->isForeign
? CODE_FOREIGN_CONSTRUCT : CODE_CONSTRUCT);
// Run its initializer.
emitShortArg(&methodCompiler, (Code)(CODE_CALL_0 + signature->arity),
@@ -3027,9 +3002,9 @@ static bool method(Compiler* compiler, ClassCompiler* classCompiler,
if (isForeign)
{
// Define a constant for the signature.
int constant = addConstant(compiler, wrenNewString(compiler->parser->vm,
fullSignature, length));
emitShortArg(compiler, CODE_CONSTANT, constant);
compiler->parser->value = wrenNewString(compiler->parser->vm,
fullSignature, length);
emitConstant(compiler);
// We don't need the function we started compiling in the parameter list
// any more.
@@ -3039,7 +3014,6 @@ static bool method(Compiler* compiler, ClassCompiler* classCompiler,
{
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' to begin method body.");
finishBody(&methodCompiler, signature.type == SIG_INITIALIZER);
endCompiler(&methodCompiler, fullSignature, length);
}
@@ -3066,13 +3040,12 @@ static bool method(Compiler* compiler, ClassCompiler* classCompiler,
// Defines a default "new()" constructor on the current class.
//
// It just invokes "this new()" on the instance. If a base class defines that,
// It just invokes "init new()" on the instance. If a base class defines that,
// it will get invoked. Otherwise, it falls to the default one in Object which
// does nothing.
static void createDefaultConstructor(Compiler* compiler, int classSlot)
{
Signature signature = { "new", 3, SIG_INITIALIZER, 0 };
int initializerSymbol = signatureSymbol(compiler, &signature);
signature.type = SIG_METHOD;
@@ -3083,17 +3056,16 @@ static void createDefaultConstructor(Compiler* compiler, int classSlot)
}
// Compiles a class definition. Assumes the "class" token has already been
// consumed.
static void classDefinition(Compiler* compiler)
// consumed (along with a possibly preceding "foreign" token).
static void classDefinition(Compiler* compiler, bool isForeign)
{
// Create a variable to store the class in.
int slot = declareNamedVariable(compiler);
// Make a string constant for the name.
int nameConstant = addConstant(compiler, wrenNewString(compiler->parser->vm,
compiler->parser->previous.start, compiler->parser->previous.length));
emitShortArg(compiler, CODE_CONSTANT, nameConstant);
compiler->parser->value = wrenNewString(compiler->parser->vm,
compiler->parser->previous.start, compiler->parser->previous.length);
emitConstant(compiler);
// Load the superclass (if there is one).
if (match(compiler, TOKEN_IS))
@@ -3109,7 +3081,15 @@ static void classDefinition(Compiler* compiler)
// Store a placeholder for the number of fields argument. We don't know
// the value until we've compiled all the methods to see which fields are
// used.
int numFieldsInstruction = emitByteArg(compiler, CODE_CLASS, 255);
int numFieldsInstruction = -1;
if (isForeign)
{
emit(compiler, CODE_FOREIGN_CLASS);
}
else
{
numFieldsInstruction = emitByteArg(compiler, CODE_CLASS, 255);
}
// Store it in its name.
defineVariable(compiler, slot);
@@ -3120,6 +3100,7 @@ static void classDefinition(Compiler* compiler)
pushScope(compiler);
ClassCompiler classCompiler;
classCompiler.isForeign = isForeign;
// Set up a symbol table for the class's fields. We'll initially compile
// them to slots starting at zero. When the method is bound to the class, the
@@ -3155,7 +3136,11 @@ static void classDefinition(Compiler* compiler)
}
// Update the class with the number of fields.
compiler->bytecode.data[numFieldsInstruction] = (uint8_t)fields.count;
if (!isForeign)
{
compiler->bytecode.data[numFieldsInstruction] = (uint8_t)fields.count;
}
wrenSymbolTableClear(compiler->parser->vm, &fields);
compiler->enclosingClass = NULL;
@@ -3163,10 +3148,11 @@ static void classDefinition(Compiler* compiler)
popScope(compiler);
}
// Compiles an "import" statement.
static void import(Compiler* compiler)
{
consume(compiler, TOKEN_STRING, "Expect a string after 'import'.");
int moduleConstant = stringConstant(compiler);
int moduleConstant = addConstant(compiler, compiler->parser->value);
// Load the module.
emitShortArg(compiler, CODE_LOAD_MODULE, moduleConstant);
@@ -3197,6 +3183,7 @@ static void import(Compiler* compiler)
} while (match(compiler, TOKEN_COMMA));
}
// Compiles a "var" variable definition statement.
static void variableDefinition(Compiler* compiler)
{
// Grab its name, but don't declare it yet. A (local) variable shouldn't be
@@ -3227,23 +3214,25 @@ void definition(Compiler* compiler)
{
if (match(compiler, TOKEN_CLASS))
{
classDefinition(compiler);
return;
classDefinition(compiler, false);
}
if (match(compiler, TOKEN_IMPORT))
else if (match(compiler, TOKEN_FOREIGN))
{
consume(compiler, TOKEN_CLASS, "Expect 'class' after 'foreign'.");
classDefinition(compiler, true);
}
else if (match(compiler, TOKEN_IMPORT))
{
import(compiler);
return;
}
if (match(compiler, TOKEN_VAR))
else if (match(compiler, TOKEN_VAR))
{
variableDefinition(compiler);
return;
}
block(compiler);
else
{
block(compiler);
}
}
ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* sourcePath,
@@ -3257,6 +3246,7 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* sourcePath,
parser.module = module;
parser.sourcePath = AS_STRING(sourcePathValue);
parser.source = source;
parser.value = UNDEFINED_VAL;
parser.tokenStart = source;
parser.currentChar = source;
@@ -3274,8 +3264,6 @@ ObjFn* wrenCompile(WrenVM* vm, ObjModule* module, const char* sourcePath,
parser.printErrors = printErrors;
parser.hasError = false;
wrenByteBufferInit(&parser.string);
// Read the first token.
nextToken(&parser);
@@ -3384,6 +3372,7 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
void wrenMarkCompiler(WrenVM* vm, Compiler* compiler)
{
wrenMarkObj(vm, (Obj*)compiler->parser->sourcePath);
wrenMarkValue(vm, compiler->parser->value);
// Walk up the parent chain to mark the outer compilers too. The VM only
// tracks the innermost one.
+1 -1
View File
@@ -1291,7 +1291,7 @@ void wrenInitializeCore(WrenVM* vm)
PRIMITIVE(vm->objectClass, "!", object_not);
PRIMITIVE(vm->objectClass, "==(_)", object_eqeq);
PRIMITIVE(vm->objectClass, "!=(_)", object_bangeq);
PRIMITIVE(vm->objectClass, "this new()", return_this);
PRIMITIVE(vm->objectClass, "init new()", return_this);
PRIMITIVE(vm->objectClass, "is(_)", object_is);
PRIMITIVE(vm->objectClass, "toString", object_toString);
PRIMITIVE(vm->objectClass, "type", object_type);
+8 -2
View File
@@ -32,10 +32,13 @@ static void dumpObject(Obj* obj)
{
switch (obj->type)
{
case OBJ_CLASS: printf("[class %p]", obj); break;
case OBJ_CLASS:
printf("[class %s %p]", ((ObjClass*)obj)->name->value, obj);
break;
case OBJ_CLOSURE: printf("[closure %p]", obj); break;
case OBJ_FIBER: printf("[fiber %p]", obj); break;
case OBJ_FN: printf("[fn %p]", obj); break;
case OBJ_FOREIGN: printf("[foreign %p]", obj); break;
case OBJ_INSTANCE: printf("[instance %p]", obj); break;
case OBJ_LIST: printf("[list %p]", obj); break;
case OBJ_MAP: printf("[map %p]", obj); break;
@@ -269,7 +272,8 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
break;
}
case CODE_CONSTRUCT: printf("CODE_CONSTRUCT\n"); break;
case CODE_CONSTRUCT: printf("CODE_CONSTRUCT\n"); break;
case CODE_FOREIGN_CONSTRUCT: printf("CODE_FOREIGN_CONSTRUCT\n"); break;
case CODE_CLASS:
{
@@ -278,6 +282,8 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
break;
}
case CODE_FOREIGN_CLASS: printf("FOREIGN_CLASS\n"); break;
case CODE_METHOD_INSTANCE:
{
int symbol = READ_SHORT();
+11
View File
@@ -157,11 +157,22 @@ OPCODE(CLOSURE)
// compiler-generated constructor metaclass methods.
OPCODE(CONSTRUCT)
// Creates a new instance of a foreign class.
//
// Assumes the class object is in slot zero, and replaces it with the new
// uninitialized instance of that class. This opcode is only emitted by the
// compiler-generated constructor metaclass methods.
OPCODE(FOREIGN_CONSTRUCT)
// Creates a class. Top of stack is the superclass, or `null` if the class
// inherits Object. Below that is a string for the name of the class. Byte
// [arg] is the number of fields in the class.
OPCODE(CLASS)
// Creates a foreign class. Top of stack is the superclass, or `null` if the
// class inherits Object. Below that is a string for the name of the class.
OPCODE(FOREIGN_CLASS)
// Define a method for symbol [arg]. The class receiving the method is popped
// off the stack, then the function defining the body is popped.
//
+2 -2
View File
@@ -6,8 +6,8 @@
// Reusable data structures and other utility functions.
// A simple structure to keep trace of the string length as long as its data
// (including the null-terminator)
// A simple structure to keep track of a string and its length (including the
// null-terminator).
typedef struct {
char* buffer;
uint32_t length;
+33 -1
View File
@@ -65,7 +65,15 @@ void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass)
subclass->superclass = superclass;
// Include the superclass in the total number of fields.
subclass->numFields += superclass->numFields;
if (subclass->numFields != -1)
{
subclass->numFields += superclass->numFields;
}
else
{
ASSERT(superclass->numFields == 0,
"A foreign class cannot inherit from a class with fields.");
}
// Inherit methods from its superclass.
for (int i = 0; i < superclass->methods.count; i++)
@@ -166,6 +174,16 @@ void wrenResetFiber(WrenVM* vm, ObjFiber* fiber, Obj* fn)
wrenAppendCallFrame(vm, fiber, fn, fiber->stack);
}
ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size)
{
ObjForeign* object = ALLOCATE_FLEX(vm, ObjForeign, uint8_t, size);
initObj(vm, &object->obj, OBJ_FOREIGN, classObj);
// Zero out the bytes.
memset(object->data, 0, size);
return object;
}
ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module,
const Value* constants, int numConstants,
int numUpvalues, int arity,
@@ -903,6 +921,15 @@ static void markFn(WrenVM* vm, ObjFn* fn)
// TODO: What about the function name?
}
static void markForeign(WrenVM* vm, ObjForeign* foreign)
{
// TODO: Keep track of how much memory the foreign object uses. We can store
// this in each foreign object, but it will balloon the size. We may not want
// that much overhead. One option would be to let the foreign class register
// a C function that returns a size for the object. That way the VM doesn't
// always have to explicitly store it.
}
static void markInstance(WrenVM* vm, ObjInstance* instance)
{
wrenMarkObj(vm, (Obj*)instance->obj.classObj);
@@ -1007,6 +1034,7 @@ void wrenMarkObj(WrenVM* vm, Obj* obj)
case OBJ_CLOSURE: markClosure( vm, (ObjClosure*) obj); break;
case OBJ_FIBER: markFiber( vm, (ObjFiber*) obj); break;
case OBJ_FN: markFn( vm, (ObjFn*) obj); break;
case OBJ_FOREIGN: markForeign( vm, (ObjForeign*) obj); break;
case OBJ_INSTANCE: markInstance(vm, (ObjInstance*)obj); break;
case OBJ_LIST: markList( vm, (ObjList*) obj); break;
case OBJ_MAP: markMap( vm, (ObjMap*) obj); break;
@@ -1059,6 +1087,10 @@ void wrenFreeObj(WrenVM* vm, Obj* obj)
DEALLOCATE(vm, fn->debug);
break;
}
case OBJ_FOREIGN:
// TODO: Call finalizer.
break;
case OBJ_LIST:
wrenValueBufferClear(vm, &((ObjList*)obj)->elements);
+11
View File
@@ -51,6 +51,7 @@
#define AS_CLOSURE(value) ((ObjClosure*)AS_OBJ(value)) // ObjClosure*
#define AS_FIBER(v) ((ObjFiber*)AS_OBJ(v)) // ObjFiber*
#define AS_FN(value) ((ObjFn*)AS_OBJ(value)) // ObjFn*
#define AS_FOREIGN(v) ((ObjForeign*)AS_OBJ(v)) // ObjForeign*
#define AS_INSTANCE(value) ((ObjInstance*)AS_OBJ(value)) // ObjInstance*
#define AS_LIST(value) ((ObjList*)AS_OBJ(value)) // ObjList*
#define AS_MAP(value) ((ObjMap*)AS_OBJ(value)) // ObjMap*
@@ -74,6 +75,7 @@
#define IS_CLOSURE(value) (wrenIsObjType(value, OBJ_CLOSURE)) // ObjClosure
#define IS_FIBER(value) (wrenIsObjType(value, OBJ_FIBER)) // ObjFiber
#define IS_FN(value) (wrenIsObjType(value, OBJ_FN)) // ObjFn
#define IS_FOREIGN(value) (wrenIsObjType(value, OBJ_FOREIGN)) // ObjForeign
#define IS_INSTANCE(value) (wrenIsObjType(value, OBJ_INSTANCE)) // ObjInstance
#define IS_RANGE(value) (wrenIsObjType(value, OBJ_RANGE)) // ObjRange
#define IS_STRING(value) (wrenIsObjType(value, OBJ_STRING)) // ObjString
@@ -89,6 +91,7 @@ typedef enum {
OBJ_CLOSURE,
OBJ_FIBER,
OBJ_FN,
OBJ_FOREIGN,
OBJ_INSTANCE,
OBJ_LIST,
OBJ_MAP,
@@ -397,6 +400,12 @@ struct sObjClass
ObjString* name;
};
typedef struct
{
Obj obj;
uint8_t data[FLEXIBLE_ARRAY];
} ObjForeign;
typedef struct
{
Obj obj;
@@ -652,6 +661,8 @@ static inline void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber,
frame->ip = wrenGetFrameFunction(frame)->bytecode;
}
ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size);
// TODO: The argument list here is getting a bit gratuitous.
// Creates a new function object with the given code and constants. The new
// function will take over ownership of [bytecode] and [sourceLines]. It will
+194 -72
View File
@@ -37,6 +37,17 @@ static void* defaultReallocate(void* ptr, size_t newSize)
return realloc(ptr, newSize);
}
void wrenInitConfiguration(WrenConfiguration* configuration)
{
configuration->reallocateFn = NULL;
configuration->loadModuleFn = NULL;
configuration->bindForeignMethodFn = NULL;
configuration->bindForeignClassFn = NULL;
configuration->initialHeapSize = 1024 * 1024 * 10;
configuration->minHeapSize = 1024 * 1024;
configuration->heapGrowthPercent = 50;
}
WrenVM* wrenNewVM(WrenConfiguration* configuration)
{
WrenReallocateFn reallocate = defaultReallocate;
@@ -49,32 +60,19 @@ WrenVM* wrenNewVM(WrenConfiguration* configuration)
memset(vm, 0, sizeof(WrenVM));
vm->reallocate = reallocate;
vm->bindForeign = configuration->bindForeignMethodFn;
vm->bindForeignMethod = configuration->bindForeignMethodFn;
vm->bindForeignClass = configuration->bindForeignClassFn;
vm->loadModule = configuration->loadModuleFn;
vm->nextGC = configuration->initialHeapSize;
vm->minNextGC = configuration->minHeapSize;
// +100 here because the configuration gives us the *additional* size of
// the heap relative to the in-use memory, while heapScalePercent is the
// *total* size of the heap relative to in-use.
vm->heapScalePercent = 100 + configuration->heapGrowthPercent;
wrenSymbolTableInit(&vm->methodNames);
vm->nextGC = 1024 * 1024 * 10;
if (configuration->initialHeapSize != 0)
{
vm->nextGC = configuration->initialHeapSize;
}
vm->minNextGC = 1024 * 1024;
if (configuration->minHeapSize != 0)
{
vm->minNextGC = configuration->minHeapSize;
}
vm->heapScalePercent = 150;
if (configuration->heapGrowthPercent != 0)
{
// +100 here because the configuration gives us the *additional* size of
// the heap relative to the in-use memory, while heapScalePercent is the
// *total* size of the heap relative to in-use.
vm->heapScalePercent = 100 + configuration->heapGrowthPercent;
}
ObjString* name = AS_STRING(CONST_STRING(vm, "core"));
wrenPushRoot(vm, (Obj*)name);
@@ -311,24 +309,21 @@ static WrenForeignMethodFn findForeignMethod(WrenVM* vm,
{
WrenForeignMethodFn fn;
// Let the host try to find it first.
if (vm->bindForeign != NULL)
{
fn = vm->bindForeign(vm, moduleName, className, isStatic, signature);
if (fn != NULL) return fn;
}
// Otherwise, try the built-in libraries.
// Bind foreign methods in the core module.
if (strcmp(moduleName, "core") == 0)
{
#if WREN_USE_LIB_IO
fn = wrenBindIOForeignMethod(vm, className, signature);
if (fn != NULL) return fn;
#endif
ASSERT(fn != NULL, "Failed to bind core module foreign method.");
return fn;
}
// TODO: Report a runtime error on failure to find it.
return NULL;
// For other modules, let the host bind it.
if (vm->bindForeignMethod == NULL) return NULL;
return vm->bindForeignMethod(vm, moduleName, className, isStatic, signature);
}
// Defines [methodValue] as a method on [classObj].
@@ -546,18 +541,23 @@ static bool importVariable(WrenVM* vm, Value moduleName, Value variableName,
return false;
}
// Verifies that [superclass] is a valid object to inherit from. That means it
// must be a class and cannot be the class of any built-in type.
// Verifies that [superclassValue] is a valid object to inherit from. That
// means it must be a class and cannot be the class of any built-in type.
//
// If successful, returns null. Otherwise, returns a string for the runtime
// Also validates that it doesn't result in a class with too many fields and
// the other limitations foreign classes have.
//
// If successful, returns `null`. Otherwise, returns a string for the runtime
// error message.
static Value validateSuperclass(WrenVM* vm, Value name,
Value superclassValue)
static Value validateSuperclass(WrenVM* vm, Value name, Value superclassValue,
int numFields)
{
// Make sure the superclass is a class.
if (!IS_CLASS(superclassValue))
{
return CONST_STRING(vm, "Must inherit from a class.");
return wrenStringFormat(vm,
"Class '@' cannot inherit from a non-class object.",
name);
}
// Make sure it doesn't inherit from a sealed built-in type. Primitive methods
@@ -572,13 +572,125 @@ static Value validateSuperclass(WrenVM* vm, Value name,
superclass == vm->rangeClass ||
superclass == vm->stringClass)
{
return wrenStringFormat(vm, "@ cannot inherit from @.",
return wrenStringFormat(vm,
"Class '@' cannot inherit from built-in class '@'.",
name, OBJ_VAL(superclass->name));
}
if (superclass->numFields == -1)
{
return wrenStringFormat(vm,
"Class '@' cannot inherit from foreign class '@'.",
name, OBJ_VAL(superclass->name));
}
if (numFields == -1 && superclass->numFields > 0)
{
return wrenStringFormat(vm,
"Foreign class '@' may not inherit from a class with fields.",
name);
}
if (superclass->numFields + numFields > MAX_FIELDS)
{
return wrenStringFormat(vm,
"Class '@' may not have more than 255 fields, including inherited "
"ones.", name);
}
return NULL_VAL;
}
static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
{
// TODO: Make this a runtime error?
ASSERT(vm->bindForeignClass != NULL,
"Cannot declare foreign classes without a bindForeignClassFn.");
WrenForeignClassMethods methods = vm->bindForeignClass(
vm, module->name->value, classObj->name->value);
Method method;
method.type = METHOD_FOREIGN;
method.fn.foreign = methods.allocate;
ASSERT(method.fn.foreign != NULL,
"A foreign class must provide an allocate function.");
int symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, "<allocate>", 10);
wrenBindMethod(vm, classObj, symbol, method);
if (methods.finalize != NULL)
{
method.fn.foreign = methods.finalize;
symbol = wrenSymbolTableEnsure(vm, &vm->methodNames, "<finalize>", 10);
wrenBindMethod(vm, classObj, symbol, method);
}
}
// Creates a new class.
//
// If [numFields] is -1, the class is a foreign class. The name and superclass
// should be on top of the fiber's stack. After calling this, the top of the
// stack will contain either the new class or a string if a runtime error
// occurred.
//
// Returns false if the result is an error.
static bool defineClass(WrenVM* vm, ObjFiber* fiber, int numFields,
ObjModule* module)
{
// Pull the name and superclass off the stack.
Value name = fiber->stackTop[-2];
Value superclassValue = fiber->stackTop[-1];
// We have two values on the stack and we are going to leave one, so discard
// the other slot.
fiber->stackTop--;
// Use implicit Object superclass if none given.
ObjClass* superclass = vm->objectClass;
if (!IS_NULL(superclassValue))
{
Value error = validateSuperclass(vm, name, superclassValue, numFields);
if (!IS_NULL(error))
{
fiber->stackTop[-1] = error;
return false;
}
superclass = AS_CLASS(superclassValue);
}
ObjClass* classObj = wrenNewClass(vm, superclass, numFields, AS_STRING(name));
fiber->stackTop[-1] = OBJ_VAL(classObj);
if (numFields == -1) bindForeignClass(vm, classObj, module);
return true;
}
static void createForeign(WrenVM* vm, ObjFiber* fiber, Value* stack)
{
ObjClass* classObj = AS_CLASS(stack[0]);
ASSERT(classObj->numFields == -1, "Class must be a foreign class.");
// TODO: Don't look up every time.
int symbol = wrenSymbolTableFind(&vm->methodNames, "<allocate>", 10);
ASSERT(symbol != -1, "Should have defined <allocate> symbol.");
ASSERT(classObj->methods.count > symbol, "Class should have allocator.");
Method* method = &classObj->methods.data[symbol];
ASSERT(method->type == METHOD_FOREIGN, "Allocator should be foreign.");
// Pass the constructor arguments to the allocator as well.
vm->foreignCallSlot = stack;
vm->foreignCallNumArgs = (int)(fiber->stackTop - stack);
method->fn.foreign(vm);
// TODO: Check that allocateForeign was called.
}
// The main bytecode interpreter loop. This is where the magic happens. It is
// also, as you can imagine, highly performance critical. Returns `true` if the
// fiber completed without error.
@@ -1022,6 +1134,11 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
stackStart[0] = wrenNewInstance(vm, AS_CLASS(stackStart[0]));
DISPATCH();
CASE_CODE(FOREIGN_CONSTRUCT):
ASSERT(IS_CLASS(stackStart[0]), "'this' should be a class.");
createForeign(vm, fiber, stackStart);
DISPATCH();
CASE_CODE(CLOSURE):
{
ObjFn* prototype = AS_FN(fn->constants[READ_SHORT()]);
@@ -1057,40 +1174,16 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
CASE_CODE(CLASS):
{
Value name = PEEK2();
ObjClass* superclass = vm->objectClass;
// Use implicit Object superclass if none given.
if (!IS_NULL(PEEK()))
{
Value error = validateSuperclass(vm, name, PEEK());
if (!IS_NULL(error)) RUNTIME_ERROR(error);
superclass = AS_CLASS(PEEK());
}
int numFields = READ_BYTE();
Value classObj = OBJ_VAL(wrenNewClass(vm, superclass, numFields,
AS_STRING(name)));
// Don't pop the superclass and name off the stack until the subclass is
// done being created, to make sure it doesn't get collected.
DROP();
DROP();
// Now that we know the total number of fields, make sure we don't
// overflow.
if (superclass->numFields + numFields > MAX_FIELDS)
{
RUNTIME_ERROR(wrenStringFormat(vm,
"Class '@' may not have more than 255 fields, including inherited "
"ones.", name));
}
PUSH(classObj);
if (!defineClass(vm, fiber, READ_BYTE(), NULL)) RUNTIME_ERROR(PEEK());
DISPATCH();
}
CASE_CODE(FOREIGN_CLASS):
{
if (!defineClass(vm, fiber, -1, fn->module)) RUNTIME_ERROR(PEEK());
DISPATCH();
}
CASE_CODE(METHOD_INSTANCE):
CASE_CODE(METHOD_STATIC):
{
@@ -1323,6 +1416,20 @@ void wrenReleaseValue(WrenVM* vm, WrenValue* value)
DEALLOCATE(vm, value);
}
void* wrenAllocateForeign(WrenVM* vm, size_t size)
{
ASSERT(vm->foreignCallSlot != NULL, "Must be in foreign call.");
// TODO: Validate this. It can fail if the user calls this inside another
// foreign method, or calls one of the return functions.
ObjClass* classObj = AS_CLASS(vm->foreignCallSlot[0]);
ObjForeign* foreign = wrenNewForeign(vm, classObj, size);
vm->foreignCallSlot[0] = OBJ_VAL(foreign);
return (void*)foreign->data;
}
// Execute [source] in the context of the core module.
static WrenInterpretResult loadIntoCore(WrenVM* vm, const char* source)
{
@@ -1460,6 +1567,12 @@ void wrenPopRoot(WrenVM* vm)
vm->numTempRoots--;
}
int wrenGetArgumentCount(WrenVM* vm)
{
ASSERT(vm->foreignCallSlot != NULL, "Must be in foreign call.");
return vm->foreignCallNumArgs;
}
static void validateForeignArgument(WrenVM* vm, int index)
{
ASSERT(vm->foreignCallSlot != NULL, "Must be in foreign call.");
@@ -1485,6 +1598,15 @@ double wrenGetArgumentDouble(WrenVM* vm, int index)
return AS_NUM(*(vm->foreignCallSlot + index));
}
void* wrenGetArgumentForeign(WrenVM* vm, int index)
{
validateForeignArgument(vm, index);
if (!IS_FOREIGN(*(vm->foreignCallSlot + index))) return NULL;
return AS_FOREIGN(*(vm->foreignCallSlot + index))->data;
}
const char* wrenGetArgumentString(WrenVM* vm, int index)
{
validateForeignArgument(vm, index);
+4 -1
View File
@@ -125,7 +125,10 @@ struct WrenVM
int foreignCallNumArgs;
// The function used to locate foreign functions.
WrenBindForeignMethodFn bindForeign;
WrenBindForeignMethodFn bindForeignMethod;
// The function used to locate foreign classes.
WrenBindForeignClassFn bindForeignClass;
// The function used to load modules.
WrenLoadModuleFn loadModule;