diff -r 03a663e96860 -r 9308d52205a0 src/BytecodeCompiler.cpp --- a/src/BytecodeCompiler.cpp Sat Sep 15 19:33:24 2012 -0700 +++ b/src/BytecodeCompiler.cpp Sun Sep 16 19:02:23 2012 -0700 @@ -739,20 +739,25 @@ __ float_((float)node->value()); } +static inline Array * +StringLiteralToArray(Zone *zone, StringLiteral *node) +{ + Local type(zone, node->type()); + Local map(zone, ArrayMap::Attach(zone, type)); + if (!map) + return NULL; + + return Array::NewString(zone, + map, + node->string()->raw(), + node->string()->length(), + Heap::Tenure_Old); +} + void BytecodeCompiler::visit(StringLiteral *node) { - Local type(zone_, node->type()); - Local map(zone_, ArrayMap::Attach(zone_, type)); - if (!map) - return; - - Local array(zone_, - Array::NewString(zone_, - map, - node->string()->raw(), - node->string()->length(), - Heap::Tenure_Old)); + Local array(zone_, StringLiteralToArray(zone_, node)); if (!array) return; @@ -774,6 +779,106 @@ __ setglobal(var); } +static bool +FillArrayLiteral_String(Zone *zone, Handle array, ArrayLiteral *literal, unsigned *maxsize) +{ + Local string(zone); + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + StringLiteral *lit = expr->toStringLiteral(); + if ((string = StringLiteralToArray(zone, lit)) == NULL) + return false; + + array->writeObject(zone, i, string); + + if (string->length() > *maxsize) + *maxsize = string->length(); + + assert(!array->type()->contained()->isFixedLength() || + array->type()->contained()->fixedLength() >= string->length()); + } + + return true; +} + +static bool +FillArrayLiteral_Primitive(Zone *zone, Handle array, ArrayLiteral *literal) +{ + assert(array->type()->fixedLength() == literal->expressions()->length()); + + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + if (IntegerLiteral *lit = expr->asIntegerLiteral()) { + array->writeInt32(i, lit->value()); + continue; + } + if (BooleanLiteral *lit = expr->asBooleanLiteral()) { + if (lit->token() == TOK_TRUE) + array->writeInt32(i, 1); + else + array->writeInt32(i, 0); + continue; + } + if (FloatLiteral *lit = expr->asFloatLiteral()) { + array->writeFloat(i, lit->value()); + continue; + } + + // Unrecognized literal type. + assert(false); + } + + return true; +} + +static bool +TraverseArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle array, Handle type, + unsigned *maxsize) +{ + assert(type->fixedLength() == literal->expressions()->length()); + + Local contained(zone, type->contained()); + if (contained->isCharArray()) + return FillArrayLiteral_String(zone, array, literal, maxsize); + + if (!contained->isArray()) + return FillArrayLiteral_Primitive(zone, array, literal); + + Local subarray(zone); + for (size_t i = 0; i < literal->expressions()->length(); i++) { + Expression *expr = literal->expressions()->at(i); + subarray = Array::cast(array->readObject(i)); + if (!TraverseArrayLiteral(zone, expr->toArrayLiteral(), subarray, contained, maxsize)) + return false; + } + + return true; +} + +static Array * +BuildArrayLiteral(Zone *zone, ArrayLiteral *literal, Handle map, unsigned *maxsize) +{ + Local root(zone, Array::NewFixed(zone, map, Heap::Tenure_Old)); + if (!root) + return NULL; + + // This should have allocated everything in the array, so all we need to + // do is traverse down to the lowest levels and copy things. + Local type(zone, map->type()); + if (!TraverseArrayLiteral(zone, literal, root, type, maxsize)) + return NULL; + + return root; +} + +static inline bool +ArrayContainsCharBuffers(Type *type) +{ + if (type->contained()->isArray()) + return ArrayContainsCharBuffers(type->contained()); + return type->isCharArray(); +} + void BytecodeCompiler::visit(VariableDeclaration *node) { @@ -784,21 +889,91 @@ Local map(zone_, ArrayMap::Attach(zone_, type)); __ note_position(node->pos()); - Opcode op; - if (type->isFixedLength()) - op = OP_NEWFIXED; - else - op = OP_NEWEMPTY; + Local right(zone_); + if (node->initialization()) + right = node->initialization()->type(); + + // There are a few methods for allocating, here: + // (1) If the array is fixed, + // (a) Initializer not present, + // NEWFIXED + // (b) Left is const non-string: + // + // (b) Otherwise: + // NEWFIXED + // COPYARRAY + // (2) If the array is not fixed, + // (a) Initializer is not present, + // NEWEMPTY + // (b) Initializer is not a string literal, + // or left is const: + // + // (c) Initializer is a string literal, + // NEWSIZED + // COPYARRAY + // + + Heap::Tenure tenure = var->scope()->kind() == Scope::GLOBAL + ? Heap::Tenure_Old + : Heap::Tenure_Default; + + if (Expression *init = node->initialization()) { + if (init->isArrayLiteral()) { + unsigned maxsize = 0; + Local array(zone_, BuildArrayLiteral(zone_, init->toArrayLiteral(), map, &maxsize)); + if (!array) + return; + + assert(type->isFixedLength()); - if (var->scope()->kind() == Scope::GLOBAL) - __ newarray(op, map, Heap::Tenure_Old); - else - __ newarray(op, map, Heap::Tenure_Default); - - if (node->initialization()) { - node->initialization()->accept(this); - __ note_position(node->pos()); - __ copyarray(); + if (type->isConst()) { + __ object(array); + } else if (ArrayContainsCharBuffers(type)) { + // This array contains mutable char buffers, so + // unfortunately this gets a little more complicated. We + // compute the maximum size needed for the innermost + // string, which is similar to SP1. This means we can use + // CopyArray rather than individually emit opcodes to clone + // every string. + Local child(zone_, type); + for (size_t i = 0; i < type->levels() - 1; i++) { + __ int_(child->fixedLength()); + child = child->contained(); + } + __ int_(maxsize); + __ newarray(OP_NEWSIZED, map, tenure); + __ object(array); + __ copyarray(); + } else { + __ newarray(OP_NEWFIXED, map, tenure); + __ object(array); + __ copyarray(); + } + } else if (type->isFixedLength()) { + // Note in the case of: + // const String:buffer[50] = "hello"; + // + // We want to preserve sizeof(buffer) == 50. + if (type->isConst() && !type->isCharArray()) { + init->accept(this); + } else { + __ newarray(OP_NEWFIXED, map, tenure); + init->accept(this); + __ copyarray(); + } + } else if (!type->isConst() && init->isStringLiteral()) { + // This is a degenerate case of ArrayLiteral. + __ int_(right->fixedLength()); + __ newarray(OP_NEWSIZED, map, tenure); + init->accept(this); + __ copyarray(); + } else if (type->isConst()) { + init->accept(this); + } + } else if (type->isFixedLength()) { + __ newarray(OP_NEWFIXED, map, tenure); + } else { + __ newarray(OP_NEWEMPTY, map, tenure); } } else if (type->isStruct()) { Local map(zone_, type->newMap()); @@ -1157,6 +1332,14 @@ { } +void +BytecodeCompiler::visit(ArrayLiteral *node) +{ + // We shouldn't reach these; they're handled manually when initializing + // declared arrays. + assert(false); +} + Code * BytecodeCompiler::compileCode(Scope *scope, unsigned lineno) {