diff --git a/Compiler/script2/cc_symboltable.cpp b/Compiler/script2/cc_symboltable.cpp index b95e9b7e23..bfac98a775 100644 --- a/Compiler/script2/cc_symboltable.cpp +++ b/Compiler/script2/cc_symboltable.cpp @@ -545,35 +545,51 @@ std::string const AGS::SymbolTable::GetName(AGS::Symbol symbl) const AGS::Vartype AGS::SymbolTable::VartypeWithArray(std::vector const &dims, AGS::Vartype vartype) { - bool const is_array_of_array = IsVTT(vartype, VTT::kArray); + // If 'vartype' is an array, the new array index must be spliced into the vartype name + // in front of the first '[' + std::string pre = GetName(vartype); + size_t first_bracket_pos = pre.find_first_of('['); + if (std::string::npos == first_bracket_pos) + first_bracket_pos = pre.length(); + std::string post = pre.substr(first_bracket_pos); + pre = pre.substr(0u, first_bracket_pos); + bool const is_array_of_array = IsVTT(vartype, VTT::kArray); std::vector aoa_dims; if (is_array_of_array) { // Classic array of classic array: Make the vartype of one joint array of them both std::vector const &old_dims = entries[vartype].VartypeD->Dims; aoa_dims.reserve(old_dims.size() + dims.size()); - aoa_dims = old_dims; - aoa_dims.insert(aoa_dims.end(), dims.begin(), dims.end()); + aoa_dims = dims; + aoa_dims.insert(aoa_dims.end(), old_dims.begin(), old_dims.end()); + // Cut off the first '[…]', it will be replaced by the index of the joint array + first_bracket_pos = post.find_first_of('[', 1u); + if (std::string::npos == first_bracket_pos) + first_bracket_pos = post.length(); + post = post.substr(first_bracket_pos); + vartype = entries[vartype].VartypeD->BaseVartype; } std::vector const &dims_to_use = is_array_of_array ? aoa_dims : dims; - std::string vartype_name = entries[vartype].Name + "["; + std::string insert = ""; size_t element_count = 1u; for (auto it = dims_to_use.cbegin(); it != dims_to_use.cend(); ++it) { element_count *= *it; - vartype_name += std::to_string(*it); - vartype_name += (it + 1 == dims_to_use.cend()) ? "]" : ", "; + insert += std::to_string(*it) + ", "; } + insert = "[" + insert.substr(0u, insert.length() - 2u) + "]"; + std::string const vartype_name = pre + insert + post; Vartype const array_vartype = FindOrAdd(vartype_name); if (!IsVartype(array_vartype)) { + // Initialise the new vartype entries[array_vartype].VartypeD = new SymbolTableEntry::VartypeDesc; entries[array_vartype].VartypeD->Type = VTT::kArray; entries[array_vartype].VartypeD->BaseVartype = vartype; entries[array_vartype].VartypeD->Size = element_count * GetSize(vartype); - entries[array_vartype].VartypeD->Dims = dims; + entries[array_vartype].VartypeD->Dims = dims_to_use; } return array_vartype; } diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index 6839e11efe..231103fd61 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -242,7 +242,7 @@ void AGS::Parser::SkipNextSymbol(SrcList src, Symbol expected) void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string const &custom_msg) { - for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) + for (size_t expected_idx = 0u; expected_idx < expected.size(); expected_idx++) if (actual == expected[expected_idx]) return; @@ -251,7 +251,7 @@ void AGS::Parser::Expect(SymbolList const &expected, Symbol actual, std::string { // Provide a default message errmsg = "Expected "; - for (size_t expected_idx = 0; expected_idx < expected.size(); expected_idx++) + for (size_t expected_idx = 0u; expected_idx < expected.size(); expected_idx++) { errmsg += "'" + _sym.GetName(expected[expected_idx]) + "'"; if (expected_idx + 2u < expected.size()) @@ -343,14 +343,18 @@ void AGS::Parser::MarMgr::UpdateMAR(size_t lineno, ccCompiledScript &scrip) default: // The start offset is already reached (e.g., when a Dynpointer chain is dereferenced) // but the component offset may need to be processed. - if (_componentOffs > 0) + if (_componentOffs > 0u) + { scrip.WriteCmd(SCMD_ADD, SREG_MAR, _componentOffs); + _parser._reg_track.SetRegister(SREG_MAR); + } break; case ScT::kGlobal: scrip.RefreshLineno(lineno); scrip.WriteCmd(SCMD_LITTOREG, SREG_MAR, _startOffs + _componentOffs); scrip.FixupPrevious(Parser::kFx_GlobalData); + _parser._reg_track.SetRegister(SREG_MAR); break; case ScT::kImport: @@ -359,8 +363,9 @@ void AGS::Parser::MarMgr::UpdateMAR(size_t lineno, ccCompiledScript &scrip) scrip.RefreshLineno(lineno); scrip.WriteCmd(SCMD_LITTOREG, SREG_MAR, _startOffs); scrip.FixupPrevious(Parser::kFx_Import); - if (_componentOffs != 0) + if (_componentOffs != 0u) scrip.WriteCmd(SCMD_ADD, SREG_MAR, _componentOffs); + _parser._reg_track.SetRegister(SREG_MAR); break; case ScT::kLocal: @@ -371,6 +376,7 @@ void AGS::Parser::MarMgr::UpdateMAR(size_t lineno, ccCompiledScript &scrip) _parser.InternalError("Trying to emit the negative offset %d to the top-of-stack", (int) offset); scrip.WriteCmd(SCMD_LOADSPOFFS, offset); + _parser._reg_track.SetRegister(SREG_MAR); break; } Reset(); @@ -1854,28 +1860,16 @@ void AGS::Parser::EvaluationResultToAx(EvaluationResult &eres) } } -void AGS::Parser::ParseExpression_CheckArgOfNew(Vartype argument_vartype) -{ - if (!_sym.IsVartype(argument_vartype)) - UserError("Expected a type after 'new', found '%s' instead", _sym.GetName(argument_vartype).c_str()); - if (_sym[argument_vartype].VartypeD->Flags[VTF::kUndefined]) - UserError( - ReferenceMsgSym("The struct '%s' hasn't been completely defined yet", argument_vartype).c_str(), - _sym.GetName(argument_vartype).c_str()); - if (!_sym.IsAnyIntegerVartype(argument_vartype) && kKW_Float != argument_vartype && !_sym.IsManagedVartype(argument_vartype)) - UserError("Can only use integer types or 'float' or managed types with 'new'"); - - // Note: While it is an error to use a built-in type with new, it is - // allowed to use a built-in type with new[]. -} - void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &eres) { if (expression.ReachedEOF()) UserError("Expected a type after 'new' but didn't find any"); Vartype const argument_vartype = expression.GetNext(); - ParseExpression_CheckArgOfNew(argument_vartype); + if (!_sym.IsVartype(argument_vartype)) + UserError( + "Expected a type after 'new', found '%s' instead", + _sym.GetName(argument_vartype).c_str()); bool const is_managed = _sym.IsManagedVartype(argument_vartype); bool const with_bracket_expr = kKW_OpenBracket == expression.PeekNext(); // "new FOO[BAR]" @@ -1899,13 +1893,26 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere element_vartype = is_managed ? _sym.VartypeWithDynpointer(argument_vartype) : argument_vartype; eres.Vartype = _sym.VartypeWithDynarray(element_vartype); + + while (kKW_OpenBracket == expression.PeekNext()) + { + SkipNextSymbol(_src, kKW_OpenBracket); + Expect(kKW_CloseBracket, expression.GetNext()); + element_vartype = eres.Vartype; + eres.Vartype = _sym.VartypeWithDynarray(element_vartype); + } } else { + if (_sym[argument_vartype].VartypeD->Flags[VTF::kUndefined]) + UserError( + ReferenceMsgSym("The struct '%s' hasn't been completely defined yet", argument_vartype).c_str(), + _sym.GetName(argument_vartype).c_str()); + if (_sym.IsBuiltinVartype(argument_vartype)) UserError("Expected '[' after the built-in type '%s'", _sym.GetName(argument_vartype).c_str()); if (!is_managed) - UserError("Expected '[' after the integer type '%s'", _sym.GetName(argument_vartype).c_str()); + UserError("Expected '[' after the non-managed type '%s'", _sym.GetName(argument_vartype).c_str()); if (kKW_OpenParenthesis == expression.PeekNext()) { @@ -1916,7 +1923,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere } // Only do this check for new, not for new[]. - if (0 == _sym.GetSize(argument_vartype)) + if (0u == _sym.GetSize(argument_vartype)) UserError( ReferenceMsgSym( "Struct '%s' doesn't contain any variables, cannot use 'new' with it", @@ -1928,7 +1935,7 @@ void AGS::Parser::ParseExpression_New(SrcList &expression, EvaluationResult &ere } size_t const element_size = _sym.GetSize(element_vartype); - if (0 == element_size) + if (0u == element_size) // The Engine really doesn't like that (division by zero error) InternalError("Trying to emit allocation of zero dynamic memory"); @@ -2876,7 +2883,6 @@ void AGS::Parser::AccessData_FunctionCall(Symbol name_of_func, SrcList &expressi { // MAR contains the address of "outer"; this is what will be used for "this" in the called function. _marMgr.UpdateMAR(_src.GetLineno(), _scrip); - _reg_track.SetRegister(SREG_MAR); // Parameter processing might entail calling yet other functions, e.g., in "f(...g(x)...)". // So we cannot emit SCMD_CALLOBJ here, before parameters have been processed. @@ -3140,54 +3146,54 @@ void AGS::Parser::AccessData_Dereference(EvaluationResult &eres) } } -void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t const dim, size_t const factor, bool const is_dynarray, SrcList &expression, EvaluationResult &eres) +void AGS::Parser::AccessData_ProcessArrayIndex(size_t const dim, size_t const factor, bool const is_dynarray, SrcList &index_expr, EvaluationResult &eres) { - // Get the index - size_t const index_start = expression.GetCursor(); - expression.SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }); - size_t const index_end = expression.GetCursor(); - SrcList current_index = SrcList(expression, index_start, index_end - index_start); - if (0u == current_index.Length()) - UserError("Array index #u is empty, this is not supported here", idx + 1u); - + index_expr.StartRead(); // If all ops are pending on the MAR register, it hasn't been set yet at all. // So then we don't need to protect MAR against being clobbered, - // but we do need to keep track of those pending ops in this case. - bool const all_ops_are_pending = _marMgr.AreAllOpsPending(); - MarMgr save_mar_state(_marMgr); - RegisterGuard(all_ops_are_pending ? RegisterList{} : RegisterList{ SREG_MAR }, + bool const guard_mar_register = !_marMgr.AreAllOpsPending(); + MarMgr orig_mar_state(_marMgr); + RegisterGuard(guard_mar_register ? RegisterList{ SREG_MAR } : RegisterList{}, [&] { - std::string msg = "In array index #: "; - msg.replace(msg.find(""), 5u, std::to_string(idx + 1u)); - current_index.StartRead(); - ParseIntegerExpression(current_index, eres, msg); + ParseIntegerExpression(index_expr, eres); + // We need the result of the array index expression (i.e., the offset to + // add to the location of the base variable) in AX unless it is a literal. + // That's the only location we can use. We can't use the address that MAR + // points to as the location because MAR will be clobbered by the 'POP(MAR)' + // that will be generated at the end of 'RegisterGuard()'. if (eres.kTY_Literal != eres.Type) EvaluationResultToAx(eres); }); + // Restore the state that the MAR register has been in + // This is important because we compute the correct MAR value lazily, + // directly before the value is needed. The state contains accumulated + // instructions that haven't been emitted yet. + _marMgr = orig_mar_state; + + if (!index_expr.ReachedEOF()) + UserError( + "Unexpected '%s' after array index", + _sym.GetName(index_expr.GetNext()).c_str()); + if (eres.kTY_Literal == eres.Type) { // The arrax index is known at compile time, so check it as far as possible int const index_value = _sym[eres.Symbol].LiteralD->Value; if (index_value < 0) UserError( - "Array index #%u is %d, thus too low (minimum is 0)", - idx + 1u, - index_value); + "Array index is %d, thus too low (minimum is 0)", index_value); if (dim > 0u && static_cast(index_value) >= dim) UserError( - "Array index #%u is %d, thus too high (maximum is %u)", - idx + 1u, - index_value, - dim - 1u); + "Array index is %d, thus too high (maximum is %u)", index_value, dim - 1u); if (is_dynarray && index_value > 0) { // We need to check the offset at runtime because we can't know the // array size that has been allocated. // Make sure that a _whole_ array element fits, so 'index_value * factor' isn't enough - WriteCmd(SCMD_LITTOREG, SREG_AX, (index_value + 1) * factor); + WriteCmd(SCMD_LITTOREG, SREG_AX, (index_value + 1) * factor - 1); _reg_track.SetRegister(SREG_AX); WriteCmd(SCMD_DYNAMICBOUNDS, SREG_AX); } @@ -3195,16 +3201,13 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c _marMgr.AddComponentOffset(index_value * factor); return; } - - if (all_ops_are_pending) - _marMgr = save_mar_state; - + // DYNAMICBOUNDS compares the offset into the memory block: // it mustn't be larger than the size of the allocated memory. // On the other hand, CHECKBOUNDS checks the index: it mustn't be // larger than the maximum given. So dynamic bounds must be checked // _after_ the multiplication; static bounds _before_ the multiplication. - // For better error messages at runtime, don't do CHECKBOUNDS after the multiplication. + // For better error messages at runtime, do CHECKBOUNDS first, then multiply. if (!is_dynarray) WriteCmd(SCMD_CHECKBOUNDS, SREG_AX, dim); if (factor != 1u) @@ -3220,62 +3223,67 @@ void AGS::Parser::AccessData_ProcessCurrentArrayIndex(size_t const idx, size_t c _reg_track.SetRegister(SREG_MAR); } -// We're processing some struct component or global or local variable. -// If an array index follows, parse it and shorten symlist accordingly -void AGS::Parser::AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres) +void AGS::Parser::AccessData_ProcessArrayIndexes(SrcList &expression, EvaluationResult &eres) { - if (kKW_OpenBracket != expression.PeekNext()) - return; - SkipNextSymbol(expression, kKW_OpenBracket); - - bool const is_dynarray = _sym.IsDynarrayVartype(eres.Vartype); - bool const is_array = _sym.IsArrayVartype(eres.Vartype); - if (!is_dynarray && !is_array) - UserError("Array index is only legal after an array expression"); - - Vartype const element_vartype = _sym[eres.Vartype].VartypeD->BaseVartype; - size_t const element_size = _sym.GetSize(element_vartype); - std::vector dim_sizes; - std::vector dynarray_dims = { 0u, }; - std::vector &dims = is_dynarray ? dynarray_dims : _sym[eres.Vartype].VartypeD->Dims; - eres.Vartype = element_vartype; - - if (is_dynarray) - AccessData_Dereference(eres); - - // Number of dimensions and the the size of the dimension for each dimension - size_t const dims_count = dims.size(); - dim_sizes.resize(dims_count); - size_t factor = element_size; - for (int dim_idx = dims_count - 1; dim_idx >= 0; dim_idx--) // yes, "int" - { - dim_sizes[dim_idx] = factor; - factor *= dims[dim_idx]; - } - - for (size_t dim_idx = 0u; dim_idx < dims_count; dim_idx++) + while (kKW_OpenBracket == expression.PeekNext()) { - EvaluationResult eres_index; - AccessData_ProcessCurrentArrayIndex(dim_idx, dims[dim_idx], dim_sizes[dim_idx], is_dynarray, expression, eres_index); - if (eres_index.SideEffects) - eres.SideEffects = true; - Symbol divider = expression.PeekNext(); - Expect(SymbolList{ kKW_CloseBracket, kKW_Comma }, divider); - - if (kKW_CloseBracket == divider) + if (!_sym.IsAnyArrayVartype(eres.Vartype)) + UserError( + "An array index cannot follow an expression of type '%s'", + _sym.GetName(eres.Vartype)); + + Vartype const array_vartype = eres.Vartype; + Vartype const element_vartype = _sym[array_vartype].VartypeD->BaseVartype; + bool const is_dynarray = _sym.IsDynarrayVartype(array_vartype); + + if (is_dynarray) + AccessData_Dereference(eres); + + std::vector dynarray_dims = { 0u, }; // Dynarrays have just 1 dimension + std::vector &dims = is_dynarray ? dynarray_dims : _sym[array_vartype].VartypeD->Dims; + std::vector factors; + factors.resize(dims.size()); + size_t factor = _sym.GetSize(element_vartype); + for (int idx = dims.size() - 1; idx >= 0; idx--) // yes, 'int' { - SkipNextSymbol(expression, kKW_CloseBracket); - divider = expression.PeekNext(); + factors[idx] = factor; + factor *= dims[idx]; } - if (kKW_Comma == divider || kKW_OpenBracket == divider) + + Expect(kKW_OpenBracket, expression.GetNext()); + + for (size_t dim_idx = 0u; dim_idx < dims.size(); dim_idx++) { - if (dims_count == dim_idx + 1u) - UserError("Expected %u indexes, found more", dims_count); - expression.GetNext(); // Eat ',' or '[' - continue; + // Get the current index + size_t const index_start = expression.GetCursor(); + expression.SkipTo(SymbolList{ kKW_Comma, kKW_CloseBracket }); + size_t const index_end = expression.GetCursor(); + SrcList index_expr = SrcList(expression, index_start, index_end - index_start); + if (0u == index_expr.Length()) + UserError("Array index must not be empty"); + + EvaluationResult eres_index; + AccessData_ProcessArrayIndex(dims[dim_idx], factors[dim_idx], is_dynarray, index_expr, eres_index); + if (eres_index.SideEffects) + eres.SideEffects = true; + + Symbol const divider = expression.GetNext(); + if (dim_idx + 1u == dims.size()) + { + Expect(kKW_CloseBracket, divider); + break; + } + Expect(SymbolList{ kKW_CloseBracket, kKW_Comma }, divider); + if (kKW_CloseBracket == divider) + { + Expect( + kKW_OpenBracket, + expression.GetNext(), + "Another index should follow: Expected"); + } } - if (dims_count != dim_idx + 1u) - UserError("Expected %u indexes, but only found %d", dims_count, dim_idx + 1u); + + eres.Vartype = element_vartype; } } @@ -3295,7 +3303,7 @@ void AGS::Parser::AccessData_Variable(VariableAccess access_type, SrcList &expre _marMgr.Reset(); _marMgr.SetStart(scope_type, soffs); - _reg_track.SetRegister(SREG_MAR); + // Note, no instructions are generated, so MAR is not set here eres.Type = eres.kTY_RunTimeValue; eres.Location = eres.kLOC_MemoryAtMAR; @@ -3303,7 +3311,7 @@ void AGS::Parser::AccessData_Variable(VariableAccess access_type, SrcList &expre eres.Vartype = _sym.GetVartype(varname); eres.LocalNonParameter = (ScT::kLocal == scope_type && entry.Scope != _sym.kParameterScope); eres.Modifiable = !var_tqs[TQ::kReadonly]; - return AccessData_ProcessArrayIndexIfThere(expression, eres); + return AccessData_ProcessArrayIndexes(expression, eres); } @@ -3362,7 +3370,7 @@ void AGS::Parser::AccessData_FirstClause(VariableAccess access_type, SrcList &ex AccessData_FunctionCall(first_sym, expression, eres); if (_sym.IsDynarrayVartype(eres.Vartype)) - AccessData_ProcessArrayIndexIfThere(expression, eres); + AccessData_ProcessArrayIndexes(expression, eres); return; } @@ -3449,7 +3457,6 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a { // make MAR point to the struct of the attribute _marMgr.UpdateMAR(_src.GetLineno(), _scrip); - _reg_track.SetRegister(SREG_MAR); if (VAC::kWriting == access_type) { @@ -3507,7 +3514,7 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a SrcList start_of_funccall = SrcList(expression, expression.GetCursor(), expression.Length()); AccessData_FunctionCall(qualified_component, start_of_funccall, eres); if (_sym.IsDynarrayVartype(vartype)) - return AccessData_ProcessArrayIndexIfThere(expression, eres); + return AccessData_ProcessArrayIndexes(expression, eres); return; } @@ -3519,7 +3526,7 @@ void AGS::Parser::AccessData_SubsequentClause(VariableAccess access_type, bool a _sym.GetName(qualified_component).c_str()); AccessData_StructMember(qualified_component, access_type, access_via_this, expression, eres); - return AccessData_ProcessArrayIndexIfThere(expression, eres); + return AccessData_ProcessArrayIndexes(expression, eres); } InternalError("Unknown kind of component of '%s'", _sym.GetName(vartype).c_str()); @@ -3611,7 +3618,7 @@ void AGS::Parser::AccessData(VariableAccess access_type, SrcList &expression, Ev continue; } - if (_sym.IsArrayVartype(eres.Vartype) || _sym.IsDynarrayVartype(eres.Vartype)) + if (_sym.IsAnyArrayVartype(eres.Vartype)) UserError("Expected a struct in front of '.' but found an array instead"); else if (!_sym.IsStructVartype(eres.Vartype)) UserError( @@ -4249,7 +4256,8 @@ void AGS::Parser::ParseVardecl_Local(Symbol var_name, Vartype vartype) EvaluationResultToAx(rhs_eres); // Vartypes must match. This is true even if the lhs is readonly. - // As a special case, a string may be assigned a const string because the const string will be copied, not modified. + // As a special case, a string may be assigned a const string + // because the const string will be copied, not modified. Vartype rhsvartype = rhs_eres.Vartype; Vartype const lhsvartype = vartype; @@ -4287,7 +4295,7 @@ void AGS::Parser::ParseVardecl_Local(Symbol var_name, Vartype vartype) void AGS::Parser::ParseVardecl0(Symbol var_name, Vartype vartype, ScopeType scope_type, TypeQualifierSet tqs) { if (kKW_OpenBracket == _src.PeekNext()) - ParseArray(var_name, vartype); + ParseArrayDecl(var_name, vartype); // Don't warn for builtins or imports, they might have been predefined if (!_sym.IsBuiltinVartype(vartype) && ScT::kImport != scope_type && 0u == _sym.GetSize(vartype)) @@ -4861,77 +4869,82 @@ void AGS::Parser::ParseStruct_Attribute(TypeQualifierSet tqs, Symbol const name_ } } -// We're parsing an array var. -void AGS::Parser::ParseArray(Symbol vname, Vartype &vartype) + +void AGS::Parser::ParseArrayDecl(Symbol vname, Vartype &vartype) { + // To the end of the '[…]' clause SkipNextSymbol(_src, kKW_OpenBracket); + size_t const array_expr_start = _src.GetCursor(); + + _src.SkipToCloser(); + SkipNextSymbol(_src, kKW_CloseBracket); + + // When more '[…]' clauses follow, they need to be processed first, so call this func recursively + if (kKW_OpenBracket == _src.PeekNext()) + ParseArrayDecl(vname, vartype); if (PP::kPreAnalyze == _pp) - { - // Skip the sequence of [...] - while (true) - { - _src.SkipToCloser(); - if (kKW_OpenBracket != _src.PeekNext()) - return; - SkipNextSymbol(_src, kKW_OpenBracket); - } - } + return; // No need to analyse it further in this phase + + size_t const array_expr_end = _src.GetCursor(); + _src.SetCursor(array_expr_start); if (kKW_CloseBracket == _src.PeekNext()) { // Dynamic array SkipNextSymbol(_src, kKW_CloseBracket); if (vartype == kKW_String) - UserError("Dynamic arrays of old-style strings are not supported"); - if (!_sym.IsAnyIntegerVartype(vartype) && !_sym.IsManagedVartype(vartype) && kKW_Float != vartype) - UserError( - "Can only have dynamic arrays of integer types, 'float', or managed structs. '%s' isn't any of this.", - _sym.GetName(vartype).c_str()); + UserError("Cannot have a dynamic array of old-style strings"); + if (_sym.IsArrayVartype(vartype)) + UserError("Cannot have a dynamic array of a classic array"); vartype = _sym.VartypeWithDynarray(vartype); + _src.SetCursor(array_expr_end); return; } - std::vector dims; - // Static array + std::vector dims; while (true) { - std::string msg = "For dimension # of array '': "; - msg.replace(msg.find(""), 5u, std::to_string(dims.size())); - msg.replace(msg.find(""), 5u, _sym.GetName(vname).c_str()); - Symbol const first_sym = _src.PeekNext(); - EvaluationResult eres; - int const cursor = _src.GetCursor(); + int const dim_start = _src.GetCursor(); _src.SkipTo(kKW_Comma); - SrcList expression = SrcList(_src, cursor, _src.GetCursor() - cursor); + SrcList expression = SrcList(_src, dim_start, _src.GetCursor() - dim_start); + if (0u == expression.Length()) + UserError( + "Expected an integer expression for array dimension #%u, did not find any", + dims.size() + 1); expression.StartRead(); - ParseIntegerExpression(expression, eres, msg); + ParseIntegerExpression(expression, eres); if (eres.kTY_Literal != eres.Type) UserError( - (msg + "Cannot evaluate the expression starting with '%s' at compile time").c_str(), - _sym.GetName(first_sym).c_str()); + "Cannot evaluate the integer expression for array dimension #%u at compile time", + dims.size() + 1u); + if (!expression.ReachedEOF()) + UserError( + "Unexpected '%s' after the integer expression for array dimension #%u", + _sym.GetName(expression.GetNext()).c_str(), + dims.size() + 1u); CodeCell const dimension_as_int = _sym[eres.Symbol].LiteralD->Value; if (dimension_as_int < 1) UserError( - "Array dimension #%u of array '%s' must be at least 1 but is %d instead", - dims.size(), - _sym.GetName(vname).c_str(), + "Array dimension #%u must be at least 1 but is %d instead", + dims.size() + 1u, dimension_as_int); - dims.push_back(dimension_as_int); + dims.push_back(static_cast(dimension_as_int)); Symbol const punctuation = _src.GetNext(); Expect(SymbolList{ kKW_Comma, kKW_CloseBracket }, punctuation); if (kKW_Comma == punctuation) continue; - if (kKW_OpenBracket != _src.PeekNext()) - break; - SkipNextSymbol(_src, kKW_OpenBracket); + // Successive static arrays will be joined within 'VartypeWithArray()', below. + break; } + vartype = _sym.VartypeWithArray(dims, vartype); + _src.SetCursor(array_expr_end); } void AGS::Parser::ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype, Symbol name_of_struct, Symbol vname) @@ -4967,7 +4980,7 @@ void AGS::Parser::ParseStruct_VariableDefn(TypeQualifierSet tqs, Vartype vartype if (_src.PeekNext() == kKW_OpenBracket) { Vartype vartype = _sym[vname].VariableD->Vartype; - ParseArray(vname, vartype); + ParseArrayDecl(vname, vartype); _sym[vname].VariableD->Vartype = vartype; } diff --git a/Compiler/script2/cs_parser.h b/Compiler/script2/cs_parser.h index e4447abaf1..782c9216ed 100644 --- a/Compiler/script2/cs_parser.h +++ b/Compiler/script2/cs_parser.h @@ -559,9 +559,6 @@ class Parser // Return whether this is possible. bool ParseExpression_CompileTime(Symbol op_sym, EvaluationResult const &eres_lhs, EvaluationResult const &eres_rhs, EvaluationResult &eres); - // Check the vartype following 'new' - void ParseExpression_CheckArgOfNew(Vartype new_vartype); - // Parse the term given in 'expression'. The lowest-binding operator is unary 'new' // 'expression' is parsed from the beginning. The term must use up 'expression' completely. void ParseExpression_New(SrcList &expression, EvaluationResult &eres); @@ -673,12 +670,12 @@ class Parser void AccessData_Dereference(EvaluationResult &eres); // Process one index in a sequence of array indexes - void AccessData_ProcessCurrentArrayIndex(size_t idx, size_t dim, size_t factor, bool is_dynarray, SrcList &expression, EvaluationResult &eres); + void AccessData_ProcessArrayIndex(size_t dim, size_t factor, bool is_dynarray, SrcList &index_expr, EvaluationResult &eres); // We're processing some struct component or global or local variable. - // If a sequence of array indexes follows, parse it and shorten symlist accordingly - void AccessData_ProcessArrayIndexIfThere(SrcList &expression, EvaluationResult &eres); - + // If a sequence of array indexes follows, parse it + void AccessData_ProcessArrayIndexes(SrcList &expression, EvaluationResult &eres); + void AccessData_Variable(VariableAccess access_type, SrcList &expression, EvaluationResult &eres); void AGS::Parser::AccessData_This(EvaluationResult &eres); @@ -757,7 +754,7 @@ class Parser void ParseVardecl_Var2SymTable(Symbol var_name, Vartype vartype); // we have accepted something like "int a" and we're expecting "[" - void ParseArray(Symbol vname, Vartype &vartype); + void ParseArrayDecl(Symbol vname, Vartype &vartype); void ParseVardecl_CheckIllegalCombis(Vartype vartype, ScopeType scope_type); // there was a forward declaration -- check that the real declaration matches it diff --git a/Compiler/test2/cc_bytecode_test_0.cpp b/Compiler/test2/cc_bytecode_test_0.cpp index b24072e1e3..6809c4bed8 100644 --- a/Compiler/test2/cc_bytecode_test_0.cpp +++ b/Compiler/test2/cc_bytecode_test_0.cpp @@ -2088,7 +2088,7 @@ TEST_F(Bytecode0, Struct01) { int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("Struct01_Rtti", scrip); + // WriteOutput("Struct01", scrip); size_t const codesize = 155; EXPECT_EQ(codesize, scrip.codesize); @@ -2098,7 +2098,7 @@ TEST_F(Bytecode0, Struct01) { 2, 52, 6, 3, 0, 8, 3, 36, // 23 11, 6, 3, 5, 75, 3, 3, 4, // 31 51, 4, 47, 3, 36, 12, 51, 4, // 39 - 48, 2, 52, 6, 3, 16, 71, 3, // 47 + 48, 2, 52, 6, 3, 15, 71, 3, // 47 6, 3, 77, 1, 2, 12, 8, 3, // 55 36, 13, 51, 4, 48, 3, 29, 3, // 63 51, 4, 50, 3, 51, 8, 49, 51, // 71 @@ -2110,7 +2110,7 @@ TEST_F(Bytecode0, Struct01) { 0, 23, 3, 2, 1, 4, 30, 2, // 119 51, 0, 47, 3, 1, 1, 4, 36, // 127 20, 51, 4, 48, 2, 52, 6, 3, // 135 - 16, 71, 3, 1, 2, 12, 7, 3, // 143 + 15, 71, 3, 1, 2, 12, 7, 3, // 143 29, 3, 36, 21, 51, 8, 49, 2, // 151 1, 12, 5, -999 }; @@ -2508,7 +2508,7 @@ TEST_F(Bytecode0, Struct06) { int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("Struct06_Rtti", scrip); + // WriteOutput("Struct06", scrip); size_t const codesize = 59; EXPECT_EQ(codesize, scrip.codesize); @@ -2517,7 +2517,7 @@ TEST_F(Bytecode0, Struct06) { 0, 29, 3, 36, 16, 6, 3, 5, // 15 75, 3, 91, 4, 51, 4, 47, 3, // 23 36, 17, 51, 4, 48, 2, 52, 6, // 31 - 3, 16, 71, 3, 1, 2, 12, 48, // 39 + 3, 15, 71, 3, 1, 2, 12, 48, // 39 2, 52, 6, 3, 77, 8, 3, 36, // 47 18, 51, 4, 49, 2, 1, 4, 6, // 55 3, 0, 5, -999 @@ -3174,20 +3174,20 @@ TEST_F(Bytecode0, Struct12) { 36, 8, 38, 0, 36, 9, 6, 3, // 7 0, 29, 3, 36, 10, 6, 3, 10, // 15 72, 3, 4, 0, 6, 2, 4, 47, // 23 - 3, 36, 14, 6, 2, 4, 48, 2, // 31 - 52, 36, 15, 6, 3, 16, 71, 3, // 39 + 3, 36, 13, 6, 2, 4, 48, 2, // 31 + 52, 36, 15, 6, 3, 15, 71, 3, // 39 36, 16, 6, 3, 7, 1, 2, 12, // 47 - 8, 3, 36, 20, 6, 2, 4, 48, // 55 + 8, 3, 36, 19, 6, 2, 4, 48, // 55 2, 52, 36, 21, 29, 2, 51, 8, // 63 7, 3, 30, 2, 32, 3, 4, 71, // 71 3, 11, 2, 3, 36, 22, 6, 3, // 79 - 7, 8, 3, 36, 28, 6, 2, 4, // 87 + 7, 8, 3, 36, 27, 6, 2, 4, // 87 48, 2, 52, 36, 29, 29, 2, 51, // 95 8, 7, 3, 30, 2, 32, 3, 4, // 103 71, 3, 11, 2, 3, 36, 30, 7, // 111 3, 36, 24, 51, 4, 8, 3, 36, // 119 - 36, 6, 2, 4, 48, 2, 52, 36, // 127 - 37, 6, 3, 16, 71, 3, 1, 2, // 135 + 35, 6, 2, 4, 48, 2, 52, 36, // 127 + 37, 6, 3, 15, 71, 3, 1, 2, // 135 12, 36, 38, 7, 3, 36, 39, 2, // 143 1, 4, 5, -999 }; @@ -5203,6 +5203,501 @@ TEST_F(Bytecode0, ArrayOfPointers2_RTTI) { EXPECT_EQ(stringssize, scrip.stringssize); } +TEST_F(Bytecode0, DynarrayOfDynarray1) { + + // Accept a dynamic array of a dynamic array of a non-managed type + // RTTI ENABLED + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + char FooK[20]; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + int i = 2; \n\ + Foo Test[][]; \n\ + Test = new Foo[3][]; \n\ + Test[2] = new Foo[5]; \n\ + Test[2][i].FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + WriteOutput("DynarrayOfDynarray1", scrip); +} + +TEST_F(Bytecode0, DynarrayOfDynarray2) { + + // Accept a dynamic array of a dynamic array of a non-managed type + // RTTI ENABLED + + char *inpl = "\ + short Test[][]; \n\ + \n\ + int game_start() \n\ + { \n\ + int i = 2; \n\ + Test = new short[3][]; \n\ + Test[i] = new short[5]; \n\ + Test[2][4] = 4711; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfDynarray2", scrip); + size_t const codesize = 103; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 4, 38, 0, 36, 5, 6, 3, // 7 + 2, 29, 3, 36, 6, 6, 3, 3, // 15 + 75, 3, 5, 4, 6, 2, 0, 47, // 23 + 3, 36, 7, 6, 3, 5, 75, 3, // 31 + 5, 2, 29, 3, 6, 2, 0, 48, // 39 + 2, 52, 29, 2, 51, 12, 7, 3, // 47 + 30, 2, 32, 3, 4, 71, 3, 11, // 55 + 2, 3, 30, 3, 47, 3, 36, 8, // 63 + 6, 2, 0, 48, 2, 52, 6, 3, // 71 + 11, 71, 3, 1, 2, 8, 48, 2, // 79 + 52, 6, 3, 9, 71, 3, 6, 3, // 87 + 4711, 1, 2, 8, 27, 3, 36, 9, // 95 + 2, 1, 4, 6, 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 3; + EXPECT_EQ(numfixups, scrip.numfixups); + + int32_t fixups[] = { + 22, 38, 66, -999 + }; + char fixuptypes[] = { + 1, 1, 1, '\0' + }; + CompareFixups(&scrip, numfixups, fixups, fixuptypes); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfDynarray3) { + + // Accept a dynamic array of a dynamic array of a managed type + // RTTI ENABLED + + char *inpl = "\ + managed struct Foo \n\ + { \n\ + int FooI; \n\ + char FooK[20]; \n\ + }; \n\ + \n\ + int i = 2; \n\ + int game_start() \n\ + { \n\ + Foo Test[][]; \n\ + Test = new Foo[3][]; \n\ + Test[2] = new Foo[5]; \n\ + Test[2][i] = new Foo; \n\ + Test[2][i].FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfDynarray3", scrip); + size_t const codesize = 156; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 9, 38, 0, 36, 10, 51, 0, // 7 + 49, 1, 1, 4, 36, 11, 6, 3, // 15 + 3, 75, 3, 91, 4, 51, 4, 47, // 23 + 3, 36, 12, 6, 3, 5, 75, 3, // 31 + 91, 4, 29, 3, 51, 8, 48, 2, // 39 + 52, 6, 3, 11, 71, 3, 30, 3, // 47 + 1, 2, 8, 47, 3, 36, 13, 74, // 55 + 3, 91, 24, 29, 3, 51, 8, 48, // 63 + 2, 52, 6, 3, 11, 71, 3, 1, // 71 + 2, 8, 48, 2, 52, 29, 2, 6, // 79 + 2, 0, 7, 3, 30, 2, 32, 3, // 87 + 4, 71, 3, 11, 2, 3, 30, 3, // 95 + 47, 3, 36, 14, 51, 4, 48, 2, // 103 + 52, 6, 3, 11, 71, 3, 1, 2, // 111 + 8, 48, 2, 52, 29, 2, 6, 2, // 119 + 0, 7, 3, 30, 2, 32, 3, 4, // 127 + 71, 3, 11, 2, 3, 48, 2, 52, // 135 + 6, 3, 99, 1, 2, 15, 26, 3, // 143 + 36, 15, 51, 4, 49, 2, 1, 4, // 151 + 6, 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 2; + EXPECT_EQ(numfixups, scrip.numfixups); + + int32_t fixups[] = { + 81, 120, -999 + }; + char fixuptypes[] = { + 1, 1, '\0' + }; + CompareFixups(&scrip, numfixups, fixups, fixuptypes); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + + +TEST_F(Bytecode0, ArrayOfDynarray) { + + // Accept a dynamic array of a dynamic array + // RTTI ENABLED + + char *inpl = "\ + int game_start() \n\ + { \n\ + int i = 2; \n\ + int Test[2, 3][]; \n\ + Test[1, 2] = new int[3]; \n\ + Test[i / 2][i][i] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("ArrayOfDynarray", scrip); + size_t const codesize = 115; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 2, 38, 0, 36, 3, 6, 3, // 7 + 2, 29, 3, 36, 4, 51, 0, 63, // 15 + 24, 1, 1, 24, 36, 5, 6, 3, // 23 + 3, 75, 3, 3, 4, 51, 4, 47, // 31 + 3, 36, 6, 51, 28, 7, 3, 29, // 39 + 3, 6, 3, 2, 30, 4, 10, 4, // 47 + 3, 3, 4, 3, 46, 3, 2, 32, // 55 + 3, 12, 51, 24, 11, 2, 3, 29, // 63 + 2, 51, 32, 7, 3, 30, 2, 46, // 71 + 3, 3, 32, 3, 4, 11, 2, 3, // 79 + 48, 2, 52, 29, 2, 51, 32, 7, // 87 + 3, 30, 2, 32, 3, 4, 71, 3, // 95 + 11, 2, 3, 6, 3, 99, 8, 3, // 103 + 36, 7, 51, 24, 2, 1, 28, 6, // 111 + 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, ArrayOfManagedStruct) { + + // Accept a classic array of a managed struct + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + float FooJ; \n\ + char FooK[20]; \n\ + }; \n\ + managed struct Bar \n\ + { \n\ + int BarI; \n\ + Foo BarJ[3]; \n\ + Foo BarK; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + int i = 4; \n\ + Bar Test[5, 7]; \n\ + Test[2, 3].BarJ[i].FooJ = 5.0; \n\ + Test[i, 0].BarK.FooK[11] = 'c'; \n\ + } \n\ + "; + + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("ArrayOfManaged", scrip); + size_t const codesize = 106; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 15, 38, 0, 36, 16, 6, 3, // 7 + 4, 29, 3, 36, 17, 51, 0, 63, // 15 + 140, 1, 1, 140, 36, 18, 51, 72, // 23 + 48, 2, 52, 29, 2, 51, 148, 7, // 31 + 3, 30, 2, 46, 3, 3, 32, 3, // 39 + 28, 1, 2, 4, 11, 2, 3, 6, // 47 + 3, 1084227584, 1, 2, 4, 8, 3, 36, // 55 + 19, 51, 144, 7, 3, 46, 3, 5, // 63 + 32, 3, 28, 51, 140, 11, 2, 3, // 71 + 48, 2, 52, 6, 3, 99, 1, 2, // 79 + 107, 26, 3, 36, 20, 51, 140, 6, // 87 + 3, 35, 49, 1, 2, 4, 2, 3, // 95 + 1, 70, -9, 2, 1, 144, 6, 3, // 103 + 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfNonManaged_NoRtti) { + + // RTTI DISABLED + // Accept a dynarray of a non-managed struct that doesn't contain managed variable components + // (Elsewhere there's a googletest checking that structs are not accepted that do + // contain managed variable components.) + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + float FooJ; \n\ + char FooK[20]; \n\ + }; \n\ + \n\ + struct Bar \n\ + { \n\ + int BarI; \n\ + float BarJ; \n\ + Foo BarK; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + Bar Test[] = new Bar[17]; \n\ + Test[3].BarJ = 5.0; \n\ + Test[7].BarK.FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, false); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfNonManaged_NoRtti", scrip); + size_t const codesize = 72; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 16, 38, 0, 36, 17, 6, 3, // 7 + 17, 72, 3, 36, 0, 51, 0, 47, // 15 + 3, 1, 1, 4, 36, 18, 51, 4, // 23 + 48, 2, 52, 6, 3, 143, 71, 3, // 31 + 6, 3, 1084227584, 1, 2, 112, 8, 3, // 39 + 36, 19, 51, 4, 48, 2, 52, 6, // 47 + 3, 287, 71, 3, 6, 3, 99, 1, // 55 + 2, 279, 26, 3, 36, 20, 51, 4, // 63 + 49, 2, 1, 4, 6, 3, 0, 5, // 71 + -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfNonManaged_Rtti) { + + // RTTI ENABLED + + // Accept a dynarray of a non-managed struct. + // RTTI is enabled, and so the non-managed struct may contain dynamic components. + + char *inpl = "\ + struct Foo \n\ + { \n\ + int FooI; \n\ + float FooJ; \n\ + char FooK[]; \n\ + }; \n\ + \n\ + struct Bar \n\ + { \n\ + int BarI; \n\ + float BarJ; \n\ + Foo BarK; \n\ + }; \n\ + \n\ + int game_start() \n\ + { \n\ + Bar Test[] = new Bar[17]; \n\ + int i = 7; \n\ + Test[3].BarJ = 5.0; \n\ + Test[i].BarK.FooK[11] = 'c'; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfNonManaged_Rtti", scrip); + size_t const codesize = 101; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 16, 38, 0, 36, 17, 6, 3, // 7 + 17, 75, 3, 95, 20, 51, 0, 47, // 15 + 3, 1, 1, 4, 36, 18, 6, 3, // 23 + 7, 29, 3, 36, 19, 51, 8, 48, // 31 + 2, 52, 6, 3, 79, 71, 3, 6, // 39 + 3, 1084227584, 1, 2, 64, 8, 3, 36, // 47 + 20, 51, 8, 48, 2, 52, 29, 2, // 55 + 51, 8, 7, 3, 30, 2, 32, 3, // 63 + 20, 71, 3, 11, 2, 3, 1, 2, // 71 + 16, 48, 2, 52, 6, 3, 11, 71, // 79 + 3, 6, 3, 99, 1, 2, 11, 26, // 87 + 3, 36, 21, 51, 8, 49, 2, 1, // 95 + 8, 6, 3, 0, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + +TEST_F(Bytecode0, DynarrayOfPrimitives) { + + // Dynamic arrays of primitives are allowed. + + char const *inpl = "\ + int main() \n\ + { \n\ + short PrmArray[] = new short[10]; \n\ + PrmArray[7] = 0; \n\ + PrmArray[3] = PrmArray[7]; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, true); + + int compileResult = cc_compile(inpl, scrip); + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("DynarrayOfPrimitives_Rtti", scrip); + size_t const codesize = 88; + EXPECT_EQ(codesize, scrip.codesize); + + int32_t code[] = { + 36, 2, 38, 0, 36, 3, 6, 3, // 7 + 10, 75, 3, 5, 2, 51, 0, 47, // 15 + 3, 1, 1, 4, 36, 4, 51, 4, // 23 + 48, 2, 52, 6, 3, 15, 71, 3, // 31 + 6, 3, 0, 1, 2, 14, 27, 3, // 39 + 36, 5, 51, 4, 48, 2, 52, 6, // 47 + 3, 15, 71, 3, 1, 2, 14, 25, // 55 + 3, 29, 3, 51, 8, 48, 2, 52, // 63 + 6, 3, 7, 71, 3, 30, 3, 1, // 71 + 2, 6, 27, 3, 36, 6, 51, 4, // 79 + 49, 2, 1, 4, 6, 3, 0, 5, // 87 + -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 0; + EXPECT_EQ(numfixups, scrip.numfixups); + + int const numimports = 0; + std::string imports[] = { + "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.numexports); + + size_t const stringssize = 0; + EXPECT_EQ(stringssize, scrip.stringssize); +} + TEST_F(Bytecode0, Writeprotected) { // Directly taken from the doc on writeprotected, simplified. @@ -5563,4 +6058,5 @@ TEST_F(Bytecode0, Import) { EXPECT_EQ(numexports, scrip.numexports); size_t const stringssize = 0; - EXPECT_EQ(stringssize, scrip.stringssize);} + EXPECT_EQ(stringssize, scrip.stringssize); +} diff --git a/Compiler/test2/cc_bytecode_test_1.cpp b/Compiler/test2/cc_bytecode_test_1.cpp index 5a137b901b..7dc33e6627 100644 --- a/Compiler/test2/cc_bytecode_test_1.cpp +++ b/Compiler/test2/cc_bytecode_test_1.cpp @@ -2564,7 +2564,7 @@ TEST_F(Bytecode1, StructWOldstyleString2) { 0, 8, 3, 36, 14, 51, 8, 48, // 95 2, 52, 1, 2, 2, 3, 2, 3, // 103 29, 3, 51, 8, 48, 2, 52, 6, // 111 - 3, 12, 71, 3, 1, 2, 8, 48, // 119 + 3, 11, 71, 3, 1, 2, 8, 48, // 119 2, 52, 30, 3, 1, 2, 2, 3, // 127 3, 5, 3, 2, 4, 6, 7, 199, // 135 3, 4, 2, 7, 3, 3, 5, 2, // 143 @@ -3215,60 +3215,6 @@ TEST_F(Bytecode1, DynarrayLength2_RTTI) { EXPECT_EQ(stringssize, scrip.stringssize); } -TEST_F(Bytecode1, DynarrayOfPrimitives) { - - // Dynamic arrays of primitives are allowed. - - char const *inpl = "\ - int main() \n\ - { \n\ - short PrmArray[] = new short[10]; \n\ - PrmArray[7] = 0; \n\ - PrmArray[3] = PrmArray[7]; \n\ - } \n\ - "; - - ccSetOption(SCOPT_RTTIOPS, true); - - int compileResult = cc_compile(inpl, scrip); - ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - - // WriteOutput("DynarrayOfPrimitives_Rtti", scrip); - size_t const codesize = 88; - EXPECT_EQ(codesize, scrip.codesize); - - int32_t code[] = { - 36, 2, 38, 0, 36, 3, 6, 3, // 7 - 10, 75, 3, 5, 2, 51, 0, 47, // 15 - 3, 1, 1, 4, 36, 4, 51, 4, // 23 - 48, 2, 52, 6, 3, 16, 71, 3, // 31 - 6, 3, 0, 1, 2, 14, 27, 3, // 39 - 36, 5, 51, 4, 48, 2, 52, 6, // 47 - 3, 16, 71, 3, 1, 2, 14, 25, // 55 - 3, 29, 3, 51, 8, 48, 2, 52, // 63 - 6, 3, 8, 71, 3, 30, 3, 1, // 71 - 2, 6, 27, 3, 36, 6, 51, 4, // 79 - 49, 2, 1, 4, 6, 3, 0, 5, // 87 - -999 - }; - CompareCode(&scrip, codesize, code); - - size_t const numfixups = 0; - EXPECT_EQ(numfixups, scrip.numfixups); - - int const numimports = 0; - std::string imports[] = { - "[[SENTINEL]]" - }; - CompareImports(&scrip, numimports, imports); - - size_t const numexports = 0; - EXPECT_EQ(numexports, scrip.numexports); - - size_t const stringssize = 0; - EXPECT_EQ(stringssize, scrip.stringssize); -} - TEST_F(Bytecode1, StringLiteral2String) { char const *inpl = "\ @@ -3486,3 +3432,4 @@ TEST_F(Bytecode1, Linenum02) size_t const stringssize = 0; EXPECT_EQ(stringssize, scrip.stringssize); } + diff --git a/Compiler/test2/cc_parser_test_0.cpp b/Compiler/test2/cc_parser_test_0.cpp index d165fe4a12..a23b55796b 100644 --- a/Compiler/test2/cc_parser_test_0.cpp +++ b/Compiler/test2/cc_parser_test_0.cpp @@ -1143,30 +1143,9 @@ TEST_F(Compile0, ImportOverride1) { EXPECT_EQ(std::string::npos, err.find("xception")); } -TEST_F(Compile0, DynamicNonManaged1) { - - // Dynamic array of non-managed struct not allowed - - char const *inpl = "\ - struct Inner \n\ - { \n\ - short Payload; \n\ - }; \n\ - managed struct Struct \n\ - { \n\ - Inner In[]; \n\ - }; \n\ - "; - - int compileResult = cc_compile(inpl, scrip); - ASSERT_STRNE("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - std::string res(last_seen_cc_error()); - EXPECT_NE(std::string::npos, res.find("Inner")); -} - TEST_F(Compile0, DynamicNonManaged2) { - // Dynamic array of non-managed struct not allowed + // Dynamic pointer to non-managed struct not allowed char const *inpl = "\ struct Inner \n\ @@ -1187,7 +1166,7 @@ TEST_F(Compile0, DynamicNonManaged2) { TEST_F(Compile0, DynamicNonManaged3) { - // Dynamic array of non-managed struct not allowed + // Dynamic pointer to non-managed struct not allowed char const *inpl = "\ struct Inner \n\ diff --git a/Compiler/test2/cc_parser_test_1.cpp b/Compiler/test2/cc_parser_test_1.cpp index 994fcd9c4a..6cac4534f5 100644 --- a/Compiler/test2/cc_parser_test_1.cpp +++ b/Compiler/test2/cc_parser_test_1.cpp @@ -2643,3 +2643,19 @@ TEST_F(Compile1, ParensAfterNew) { ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : mh.GetError().Message.c_str()); ASSERT_EQ(1u, mh.GetMessages().size()); } + +TEST_F(Compile1, DynarrayOfArray) { + + // Refuse a dynarray of an array + + char *inpl = "\ + int game_start() \n\ + { \n\ + float Test[][5, 7]; \n\ + } \n\ + "; + + ccSetOption(SCOPT_RTTIOPS, false); + int compileResult = cc_compile(inpl, scrip); + ASSERT_STRNE("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); +}