feat: add MAX_FIELDS constant and runtime overflow checks for class field limit of 255
Add a MAX_FIELDS constant (255) to wren_common.h and implement field overflow validation in the compiler and VM. The compiler now emits an error when a class declares more than 255 fields, and the VM checks for inherited field overflow at class creation time, producing a descriptive runtime error. Also add four test files (many_fields, many_inherited_fields, too_many_fields, too_many_inherited_fields) to verify both valid and invalid field counts.
This commit is contained in:
@@ -63,6 +63,13 @@
|
||||
// extra spaces added to handle arity, and another byte to terminate the string.
|
||||
#define MAX_METHOD_SIGNATURE (MAX_METHOD_NAME + MAX_PARAMETERS + 1)
|
||||
|
||||
// The maximum number of fields a class can have, including inherited fields.
|
||||
// This is explicit in the bytecode since `CODE_CLASS` and `CODE_SUBCLASS` take
|
||||
// a single byte for the number of fields. Note that it's 255 and not 256
|
||||
// because creating a class takes the *number* of fields, not the *highest
|
||||
// field index*.
|
||||
#define MAX_FIELDS (255)
|
||||
|
||||
// Assertions are used to validate program invariants. They indicate things the
|
||||
// program expects to be true about its internal state during execution. If an
|
||||
// assertion fails, there is a bug in Wren.
|
||||
|
||||
+8
-6
@@ -1504,6 +1504,11 @@ static void field(Compiler* compiler, bool allowAssignment)
|
||||
field = wrenSymbolTableEnsure(compiler->parser->vm, compiler->fields,
|
||||
compiler->parser->previous.start,
|
||||
compiler->parser->previous.length);
|
||||
|
||||
if (field >= MAX_FIELDS)
|
||||
{
|
||||
error(compiler, "A class can only have %d fields.", MAX_FIELDS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1513,8 +1518,6 @@ static void field(Compiler* compiler, bool allowAssignment)
|
||||
field = 255;
|
||||
}
|
||||
|
||||
// TODO: Make sure field number fits in byte.
|
||||
|
||||
// If there's an "=" after a field name, it's an assignment.
|
||||
if (match(compiler, TOKEN_EQ))
|
||||
{
|
||||
@@ -2350,7 +2353,6 @@ void statement(Compiler* compiler)
|
||||
static void classDefinition(Compiler* compiler)
|
||||
{
|
||||
// Create a variable to store the class in.
|
||||
// TODO: Allow anonymous classes?
|
||||
int symbol = declareVariable(compiler);
|
||||
|
||||
// Load the superclass (if there is one).
|
||||
@@ -2522,10 +2524,10 @@ void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn)
|
||||
case CODE_STORE_FIELD:
|
||||
case CODE_LOAD_FIELD_THIS:
|
||||
case CODE_STORE_FIELD_THIS:
|
||||
// Shift this class's fields down past the inherited ones.
|
||||
// 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->bytecode[ip++] += classObj->superclass->numFields;
|
||||
|
||||
// TODO: Make sure field number still fits in byte.
|
||||
break;
|
||||
|
||||
case CODE_CLOSURE:
|
||||
|
||||
@@ -530,6 +530,22 @@ static void methodNotFound(WrenVM* vm, ObjFiber* fiber, Value* receiver,
|
||||
wrenDebugPrintStackTrace(vm, fiber, *receiver);
|
||||
}
|
||||
|
||||
static void tooManyInheritedFields(WrenVM* vm, ObjFiber* fiber, Value* slot)
|
||||
{
|
||||
// TODO: Tune size.
|
||||
char message[200];
|
||||
|
||||
// TODO: Include class name in message. Mention inheritance.
|
||||
snprintf(message, 200,
|
||||
"A class may not have more than %d fields, including inherited ones.",
|
||||
MAX_FIELDS);
|
||||
|
||||
// Store the error message in the receiver slot so that it's on the fiber's
|
||||
// stack and doesn't get garbage collected.
|
||||
*slot = wrenNewString(vm, message, strlen(message));
|
||||
wrenDebugPrintStackTrace(vm, fiber, *slot);
|
||||
}
|
||||
|
||||
// Pushes [function] onto [fiber]'s callstack and invokes it. Expects [numArgs]
|
||||
// arguments (including the receiver) to be on the top of the stack already.
|
||||
// [function] can be an `ObjFn` or `ObjClosure`.
|
||||
@@ -1181,6 +1197,16 @@ static bool runInterpreter(WrenVM* vm)
|
||||
|
||||
ObjClass* classObj = wrenNewClass(vm, superclass, numFields);
|
||||
|
||||
// Now that we know the total number of fields, make sure we don't
|
||||
// overflow.
|
||||
// TODO: Same check for static fields.
|
||||
if (superclass->numFields + numFields > MAX_FIELDS)
|
||||
{
|
||||
STORE_FRAME();
|
||||
tooManyInheritedFields(vm, fiber, &fiber->stack[fiber->stackSize - 1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't pop the superclass off the stack until the subclass is done
|
||||
// being created, to make sure it doesn't get collected.
|
||||
if (isSubclass) POP();
|
||||
|
||||
Reference in New Issue
Block a user