diff --git a/include/scratchcpp/list.h b/include/scratchcpp/list.h index 47c6782c..25f1f361 100644 --- a/include/scratchcpp/list.h +++ b/include/scratchcpp/list.h @@ -153,7 +153,7 @@ class LIBSCRATCHCPP_EXPORT List : public Entity } /*! Joins the list items with spaces or without any separator if there are only digits and returns the result as StringPtr. */ - inline StringPtr *toStringPtr() const + inline void toStringPtr(StringPtr *dst) const { veque::veque strings; size_t size = 0; @@ -163,7 +163,9 @@ class LIBSCRATCHCPP_EXPORT List : public Entity for (i = 0; i < m_size; i++) { const ValueData *item = &m_dataPtr->operator[](i); - strings.push_back(value_toStringPtr(item)); + StringPtr *str = string_pool_new(); + value_toStringPtr(item, str); + strings.push_back(str); size += strings.back()->size; if (value_isValidNumber(item) && !value_isBool(item) && strings.back()->size > 0) { @@ -185,54 +187,54 @@ class LIBSCRATCHCPP_EXPORT List : public Entity } } - StringPtr *ret = string_pool_new(); - ret->size = 0; + dst->size = 0; if (digits) { - string_alloc(ret, size); + string_alloc(dst, size); for (i = 0; i < strings.size(); i++) { - memcpy(ret->data + ret->size, strings[i]->data, strings[i]->size * sizeof(char16_t)); - ret->size += strings[i]->size; + memcpy(dst->data + dst->size, strings[i]->data, strings[i]->size * sizeof(char16_t)); + dst->size += strings[i]->size; string_pool_free(strings[i]); } for (; i < m_size; i++) { - StringPtr *item = value_toStringPtr(&m_dataPtr->operator[](i)); + StringPtr *item = string_pool_new(); + value_toStringPtr(&m_dataPtr->operator[](i), item); size += item->size + 1; - string_alloc(ret, size); - memcpy(ret->data + ret->size, item->data, item->size * sizeof(char16_t)); - ret->size += item->size; + string_alloc(dst, size); + memcpy(dst->data + dst->size, item->data, item->size * sizeof(char16_t)); + dst->size += item->size; string_pool_free(item); } } else { size += strings.size() - 1; - string_alloc(ret, size); + string_alloc(dst, size); for (i = 0; i < strings.size(); i++) { - memcpy(ret->data + ret->size, strings[i]->data, strings[i]->size * sizeof(char16_t)); - ret->size += strings[i]->size; + memcpy(dst->data + dst->size, strings[i]->data, strings[i]->size * sizeof(char16_t)); + dst->size += strings[i]->size; string_pool_free(strings[i]); if (i + 1 < m_size) - ret->data[ret->size++] = u' '; + dst->data[dst->size++] = u' '; } for (; i < m_size; i++) { - StringPtr *item = value_toStringPtr(&m_dataPtr->operator[](i)); + StringPtr *item = string_pool_new(); + value_toStringPtr(&m_dataPtr->operator[](i), item); size += item->size + 1; - string_alloc(ret, size); - memcpy(ret->data + ret->size, item->data, item->size * sizeof(char16_t)); - ret->size += item->size; + string_alloc(dst, size); + memcpy(dst->data + dst->size, item->data, item->size * sizeof(char16_t)); + dst->size += item->size; string_pool_free(item); if (i + 1 < m_size) - ret->data[ret->size++] = u' '; + dst->data[dst->size++] = u' '; } } - ret->data[ret->size] = u'\0'; - return ret; + dst->data[dst->size] = u'\0'; } std::string toString() const; diff --git a/include/scratchcpp/string_pool.h b/include/scratchcpp/string_pool.h index 57109aff..0b68a6a7 100644 --- a/include/scratchcpp/string_pool.h +++ b/include/scratchcpp/string_pool.h @@ -11,7 +11,7 @@ struct StringPtr; extern "C" { - LIBSCRATCHCPP_EXPORT StringPtr *string_pool_new(bool internal = false); + LIBSCRATCHCPP_EXPORT StringPtr *string_pool_new(); LIBSCRATCHCPP_EXPORT void string_pool_free(StringPtr *str); } diff --git a/include/scratchcpp/thread.h b/include/scratchcpp/thread.h index 7a61529f..353d9aee 100644 --- a/include/scratchcpp/thread.h +++ b/include/scratchcpp/thread.h @@ -20,7 +20,6 @@ class LIBSCRATCHCPP_EXPORT Thread public: Thread(Target *target, IEngine *engine, Script *script); Thread(const Thread &) = delete; - ~Thread(); Target *target() const; IEngine *engine() const; diff --git a/include/scratchcpp/value_functions.h b/include/scratchcpp/value_functions.h index 3849d5dc..f8f983bb 100644 --- a/include/scratchcpp/value_functions.h +++ b/include/scratchcpp/value_functions.h @@ -75,14 +75,14 @@ extern "C" LIBSCRATCHCPP_EXPORT double value_toDouble(const ValueData *v); LIBSCRATCHCPP_EXPORT bool value_toBool(const ValueData *v); LIBSCRATCHCPP_EXPORT void value_toString(const ValueData *v, std::string *dst); - LIBSCRATCHCPP_EXPORT StringPtr *value_toStringPtr(const ValueData *v); + LIBSCRATCHCPP_EXPORT void value_toStringPtr(const ValueData *v, StringPtr *dst); LIBSCRATCHCPP_EXPORT void value_toUtf16(const ValueData *v, std::u16string *dst); LIBSCRATCHCPP_EXPORT Rgb value_toRgba(const ValueData *v); LIBSCRATCHCPP_EXPORT const void *value_toPointer(const ValueData *v); LIBSCRATCHCPP_EXPORT bool value_doubleIsInt(double v); - LIBSCRATCHCPP_EXPORT StringPtr *value_doubleToStringPtr(double v); + LIBSCRATCHCPP_EXPORT void value_doubleToStringPtr(double v, StringPtr *dst); LIBSCRATCHCPP_EXPORT const StringPtr *value_boolToStringPtr(bool v); LIBSCRATCHCPP_EXPORT double value_stringToDouble(const StringPtr *s); LIBSCRATCHCPP_EXPORT double value_stringToDoubleWithCheck(const StringPtr *s, bool *ok); diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index 41411d24..90d5a378 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -561,12 +561,10 @@ extern "C" double looks_backdrop_number(ExecutionContext *ctx) return ctx->engine()->stage()->costumeIndex() + 1; } -extern "C" StringPtr *looks_backdrop_name(ExecutionContext *ctx) +extern "C" void looks_backdrop_name(StringPtr *ret, ExecutionContext *ctx) { const std::string &name = ctx->engine()->stage()->currentCostume()->name(); - StringPtr *ret = string_pool_new(); string_assign_cstring(ret, name.c_str()); - return ret; } extern "C" double looks_costume_number(Target *target) @@ -574,12 +572,10 @@ extern "C" double looks_costume_number(Target *target) return target->costumeIndex() + 1; } -extern "C" StringPtr *looks_costume_name(Target *target) +extern "C" void looks_costume_name(StringPtr *ret, Target *target) { const std::string &name = target->currentCostume()->name(); - StringPtr *ret = string_pool_new(); string_assign_cstring(ret, name.c_str()); - return ret; } extern "C" bool looks_backdrop_promise(ExecutionContext *ctx) diff --git a/src/blocks/sensingblocks.cpp b/src/blocks/sensingblocks.cpp index 0238889b..6f744521 100644 --- a/src/blocks/sensingblocks.cpp +++ b/src/blocks/sensingblocks.cpp @@ -522,11 +522,9 @@ extern "C" void sensing_askandwait(ExecutionContext *ctx, const StringPtr *quest ctx->thread()->setPromise(std::make_shared()); } -extern "C" StringPtr *sensing_answer(ExecutionContext *ctx) +extern "C" void sensing_answer(StringPtr *ret, ExecutionContext *ctx) { - StringPtr *ret = string_pool_new(); string_assign(ret, ctx->engine()->answer()); - return ret; } extern "C" bool sensing_keypressed(ExecutionContext *ctx, const StringPtr *key) @@ -595,12 +593,10 @@ extern "C" double sensing_costume_number_of_target(Target *target) return target->costumeIndex() + 1; } -extern "C" StringPtr *sensing_costume_name_of_target(Target *target) +extern "C" void sensing_costume_name_of_target(StringPtr *ret, Target *target) { const std::string &name = target->currentCostume()->name(); - StringPtr *ret = string_pool_new(); string_assign_cstring(ret, name.c_str()); - return ret; } extern "C" double sensing_size_of_sprite(Sprite *sprite) @@ -659,17 +655,13 @@ extern "C" double sensing_costume_number_of_sprite_with_check(Target *target) return 0.0; } -extern "C" StringPtr *sensing_costume_name_of_sprite_with_check(Target *target) +extern "C" void sensing_costume_name_of_sprite_with_check(StringPtr *ret, Target *target) { - StringPtr *ret = string_pool_new(); - if (target && !target->isStage()) { const std::string &name = target->currentCostume()->name(); string_assign_cstring(ret, name.c_str()); } else string_assign_cstring(ret, "0"); - - return ret; } extern "C" double sensing_size_of_sprite_with_check(Target *target) @@ -690,17 +682,13 @@ extern "C" double sensing_backdrop_number_of_stage_with_check(Target *target) return 0.0; } -extern "C" StringPtr *sensing_backdrop_name_of_stage_with_check(Target *target) +extern "C" void sensing_backdrop_name_of_stage_with_check(StringPtr *ret, Target *target) { - StringPtr *ret = string_pool_new(); - if (target && target->isStage()) { const std::string &name = target->currentCostume()->name(); string_assign_cstring(ret, name.c_str()); } else string_assign_cstring(ret, ""); - - return ret; } extern "C" double sensing_volume_of_target_with_check(Target *target) diff --git a/src/engine/internal/llvm/instructions/control.cpp b/src/engine/internal/llvm/instructions/control.cpp index b41dd292..5afc458c 100644 --- a/src/engine/internal/llvm/instructions/control.cpp +++ b/src/engine/internal/llvm/instructions/control.cpp @@ -119,7 +119,6 @@ LLVMInstruction *Control::buildBeginIf(LLVMInstruction *ins) m_builder.SetInsertPoint(statement.body); m_utils.ifStatements().push_back(statement); - m_utils.pushScopeLevel(); return ins->next; } @@ -135,7 +134,6 @@ LLVMInstruction *Control::buildBeginElse(LLVMInstruction *ins) // Jump to the branch after the if statement assert(!statement.afterIf); statement.afterIf = llvm::BasicBlock::Create(llvmCtx, "", function); - m_utils.freeScopeHeap(); m_builder.CreateBr(statement.afterIf); // Create else branch @@ -157,7 +155,6 @@ LLVMInstruction *Control::buildEndIf(LLVMInstruction *ins) auto &ifStatements = m_utils.ifStatements(); assert(!ifStatements.empty()); LLVMIfStatement &statement = ifStatements.back(); - m_utils.freeScopeHeap(); // Jump to the branch after the if statement if (!statement.afterIf) @@ -176,7 +173,6 @@ LLVMInstruction *Control::buildEndIf(LLVMInstruction *ins) m_builder.SetInsertPoint(statement.afterIf); ifStatements.pop_back(); - m_utils.popScopeLevel(); return ins->next; } @@ -236,7 +232,6 @@ LLVMInstruction *Control::buildBeginRepeatLoop(LLVMInstruction *ins) m_builder.SetInsertPoint(body); m_utils.loops().push_back(loop); - m_utils.pushScopeLevel(); return ins->next; } @@ -274,7 +269,6 @@ LLVMInstruction *Control::buildBeginWhileLoop(LLVMInstruction *ins) // Switch to body branch m_builder.SetInsertPoint(body); - m_utils.pushScopeLevel(); return ins->next; } @@ -300,7 +294,6 @@ LLVMInstruction *Control::buildBeginRepeatUntilLoop(LLVMInstruction *ins) // Switch to body branch m_builder.SetInsertPoint(body); - m_utils.pushScopeLevel(); return ins->next; } @@ -331,14 +324,12 @@ LLVMInstruction *Control::buildEndLoop(LLVMInstruction *ins) } // Jump to the condition branch - m_utils.freeScopeHeap(); m_builder.CreateBr(loop.conditionBranch); // Switch to the branch after the loop m_builder.SetInsertPoint(loop.afterLoop); loops.pop_back(); - m_utils.popScopeLevel(); return ins->next; } @@ -351,7 +342,6 @@ LLVMInstruction *Control::buildStop(LLVMInstruction *ins) LLVMInstruction *Control::buildStopWithoutSync(LLVMInstruction *ins) { - m_utils.freeScopeHeap(); m_builder.CreateBr(m_utils.endBranch()); llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); m_builder.SetInsertPoint(nextBranch); diff --git a/src/engine/internal/llvm/instructions/functions.cpp b/src/engine/internal/llvm/instructions/functions.cpp index a40a8c79..50a37550 100644 --- a/src/engine/internal/llvm/instructions/functions.cpp +++ b/src/engine/internal/llvm/instructions/functions.cpp @@ -32,6 +32,15 @@ LLVMInstruction *Functions::buildFunctionCall(LLVMInstruction *ins) // Variables must be synchronized because the function can read them m_utils.syncVariables(); + // Strings are returned through an output parameter + llvm::Value *stringRet = nullptr; + + if (ins->functionReturnReg && ins->functionReturnReg->type() == Compiler::StaticType::String) { + stringRet = m_utils.addStringAlloca(); + types.push_back(m_utils.getType(Compiler::StaticType::String, false)); + args.push_back(stringRet); + } + // Add execution context arg if (ins->functionCtxArg) { types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_utils.llvmCtx()), 0)); @@ -54,15 +63,14 @@ LLVMInstruction *Functions::buildFunctionCall(LLVMInstruction *ins) llvm::Value *ret = m_builder.CreateCall(m_utils.functions().resolveFunction(ins->functionName, llvm::FunctionType::get(retType, types, false)), args); if (ins->functionReturnReg) { - if (m_utils.isSingleType(ins->functionReturnReg->type())) + if (ins->functionReturnReg->type() == Compiler::StaticType::String) + ins->functionReturnReg->value = stringRet; + else if (m_utils.isSingleType(ins->functionReturnReg->type())) ins->functionReturnReg->value = ret; else { ins->functionReturnReg->value = m_utils.addAlloca(retType); m_builder.CreateStore(ret, ins->functionReturnReg->value); } - - if (ins->functionReturnReg->type() == Compiler::StaticType::String) - m_utils.freeStringLater(ins->functionReturnReg->value); } return ins->next; diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 88a75a4d..77efbee5 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -270,8 +270,8 @@ LLVMInstruction *Lists::buildGetListContents(LLVMInstruction *ins) { assert(ins->args.size() == 0); const LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); - llvm::Value *ptr = m_builder.CreateCall(m_utils.functions().resolve_list_to_string(), listPtr.ptr); - m_utils.freeStringLater(ptr); + llvm::Value *ptr = m_utils.addStringAlloca(); + m_builder.CreateCall(m_utils.functions().resolve_list_to_string(), { listPtr.ptr, ptr }); ins->functionReturnReg->value = ptr; return ins->next; diff --git a/src/engine/internal/llvm/instructions/procedures.cpp b/src/engine/internal/llvm/instructions/procedures.cpp index f6022458..83577f82 100644 --- a/src/engine/internal/llvm/instructions/procedures.cpp +++ b/src/engine/internal/llvm/instructions/procedures.cpp @@ -38,7 +38,6 @@ LLVMInstruction *Procedures::buildCallProcedure(LLVMInstruction *ins) assert(ins->procedurePrototype); assert(ins->args.size() == ins->procedurePrototype->argumentTypes().size()); - m_utils.freeScopeHeap(); m_utils.syncVariables(); std::string name = m_utils.scriptFunctionName(ins->procedurePrototype); diff --git a/src/engine/internal/llvm/instructions/string.cpp b/src/engine/internal/llvm/instructions/string.cpp index 8cf7a0da..77120976 100644 --- a/src/engine/internal/llvm/instructions/string.cpp +++ b/src/engine/internal/llvm/instructions/string.cpp @@ -44,9 +44,8 @@ LLVMInstruction *String::buildStringConcat(LLVMInstruction *ins) llvm::StructType *stringPtrType = m_utils.compilerCtx()->stringPtrType(); llvm::Function *memcpyFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::memcpy_inline, { charPointerType, charPointerType, m_builder.getInt64Ty() }); - // StringPtr *result = string_pool_new(true) - llvm::Value *result = m_builder.CreateCall(m_utils.functions().resolve_string_pool_new(), m_builder.getInt1(true)); - m_utils.freeStringLater(result); + // StringPtr *result = (allocated string) + llvm::Value *result = m_utils.addStringAlloca(); // result->size = string1->size + string2->size llvm::Value *sizeField1 = m_builder.CreateStructGEP(stringPtrType, str1, 1); @@ -100,8 +99,7 @@ LLVMInstruction *String::buildStringChar(LLVMInstruction *ins) llvm::Value *charPtr = m_builder.CreateGEP(m_builder.getInt16Ty(), data, index); // Allocate string - llvm::Value *result = m_builder.CreateCall(m_utils.functions().resolve_string_pool_new(), m_builder.getInt1(true)); - m_utils.freeStringLater(result); + llvm::Value *result = m_utils.addStringAlloca(); m_builder.CreateCall(m_utils.functions().resolve_string_alloc(), { result, m_builder.getInt64(1) }); // size 1 to avoid branching // Get result data ptr diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index c8a70426..f871e825 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -34,7 +34,8 @@ LLVMBuildUtils::LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &buil m_builder(builder), m_functions(ctx, &builder), m_target(ctx->target()), - m_codeType(codeType) + m_codeType(codeType), + m_functionId(ctx->getNextFunctionId()) { initTypes(); createVariableMap(); @@ -47,6 +48,8 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro m_procedurePrototype = procedurePrototype; m_warp = warp; + m_functionIdValue = llvm::ConstantInt::get(m_functionIdType, m_functionId); + m_executionContextPtr = m_function->getArg(0); m_targetPtr = m_function->getArg(1); m_targetVariables = m_function->getArg(2); @@ -56,9 +59,6 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro if (m_procedurePrototype && m_warp) m_function->addFnAttr(llvm::Attribute::InlineHint); - m_stringHeap.clear(); - pushScopeLevel(); - // Init coroutine if (!m_warp) m_coroutine = std::make_unique(m_ctx->module(), &m_builder, m_function); @@ -74,6 +74,19 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro reg->intValue = llvm::ConstantInt::get(m_builder.getInt64Ty(), reg->constValue().toDouble(), true); } + // Create string allocation block + m_stringAllocaBlock = llvm::BasicBlock::Create(m_llvmCtx, "stringAlloca", m_function); + m_builder.CreateBr(m_stringAllocaBlock); + + // Get string array + m_builder.SetInsertPoint(m_stringAllocaBlock); + m_stringArray = m_builder.CreateCall(m_functions.resolve_llvm_get_string_array(), { m_executionContextPtr, m_functionIdValue }); + // NOTE: This block is terminated later + + // Create next block + m_stringAllocaNextBlock = llvm::BasicBlock::Create(m_llvmCtx, "entry.next", m_function); + m_builder.SetInsertPoint(m_stringAllocaNextBlock); + // Create variable pointers for (auto &[var, varPtr] : m_variablePtrs) { llvm::Value *ptr = getVariablePtr(m_targetVariables, var); @@ -123,8 +136,14 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro void LLVMBuildUtils::end(LLVMInstruction *lastInstruction, LLVMRegister *lastConstant) { - assert(m_stringHeap.size() == 1); - freeScopeHeap(); + // Terminate string allocation block + if (m_stringCount == 0) + m_stringAllocaBlock->erase(m_stringAllocaBlock->begin(), m_stringAllocaBlock->end()); + + llvm::BasicBlock *previousBlock = m_builder.GetInsertBlock(); + m_builder.SetInsertPoint(m_stringAllocaBlock); + m_builder.CreateBr(m_stringAllocaNextBlock); + m_builder.SetInsertPoint(previousBlock); // Sync llvm::BasicBlock *syncBranch = llvm::BasicBlock::Create(m_llvmCtx, "sync", m_function); @@ -258,6 +277,16 @@ llvm::FunctionType *LLVMBuildUtils::scriptFunctionType(BlockPrototype *procedure return llvm::FunctionType::get(retType, argTypes, false); } +function_id_t LLVMBuildUtils::scriptFunctionId() const +{ + return m_functionId; +} + +size_t LLVMBuildUtils::stringCount() const +{ + return m_stringCount; +} + llvm::BasicBlock *LLVMBuildUtils::endBranch() const { return m_endBranch; @@ -384,41 +413,6 @@ void LLVMBuildUtils::reloadLists() } } -void LLVMBuildUtils::pushScopeLevel() -{ - m_stringHeap.push_back({}); -} - -void LLVMBuildUtils::popScopeLevel() -{ - freeScopeHeap(); - m_stringHeap.pop_back(); -} - -void LLVMBuildUtils::freeStringLater(llvm::Value *value) -{ - assert(!m_stringHeap.empty()); - - if (m_stringHeap.empty()) - return; - - m_stringHeap.back().push_back(value); -} - -void LLVMBuildUtils::freeScopeHeap() -{ - if (m_stringHeap.empty()) - return; - - // Free strings in current scope - auto &heap = m_stringHeap.back(); - - for (llvm::Value *ptr : heap) - m_builder.CreateCall(m_functions.resolve_string_pool_free(), { ptr }); - - heap.clear(); -} - std::vector &LLVMBuildUtils::ifStatements() { return m_ifStatements; @@ -468,6 +462,20 @@ llvm::Value *LLVMBuildUtils::addAlloca(llvm::Type *type) return ret; } +llvm::Value *LLVMBuildUtils::addStringAlloca() +{ + // All temporary strings are allocated before the script is called + llvm::BasicBlock *block = m_builder.GetInsertBlock(); + m_builder.SetInsertPoint(m_stringAllocaBlock); + + llvm::Value *index = m_builder.getInt64(m_stringCount++); + llvm::Value *elementPtr = m_builder.CreateGEP(m_stringPtrType->getPointerTo(), m_stringArray, index); + llvm::Value *ret = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), elementPtr, "localString"); + + m_builder.SetInsertPoint(block); + return ret; +} + llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType) { if (reg->isConst()) { @@ -629,7 +637,8 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t llvm::Value *intCast = m_builder.CreateSIToFP(reg->intValue, m_builder.getDoubleTy()); llvm::Value *value = m_builder.CreateSelect(reg->isInt, intCast, doubleValue); - llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); + llvm::Value *numberResult = addStringAlloca(); + m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), { value, numberResult }); m_builder.CreateBr(mergeBlock); results.push_back({ numberBlock, numberResult }); } @@ -639,13 +648,10 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); - // Since the value is deallocated later, we need to create a copy m_builder.SetInsertPoint(boolBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); llvm::Value *value = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); - llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); - m_builder.CreateCall(m_functions.resolve_string_assign(), { boolResult, stringPtr }); + llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); m_builder.CreateBr(mergeBlock); results.push_back({ boolBlock, boolResult }); } @@ -657,9 +663,7 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t m_builder.SetInsertPoint(stringBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); - m_builder.CreateCall(m_functions.resolve_string_assign(), { stringResult, stringPtr }); + llvm::Value *stringResult = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); m_builder.CreateBr(mergeBlock); results.push_back({ stringBlock, stringResult }); } @@ -706,7 +710,11 @@ llvm::Type *LLVMBuildUtils::getType(Compiler::StaticType type, bool isReturnType return m_builder.getInt1Ty(); case Compiler::StaticType::String: - return m_stringPtrType->getPointerTo(); + // Strings are returned through an output parameter + if (isReturnType) + return m_builder.getVoidTy(); + else + return m_stringPtrType->getPointerTo(); case Compiler::StaticType::Pointer: return m_builder.getVoidTy()->getPointerTo(); @@ -807,7 +815,7 @@ void LLVMBuildUtils::createValueStore( // Create a new string, change type and assign llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(false)); + llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new()); m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), destTypePtr); m_builder.CreateStore(destStringPtr, ptr); @@ -871,7 +879,7 @@ void LLVMBuildUtils::createValueStore( // Create a new string, change type and assign llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(false)); + llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new()); m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), destTypePtr); m_builder.CreateStore(destStringPtr, ptr); @@ -1307,8 +1315,11 @@ llvm::Value *LLVMBuildUtils::createStringComparison(LLVMRegister *arg1, LLVMRegi if (arg1->isConst() && arg2->isConst()) { // If both operands are constant, perform the comparison at compile time - StringPtr *str1 = value_toStringPtr(&arg1->constValue().data()); - StringPtr *str2 = value_toStringPtr(&arg2->constValue().data()); + StringPtr *str1 = string_pool_new(); + StringPtr *str2 = string_pool_new(); + value_toStringPtr(&arg1->constValue().data(), str1); + value_toStringPtr(&arg2->constValue().data(), str2); + bool result; if (caseSensitive) @@ -1364,6 +1375,7 @@ void LLVMBuildUtils::initTypes() { m_valueDataType = m_ctx->valueDataType(); m_stringPtrType = m_ctx->stringPtrType(); + m_functionIdType = m_ctx->functionIdType(); } void LLVMBuildUtils::createVariableMap() @@ -1496,8 +1508,8 @@ llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticTyp // Convert double/int to string llvm::Value *intCast = m_builder.CreateSIToFP(reg->intValue, m_builder.getDoubleTy()); llvm::Value *doubleValue = m_builder.CreateSelect(reg->isInt, intCast, reg->value); - llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), doubleValue); - freeStringLater(ptr); + llvm::Value *ptr = addStringAlloca(); + m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), { doubleValue, ptr }); return ptr; } @@ -1791,9 +1803,9 @@ llvm::Value *LLVMBuildUtils::createNumberAndStringComparison(LLVMRegister *arg1, // String comparison m_builder.SetInsertPoint(stringBlock); - llvm::Value *stringValue = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), { value1 }); + llvm::Value *stringValue = addStringAlloca(); + m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), { value1, stringValue }); llvm::Value *cmp = m_builder.CreateCall(m_functions.resolve_string_compare_case_insensitive(), { stringValue, value2 }); - m_builder.CreateCall(m_functions.resolve_string_pool_free(), { stringValue }); // free the string immediately llvm::Value *zero = llvm::ConstantInt::get(m_builder.getInt32Ty(), 0, true); llvm::Value *stringCmp; diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index e6192ec9..cd6ae020 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -11,6 +11,7 @@ #include "llvmcoroutine.h" #include "llvmifstatement.h" #include "llvmloop.h" +#include "llvmcompilercontext.h" namespace libscratchcpp { @@ -49,6 +50,10 @@ class LLVMBuildUtils std::string scriptFunctionName(BlockPrototype *procedurePrototype); llvm::FunctionType *scriptFunctionType(BlockPrototype *procedurePrototype); + function_id_t scriptFunctionId() const; + + size_t stringCount() const; + llvm::BasicBlock *endBranch() const; BlockPrototype *procedurePrototype() const; @@ -74,12 +79,6 @@ class LLVMBuildUtils void reloadVariables(); void reloadLists(); - void pushScopeLevel(); - void popScopeLevel(); - - void freeStringLater(llvm::Value *value); - void freeScopeHeap(); - std::vector &ifStatements(); std::vector &loops(); @@ -89,6 +88,8 @@ class LLVMBuildUtils static bool isSingleType(Compiler::StaticType type); llvm::Value *addAlloca(llvm::Type *type); + llvm::Value *addStringAlloca(); + llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType = NumberType::Double); llvm::Type *getType(Compiler::StaticType type, bool isReturnType); llvm::Value *isNaN(llvm::Value *num); @@ -151,11 +152,14 @@ class LLVMBuildUtils LLVMFunctions m_functions; Target *m_target = nullptr; llvm::Function *m_function = nullptr; + function_id_t m_functionId = 0; + llvm::Value *m_functionIdValue = nullptr; llvm::BasicBlock *m_endBranch = nullptr; llvm::StructType *m_valueDataType = nullptr; llvm::StructType *m_stringPtrType = nullptr; + llvm::Type *m_functionIdType = nullptr; BlockPrototype *m_procedurePrototype = nullptr; bool m_warp = false; @@ -177,10 +181,13 @@ class LLVMBuildUtils std::unordered_map m_targetListMap; std::unordered_map m_listPtrs; - std::vector> m_stringHeap; // scopes - std::vector m_ifStatements; std::vector m_loops; + + llvm::BasicBlock *m_stringAllocaBlock = nullptr; + llvm::BasicBlock *m_stringAllocaNextBlock = nullptr; + llvm::Value *m_stringArray = nullptr; + size_t m_stringCount = 0; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index 07dcf003..45662eb8 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -91,7 +91,7 @@ std::shared_ptr LLVMCodeBuilder::build() m_utils.end(m_instructions.empty() ? nullptr : m_instructions.last(), m_lastConstValue); verifyFunction(m_function); - return std::make_shared(m_ctx, m_function->getName().str(), m_ctx->coroutineResumeFunction()->getName().str(), m_codeType); + return std::make_shared(m_ctx, m_utils.scriptFunctionId(), m_function->getName().str(), m_ctx->coroutineResumeFunction()->getName().str(), m_utils.stringCount(), m_codeType); } CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) diff --git a/src/engine/internal/llvm/llvmcompilercontext.cpp b/src/engine/internal/llvm/llvmcompilercontext.cpp index 5ec8c6b4..268bdfaa 100644 --- a/src/engine/internal/llvm/llvmcompilercontext.cpp +++ b/src/engine/internal/llvm/llvmcompilercontext.cpp @@ -12,6 +12,7 @@ #include "llvmcompilercontext.h" #include "llvmcoroutine.h" #include "llvmtypes.h" +#include "llvmexecutablecode.h" using namespace libscratchcpp; @@ -30,6 +31,7 @@ LLVMCompilerContext::LLVMCompilerContext(IEngine *engine, Target *target) : // Create types m_valueDataType = LLVMTypes::createValueDataType(*m_llvmCtx); m_stringPtrType = LLVMTypes::createStringPtrType(*m_llvmCtx); + m_functionIdType = LLVMTypes::createFunctionIdType(*m_llvmCtx); if (!m_jit) { llvm::errs() << "error: failed to create JIT: " << toString(m_jit.takeError()) << "\n"; @@ -52,6 +54,22 @@ llvm::Module *LLVMCompilerContext::module() return m_modulePtr; } +void LLVMCompilerContext::addCode(LLVMExecutableCode *code) +{ + assert(m_codeMap.find(code->functionId()) == m_codeMap.cend()); + m_codeMap[code->functionId()] = code; +} + +const std::unordered_map &LLVMCompilerContext::codeMap() const +{ + return m_codeMap; +} + +function_id_t LLVMCompilerContext::getNextFunctionId() +{ + return m_nextFunctionId++; +} + void LLVMCompilerContext::initJit() { if (m_jitInitialized) { @@ -135,6 +153,11 @@ llvm::StructType *LLVMCompilerContext::stringPtrType() const return m_stringPtrType; } +llvm::Type *LLVMCompilerContext::functionIdType() const +{ + return m_functionIdType; +} + void LLVMCompilerContext::initTarget() { llvm::InitializeNativeTarget(); diff --git a/src/engine/internal/llvm/llvmcompilercontext.h b/src/engine/internal/llvm/llvmcompilercontext.h index ce606980..2c547d2f 100644 --- a/src/engine/internal/llvm/llvmcompilercontext.h +++ b/src/engine/internal/llvm/llvmcompilercontext.h @@ -16,6 +16,9 @@ class ValueData; class List; class LLVMExecutableCode; +// NOTE: Change this in LLVMTypes as well +using function_id_t = unsigned int; + class LLVMCompilerContext : public CompilerContext { public: @@ -26,6 +29,11 @@ class LLVMCompilerContext : public CompilerContext llvm::LLVMContext *llvmCtx(); llvm::Module *module(); + void addCode(LLVMExecutableCode *code); + const std::unordered_map &codeMap() const; + + function_id_t getNextFunctionId(); + void initJit(); bool jitInitialized() const; @@ -34,6 +42,7 @@ class LLVMCompilerContext : public CompilerContext llvm::StructType *valueDataType() const; llvm::StructType *stringPtrType() const; + llvm::Type *functionIdType() const; template T lookupFunction(const std::string &name) @@ -69,6 +78,9 @@ class LLVMCompilerContext : public CompilerContext llvm::Expected> m_jit; bool m_jitInitialized = false; + function_id_t m_nextFunctionId = 0; + std::unordered_map m_codeMap; + llvm::Function *m_llvmCoroResumeFunction = nullptr; llvm::Function *m_llvmCoroDestroyFunction = nullptr; @@ -76,6 +88,7 @@ class LLVMCompilerContext : public CompilerContext llvm::StructType *m_valueDataType = nullptr; llvm::StructType *m_stringPtrType = nullptr; + llvm::Type *m_functionIdType = nullptr; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmexecutablecode.cpp b/src/engine/internal/llvm/llvmexecutablecode.cpp index 2cf3b486..1acb79f4 100644 --- a/src/engine/internal/llvm/llvmexecutablecode.cpp +++ b/src/engine/internal/llvm/llvmexecutablecode.cpp @@ -16,10 +16,18 @@ using namespace libscratchcpp; -LLVMExecutableCode::LLVMExecutableCode(LLVMCompilerContext *ctx, const std::string &mainFunctionName, const std::string &resumeFunctionName, Compiler::CodeType codeType) : +LLVMExecutableCode::LLVMExecutableCode( + LLVMCompilerContext *ctx, + function_id_t functionId, + const std::string &mainFunctionName, + const std::string &resumeFunctionName, + size_t stringCount, + Compiler::CodeType codeType) : m_ctx(ctx), + m_functionId(functionId), m_mainFunctionName(mainFunctionName), m_resumeFunctionName(resumeFunctionName), + m_stringCount(stringCount), m_codeType(codeType) { assert(m_ctx); @@ -28,6 +36,8 @@ LLVMExecutableCode::LLVMExecutableCode(LLVMCompilerContext *ctx, const std::stri std::cerr << "error: cannot create LLVM code after JIT compiler had been initialized" << std::endl; assert(false); } + + m_ctx->addCode(this); } void LLVMExecutableCode::run(ExecutionContext *context) @@ -135,6 +145,16 @@ std::shared_ptr LLVMExecutableCode::createExecutionContext(Thr return std::make_shared(m_ctx, thread); } +function_id_t LLVMExecutableCode::functionId() const +{ + return m_functionId; +} + +size_t LLVMExecutableCode::stringCount() const +{ + return m_stringCount; +} + LLVMExecutionContext *LLVMExecutableCode::getContext(ExecutionContext *context) { assert(dynamic_cast(context)); diff --git a/src/engine/internal/llvm/llvmexecutablecode.h b/src/engine/internal/llvm/llvmexecutablecode.h index a4b150b1..2b18b445 100644 --- a/src/engine/internal/llvm/llvmexecutablecode.h +++ b/src/engine/internal/llvm/llvmexecutablecode.h @@ -17,7 +17,13 @@ class LLVMExecutionContext; class LLVMExecutableCode : public ExecutableCode { public: - LLVMExecutableCode(LLVMCompilerContext *ctx, const std::string &mainFunctionName, const std::string &resumeFunctionName, Compiler::CodeType codeType); + LLVMExecutableCode( + LLVMCompilerContext *ctx, + function_id_t functionId, + const std::string &mainFunctionName, + const std::string &resumeFunctionName, + size_t stringCount, + Compiler::CodeType codeType); void run(ExecutionContext *context) override; ValueData runReporter(ExecutionContext *context) override; @@ -29,6 +35,9 @@ class LLVMExecutableCode : public ExecutableCode std::shared_ptr createExecutionContext(Thread *thread) const override; + function_id_t functionId() const; + size_t stringCount() const; + private: using MainFunctionType = void *(*)(ExecutionContext *, Target *, ValueData **, List **); using ReporterFunctionType = ValueData (*)(ExecutionContext *, Target *, ValueData **, List **); @@ -38,9 +47,11 @@ class LLVMExecutableCode : public ExecutableCode static LLVMExecutionContext *getContext(ExecutionContext *context); LLVMCompilerContext *m_ctx = nullptr; + function_id_t m_functionId = 0; std::string m_mainFunctionName; std::string m_predicateFunctionName; std::string m_resumeFunctionName; + size_t m_stringCount = 0; Compiler::CodeType m_codeType; mutable std::variant m_mainFunction; diff --git a/src/engine/internal/llvm/llvmexecutioncontext.cpp b/src/engine/internal/llvm/llvmexecutioncontext.cpp index d7d24787..40f35990 100644 --- a/src/engine/internal/llvm/llvmexecutioncontext.cpp +++ b/src/engine/internal/llvm/llvmexecutioncontext.cpp @@ -1,7 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 +#include + #include "llvmexecutioncontext.h" -#include "llvmcompilercontext.h" +#include "llvmexecutablecode.h" using namespace libscratchcpp; @@ -9,6 +11,25 @@ LLVMExecutionContext::LLVMExecutionContext(LLVMCompilerContext *compilerCtx, Thr ExecutionContext(thread), m_compilerCtx(compilerCtx) { + // Allocate strings for all scripts in the target + // TODO: Allocate strings for this script and procedures it calls + const auto &codeMap = compilerCtx->codeMap(); + + for (const auto &[functionId, code] : codeMap) { + size_t count = code->stringCount(); + + if (count > 0) { + m_stringVectors[functionId] = {}; + + auto &strings = m_stringVectors[functionId]; + strings.reserve(count); + + for (size_t i = 0; i < count; i++) + strings.push_back(string_pool_new()); + + m_stringArrays[functionId] = strings.data(); + } + } } LLVMExecutionContext::~LLVMExecutionContext() @@ -20,6 +41,12 @@ LLVMExecutionContext::~LLVMExecutionContext() } else assert(false); } + + // Deallocate strings + for (const auto &[functionId, strings] : m_stringVectors) { + for (StringPtr *str : strings) + string_pool_free(str); + } } void *LLVMExecutionContext::coroutineHandle() const diff --git a/src/engine/internal/llvm/llvmexecutioncontext.h b/src/engine/internal/llvm/llvmexecutioncontext.h index 854716de..8201af3a 100644 --- a/src/engine/internal/llvm/llvmexecutioncontext.h +++ b/src/engine/internal/llvm/llvmexecutioncontext.h @@ -4,10 +4,12 @@ #include +#include "llvmcompilercontext.h" + namespace libscratchcpp { -class LLVMCompilerContext; +struct StringPtr; class LLVMExecutionContext : public ExecutionContext { @@ -21,10 +23,19 @@ class LLVMExecutionContext : public ExecutionContext bool finished() const; void setFinished(bool newFinished); + inline StringPtr **getStringArray(function_id_t functionId) + { + assert(m_stringArrays.find(functionId) != m_stringArrays.cend()); + return m_stringArrays[functionId]; + } + private: LLVMCompilerContext *m_compilerCtx = nullptr; void *m_coroutineHandle = nullptr; bool m_finished = false; + + std::unordered_map> m_stringVectors; + std::unordered_map m_stringArrays; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmfunctions.cpp b/src/engine/internal/llvm/llvmfunctions.cpp index 36ebcc83..3c9b518d 100644 --- a/src/engine/internal/llvm/llvmfunctions.cpp +++ b/src/engine/internal/llvm/llvmfunctions.cpp @@ -1,11 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include #include #include "llvmfunctions.h" #include "llvmcompilercontext.h" +#include "llvmexecutioncontext.h" using namespace libscratchcpp; @@ -30,6 +30,11 @@ extern "C" { return ctx->rng()->randint(from, to); } + + StringPtr **llvm_get_string_array(ExecutionContext *ctx, function_id_t functionId) + { + return static_cast(ctx)->getStringArray(functionId); + } } LLVMFunctions::LLVMFunctions(LLVMCompilerContext *ctx, llvm::IRBuilder<> *builder) : @@ -39,6 +44,7 @@ LLVMFunctions::LLVMFunctions(LLVMCompilerContext *ctx, llvm::IRBuilder<> *builde // Custom types m_stringPtrType = m_ctx->stringPtrType(); m_valueDataType = m_ctx->valueDataType(); + m_functionIdType = m_ctx->functionIdType(); } llvm::FunctionCallee LLVMFunctions::resolveFunction(const std::string name, llvm::FunctionType *type) @@ -104,14 +110,18 @@ llvm::FunctionCallee LLVMFunctions::resolve_value_toBool() llvm::FunctionCallee LLVMFunctions::resolve_value_toStringPtr() { - // NOTE: This function can't be marked read-only because it allocates on the heap - return resolveFunction("value_toStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_valueDataType->getPointerTo(), false)); + llvm::FunctionCallee callee = resolveFunction("value_toStringPtr", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_stringPtrType->getPointerTo() }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; } llvm::FunctionCallee LLVMFunctions::resolve_value_doubleToStringPtr() { - // NOTE: This function can't be marked read-only because it allocates on the heap - return resolveFunction("value_doubleToStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_builder->getDoubleTy(), false)); + llvm::FunctionCallee callee = resolveFunction("value_doubleToStringPtr", llvm::FunctionType::get(m_builder->getVoidTy(), { m_builder->getDoubleTy(), m_stringPtrType->getPointerTo() }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; } llvm::FunctionCallee LLVMFunctions::resolve_value_boolToStringPtr() @@ -230,7 +240,7 @@ llvm::FunctionCallee LLVMFunctions::resolve_list_alloc_size_ptr() llvm::FunctionCallee LLVMFunctions::resolve_list_to_string() { llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); - llvm::FunctionCallee callee = resolveFunction("list_to_string", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), { pointerType }, false)); + llvm::FunctionCallee callee = resolveFunction("list_to_string", llvm::FunctionType::get(m_builder->getVoidTy(), { pointerType, m_stringPtrType->getPointerTo() }, false)); llvm::Function *func = llvm::cast(callee.getCallee()); func->addFnAttr(llvm::Attribute::ReadOnly); return callee; @@ -261,9 +271,20 @@ llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_bool() return resolveFunction("llvm_random_bool", llvm::FunctionType::get(m_builder->getDoubleTy(), { pointerType, m_builder->getInt1Ty(), m_builder->getInt1Ty() }, false)); } +llvm::FunctionCallee LLVMFunctions::resolve_llvm_get_string_array() +{ + llvm::Type *stringPtr = m_stringPtrType->getPointerTo(); + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + + llvm::FunctionCallee callee = resolveFunction("llvm_get_string_array", llvm::FunctionType::get(stringPtr->getPointerTo(), { pointerType, m_functionIdType }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + llvm::FunctionCallee LLVMFunctions::resolve_string_pool_new() { - return resolveFunction("string_pool_new", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), { m_builder->getInt1Ty() }, false)); + return resolveFunction("string_pool_new", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), false)); } llvm::FunctionCallee LLVMFunctions::resolve_string_pool_free() diff --git a/src/engine/internal/llvm/llvmfunctions.h b/src/engine/internal/llvm/llvmfunctions.h index 13968175..3aba6691 100644 --- a/src/engine/internal/llvm/llvmfunctions.h +++ b/src/engine/internal/llvm/llvmfunctions.h @@ -47,6 +47,7 @@ class LLVMFunctions llvm::FunctionCallee resolve_llvm_random_double(); llvm::FunctionCallee resolve_llvm_random_int64(); llvm::FunctionCallee resolve_llvm_random_bool(); + llvm::FunctionCallee resolve_llvm_get_string_array(); llvm::FunctionCallee resolve_string_pool_new(); llvm::FunctionCallee resolve_string_pool_free(); llvm::FunctionCallee resolve_string_alloc(); @@ -60,6 +61,7 @@ class LLVMFunctions llvm::StructType *m_stringPtrType = nullptr; llvm::StructType *m_valueDataType = nullptr; + llvm::Type *m_functionIdType = nullptr; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmtypes.cpp b/src/engine/internal/llvm/llvmtypes.cpp index 8d46440a..db78789e 100644 --- a/src/engine/internal/llvm/llvmtypes.cpp +++ b/src/engine/internal/llvm/llvmtypes.cpp @@ -30,3 +30,8 @@ llvm::StructType *LLVMTypes::createStringPtrType(llvm::LLVMContext &ctx) return ret; } + +llvm::Type *LLVMTypes::createFunctionIdType(llvm::LLVMContext &ctx) +{ + return llvm::Type::getInt32Ty(ctx); +} diff --git a/src/engine/internal/llvm/llvmtypes.h b/src/engine/internal/llvm/llvmtypes.h index cbfefa60..a0ae08b0 100644 --- a/src/engine/internal/llvm/llvmtypes.h +++ b/src/engine/internal/llvm/llvmtypes.h @@ -19,6 +19,7 @@ class LLVMTypes public: static llvm::StructType *createValueDataType(llvm::LLVMContext &ctx); static llvm::StructType *createStringPtrType(llvm::LLVMContext &ctx); + static llvm::Type *createFunctionIdType(llvm::LLVMContext &ctx); }; } // namespace libscratchcpp diff --git a/src/engine/thread.cpp b/src/engine/thread.cpp index 6279a60d..3ab120c5 100644 --- a/src/engine/thread.cpp +++ b/src/engine/thread.cpp @@ -6,7 +6,6 @@ #include #include "thread_p.h" -#include "scratch/string_pool_p.h" using namespace libscratchcpp; @@ -14,8 +13,6 @@ using namespace libscratchcpp; Thread::Thread(Target *target, IEngine *engine, Script *script) : impl(spimpl::make_unique_impl(target, engine, script)) { - string_pool_add_thread(this); - if (impl->script) { impl->code = impl->script->code(); impl->hatPredicateCode = impl->script->hatPredicateCode(); @@ -28,12 +25,6 @@ Thread::Thread(Target *target, IEngine *engine, Script *script) : } } -/*! Destroys Thread. */ -Thread::~Thread() -{ - string_pool_remove_thread(this); -} - /*! Returns the Target of the script. */ Target *Thread::target() const { @@ -55,17 +46,13 @@ Script *Thread::script() const /*! Runs the script until it finishes or yields. */ void Thread::run() { - string_pool_set_thread(this); impl->code->run(impl->executionContext.get()); - string_pool_set_thread(nullptr); } /*! Runs the reporter and returns its return value. */ ValueData Thread::runReporter() { - string_pool_set_thread(this); ValueData ret = impl->code->runReporter(impl->executionContext.get()); - string_pool_set_thread(nullptr); return ret; } @@ -75,9 +62,7 @@ bool Thread::runPredicate() if (!impl->hatPredicateCode) return false; - string_pool_set_thread(this); const bool ret = impl->hatPredicateCode->runPredicate(impl->hatPredicateExecutionContext.get()); - string_pool_set_thread(nullptr); return ret; } diff --git a/src/scratch/list.cpp b/src/scratch/list.cpp index a27d5ac9..4878502a 100644 --- a/src/scratch/list.cpp +++ b/src/scratch/list.cpp @@ -61,7 +61,8 @@ void List::setMonitor(Monitor *monitor) /*! Same as the other method, but returns the result as std::string. */ std::string List::toString() const { - StringPtr *str = toStringPtr(); + StringPtr *str = string_pool_new(); + toStringPtr(str); std::string ret = utf8::utf16to8(std::u16string(str->data)); string_pool_free(str); return ret; diff --git a/src/scratch/list_functions.cpp b/src/scratch/list_functions.cpp index 5ef5e681..44586f62 100644 --- a/src/scratch/list_functions.cpp +++ b/src/scratch/list_functions.cpp @@ -58,8 +58,8 @@ extern "C" return list->size(); } - StringPtr *list_to_string(List *list) + void list_to_string(List *list, StringPtr *dst) { - return list->toStringPtr(); + list->toStringPtr(dst); } } diff --git a/src/scratch/list_functions.h b/src/scratch/list_functions.h index f6837419..86abdf0a 100644 --- a/src/scratch/list_functions.h +++ b/src/scratch/list_functions.h @@ -26,7 +26,7 @@ extern "C" const size_t *list_alloc_size_ptr(List *list); size_t list_size(List *list); - StringPtr *list_to_string(List *list); + void list_to_string(List *list, StringPtr *dst); } } // namespace libscratchcpp diff --git a/src/scratch/string_pool.cpp b/src/scratch/string_pool.cpp index 7e82245f..ba2554d9 100644 --- a/src/scratch/string_pool.cpp +++ b/src/scratch/string_pool.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -13,22 +12,16 @@ namespace libscratchcpp { -class Thread; - static std::unordered_set> strings; static std::multimap freeStrings; // sorted by allocated space -static std::unordered_map> threadStrings; -static Thread *currentThread = nullptr; - extern "C" { /*! * Use this instead of dynamically allocating StringPtr. - * \param[in] internal Whether the string should be deallocated when the current thread is removed (it's only used within the thread). * \note The returned string is uninitialized. Use e. g. string_assign_cstring() to initialize it. */ - StringPtr *string_pool_new(bool internal) + StringPtr *string_pool_new() { if (freeStrings.empty()) { auto str = std::make_unique(); @@ -36,9 +29,6 @@ extern "C" assert(strings.find(str) == strings.cend()); strings.insert(std::move(str)); - if (internal && currentThread) - threadStrings[currentThread].insert(ptr); - return ptr; } @@ -47,51 +37,16 @@ extern "C" StringPtr *ptr = last->second; freeStrings.erase(last); - if (internal && currentThread) - threadStrings[currentThread].insert(ptr); - return ptr; } /*! Invalidates the given StringPtr so that it can be used for new strings later. */ void string_pool_free(StringPtr *str) { - if (currentThread) - threadStrings[currentThread].erase(str); - assert(std::find_if(freeStrings.begin(), freeStrings.end(), [str](const std::pair &p) { return p.second == str; }) == freeStrings.end()); assert(std::find_if(strings.begin(), strings.end(), [str](const std::unique_ptr &p) { return p.get() == str; }) != strings.end()); freeStrings.insert(std::pair(str->allocatedSize, str)); } } -/* string_pool_p.h */ - -void string_pool_add_thread(Thread *thread) -{ - // Start capturing allocated strings in the thread - assert(threadStrings.find(thread) == threadStrings.cend()); - threadStrings[thread] = {}; -} - -void string_pool_remove_thread(Thread *thread) -{ - // Free all strings in the thread (garbage collection) - assert(threadStrings.find(thread) != threadStrings.cend()); - auto &strings = threadStrings[thread]; - - for (StringPtr *str : strings) - freeStrings.insert(std::pair(str->allocatedSize, str)); - - threadStrings.erase(thread); - - if (currentThread == thread) - currentThread = nullptr; -} - -void string_pool_set_thread(Thread *thread) -{ - currentThread = thread; -} - } // namespace libscratchcpp diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp index 64a08300..1d9b9921 100644 --- a/src/scratch/value_functions.cpp +++ b/src/scratch/value_functions.cpp @@ -281,44 +281,31 @@ extern "C" /*! Writes the string representation of the given value to dst. */ void value_toString(const ValueData *v, std::string *dst) { - StringPtr *str = value_toStringPtr(v); + StringPtr *str = string_pool_new(); + value_toStringPtr(v, str); dst->assign(utf8::utf16to8(std::u16string(str->data))); string_pool_free(str); } - /*! - * Returns the string representation of the given value. - * \note It is the caller's responsibility to free allocated memory. - */ - StringPtr *value_toStringPtr(const ValueData *v) + /*! Writes the string representation of the given value to dst. */ + void value_toStringPtr(const ValueData *v, StringPtr *dst) { - if (v->type == ValueType::String) { - StringPtr *ret = string_pool_new(); - string_assign(ret, v->stringValue); - return ret; - } else if (v->type == ValueType::Number) - return value_doubleToStringPtr(v->numberValue); + if (v->type == ValueType::String) + string_assign(dst, v->stringValue); + else if (v->type == ValueType::Number) + value_doubleToStringPtr(v->numberValue, dst); else if (v->type == ValueType::Bool) { - if (v->boolValue) { - StringPtr *ret = string_pool_new(); - string_assign(ret, &TRUE_STR); - return ret; - } else { - StringPtr *ret = string_pool_new(); - string_assign(ret, &FALSE_STR); - return ret; - } - } else { - StringPtr *ret = string_pool_new(); - string_assign(ret, &ZERO_STR); - return ret; - } + const StringPtr *boolStr = value_boolToStringPtr(v->boolValue); + string_assign(dst, boolStr); + } else + string_assign(dst, &ZERO_STR); } /*! Writes the UTF-16 representation of the given value to dst. */ void value_toUtf16(const libscratchcpp::ValueData *v, std::u16string *dst) { - StringPtr *str = value_toStringPtr(v); + StringPtr *str = string_pool_new(); + value_toStringPtr(v, str); dst->assign(str->data); string_pool_free(str); } @@ -395,90 +382,76 @@ extern "C" return v == intpart; } - /*! - * Converts the given number to string. - * \note It is the caller's responsibility to free the string. - */ - StringPtr *value_doubleToStringPtr(double v) + /*! Converts the given number to string and stores the result in dst. */ + void value_doubleToStringPtr(double v, StringPtr *dst) { - if (v == 0) { - StringPtr *ret = string_pool_new(); - string_assign_cstring(ret, "0"); - return ret; - } else if (std::isinf(v)) { - if (v > 0) { - StringPtr *ret = string_pool_new(); - string_assign(ret, &INFINITY_STR); - return ret; + if (v == 0) + string_assign_cstring(dst, "0"); + else if (std::isinf(v)) { + if (v > 0) + string_assign(dst, &INFINITY_STR); + else + string_assign(dst, &NEGATIVE_INFINITY_STR); + } else if (std::isnan(v)) + string_assign(dst, &NAN_STR); + else { + // snprintf() is locale-dependent + std::string oldLocale = std::setlocale(LC_NUMERIC, nullptr); + std::setlocale(LC_NUMERIC, "C"); + + const int maxlen = 26; // should be enough for any number + char *buffer = (char *)malloc((maxlen + 1) * sizeof(char)); + + // Constants for significant digits and thresholds + const int significantDigits = 17 - value_intDigitCount(std::floor(std::fabs(v))); + constexpr double scientificThreshold = 1e21; + constexpr double minScientificThreshold = 1e-6; + + // Use scientific notation if the number is very large or very small + if (std::fabs(v) >= scientificThreshold || std::fabs(v) < minScientificThreshold) { + int ret = snprintf(buffer, maxlen, "%.*g", significantDigits - 1, v); + assert(ret >= 0); } else { - StringPtr *ret = string_pool_new(); - string_assign(ret, &NEGATIVE_INFINITY_STR); - return ret; - } - } else if (std::isnan(v)) { - StringPtr *ret = string_pool_new(); - string_assign(ret, &NAN_STR); - return ret; - } - - // snprintf() is locale-dependent - std::string oldLocale = std::setlocale(LC_NUMERIC, nullptr); - std::setlocale(LC_NUMERIC, "C"); - - const int maxlen = 26; // should be enough for any number - char *buffer = (char *)malloc((maxlen + 1) * sizeof(char)); - - // Constants for significant digits and thresholds - const int significantDigits = 17 - value_intDigitCount(std::floor(std::fabs(v))); - constexpr double scientificThreshold = 1e21; - constexpr double minScientificThreshold = 1e-6; - - // Use scientific notation if the number is very large or very small - if (std::fabs(v) >= scientificThreshold || std::fabs(v) < minScientificThreshold) { - int ret = snprintf(buffer, maxlen, "%.*g", significantDigits - 1, v); - assert(ret >= 0); - } else { - snprintf(buffer, maxlen, "%.*f", significantDigits - 1, v); - - // Remove trailing zeros from the fractional part - char *dot = std::strchr(buffer, '.'); - - if (dot) { - char *end = buffer + std::strlen(buffer) - 1; - while (end > dot && *end == '0') { - *end-- = '\0'; - } - if (*end == '.') { - *end = '\0'; // Remove trailing dot + snprintf(buffer, maxlen, "%.*f", significantDigits - 1, v); + + // Remove trailing zeros from the fractional part + char *dot = std::strchr(buffer, '.'); + + if (dot) { + char *end = buffer + std::strlen(buffer) - 1; + while (end > dot && *end == '0') { + *end-- = '\0'; + } + if (*end == '.') { + *end = '\0'; // Remove trailing dot + } } } - } - // Remove leading zeros after e+/e- - for (int i = 0; i < 2; i++) { - const char *target = (i == 0) ? "e+" : "e-"; - char *index = strstr(buffer, target); - - if (index != nullptr) { - char *ptr = index + 2; - while (*(ptr + 1) != '\0' && *ptr == '0') { - // Shift the characters left to erase '0' - char *shiftPtr = ptr; - do { - *shiftPtr = *(shiftPtr + 1); - shiftPtr++; - } while (*shiftPtr != '\0'); + // Remove leading zeros after e+/e- + for (int i = 0; i < 2; i++) { + const char *target = (i == 0) ? "e+" : "e-"; + char *index = strstr(buffer, target); + + if (index != nullptr) { + char *ptr = index + 2; + while (*(ptr + 1) != '\0' && *ptr == '0') { + // Shift the characters left to erase '0' + char *shiftPtr = ptr; + do { + *shiftPtr = *(shiftPtr + 1); + shiftPtr++; + } while (*shiftPtr != '\0'); + } } } - } - // Restore old locale - std::setlocale(LC_NUMERIC, oldLocale.c_str()); + // Restore old locale + std::setlocale(LC_NUMERIC, oldLocale.c_str()); - StringPtr *ret = string_pool_new(); - string_assign_cstring(ret, buffer); - free(buffer); - return ret; + string_assign_cstring(dst, buffer); + free(buffer); + } } /*! diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h index a55673d9..fd4b0e76 100644 --- a/src/scratch/value_functions_p.h +++ b/src/scratch/value_functions_p.h @@ -486,8 +486,11 @@ extern "C" if (!ok) { // At least one argument can't be converted to a number // Scratch compares strings as case insensitive - StringPtr *s1 = value_toStringPtr(v1); - StringPtr *s2 = value_toStringPtr(v2); + StringPtr *s1 = string_pool_new(); + StringPtr *s2 = string_pool_new(); + value_toStringPtr(v1, s1); + value_toStringPtr(v2, s2); + int ret = string_compare_case_insensitive(s1, s2); string_pool_free(s1); string_pool_free(s2); diff --git a/test/blocks/util.cpp b/test/blocks/util.cpp index 279513d2..0de19290 100644 --- a/test/blocks/util.cpp +++ b/test/blocks/util.cpp @@ -68,11 +68,9 @@ extern "C" bool test_condition() return conditionReturnValue; } -extern "C" StringPtr *test_const_string(const StringPtr *str) +extern "C" void test_const_string(StringPtr *ret, const StringPtr *str) { - StringPtr *ret = string_pool_new(); string_assign(ret, str); - return ret; } } // namespace libscratchcpp diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index e33ef788..3438cccd 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -3451,7 +3451,7 @@ TEST_F(LLVMCodeBuilderTest, Procedures) v = builder->addProcedureArgument("invalid"); builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - builder->build(); + auto proc1 = builder->build(); // Procedure 2 BlockPrototype prototype2; @@ -3464,7 +3464,7 @@ TEST_F(LLVMCodeBuilderTest, Procedures) builder->createProcedureCall(&prototype1, { builder->addConstValue(-652.3), builder->addConstValue(false), builder->addConstValue(true) }); builder->endLoop(); - builder->build(); + auto proc2 = builder->build(); // Script builder = m_utils.createBuilder(&sprite, false); diff --git a/test/llvm/llvmexecutablecode_test.cpp b/test/llvm/llvmexecutablecode_test.cpp index 055efa48..1bb2fae0 100644 --- a/test/llvm/llvmexecutablecode_test.cpp +++ b/test/llvm/llvmexecutablecode_test.cpp @@ -116,6 +116,32 @@ class LLVMExecutableCodeTest : public testing::Test TestMock m_mock; }; +TEST_F(LLVMExecutableCodeTest, FunctionId) +{ + { + auto code = std::make_shared(m_ctx.get(), 8, "script", "resume", 0, Compiler::CodeType::Script); + ASSERT_EQ(code->functionId(), 8); + } + + { + auto code = std::make_shared(m_ctx.get(), 3, "script", "resume", 0, Compiler::CodeType::Script); + ASSERT_EQ(code->functionId(), 3); + } +} + +TEST_F(LLVMExecutableCodeTest, StringCount) +{ + { + auto code = std::make_shared(m_ctx.get(), 0, "script", "resume", 2, Compiler::CodeType::Script); + ASSERT_EQ(code->stringCount(), 2); + } + + { + auto code = std::make_shared(m_ctx.get(), 1, "script", "resume", 56, Compiler::CodeType::Script); + ASSERT_EQ(code->stringCount(), 56); + } +} + TEST_F(LLVMExecutableCodeTest, CreateExecutionContext) { llvm::Function *mainFunc = beginMainFunction(); @@ -125,7 +151,7 @@ TEST_F(LLVMExecutableCodeTest, CreateExecutionContext) endFunction(m_builder->getInt1(true)); { - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Script); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::Script); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); @@ -144,7 +170,7 @@ TEST_F(LLVMExecutableCodeTest, CreatePredicateExecutionContext) endFunction(m_builder->getInt1(true)); { - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::HatPredicate); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::HatPredicate); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); @@ -163,7 +189,7 @@ TEST_F(LLVMExecutableCodeTest, CreateReporterExecutionContext) endFunction(m_builder->getInt1(true)); { - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Reporter); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::Reporter); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); @@ -185,7 +211,7 @@ TEST_F(LLVMExecutableCodeTest, MainFunction) llvm::Function *resumeFunc = beginResumeFunction(); endFunction(m_builder->getInt1(true)); - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Script); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::Script); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); @@ -240,7 +266,7 @@ TEST_F(LLVMExecutableCodeTest, PredicateFunction) llvm::Function *resumeFunc = beginResumeFunction(); endFunction(m_builder->getInt1(true)); - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::HatPredicate); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::HatPredicate); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); @@ -269,7 +295,7 @@ TEST_F(LLVMExecutableCodeTest, ReporterFunction) llvm::Function *resumeFunc = beginResumeFunction(); endFunction(m_builder->getInt1(true)); - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Reporter); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::Reporter); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); @@ -288,7 +314,7 @@ TEST_F(LLVMExecutableCodeTest, Promise) llvm::Function *resumeFunc = beginResumeFunction(); endFunction(m_builder->getInt1(true)); - auto code = std::make_shared(m_ctx.get(), mainFunc->getName().str(), resumeFunc->getName().str(), Compiler::CodeType::Script); + auto code = std::make_shared(m_ctx.get(), 0, mainFunc->getName().str(), resumeFunc->getName().str(), 0, Compiler::CodeType::Script); m_script->setCode(code); Thread thread(&m_target, &m_engine, m_script.get()); auto ctx = code->createExecutionContext(&thread); diff --git a/test/llvm/llvmtestutils.cpp b/test/llvm/llvmtestutils.cpp index 2089493f..38ac716a 100644 --- a/test/llvm/llvmtestutils.cpp +++ b/test/llvm/llvmtestutils.cpp @@ -197,11 +197,31 @@ Value LLVMTestUtils::getExpectedOpResult(OpType type, const Value &v1, const Val case OpType::CmpLT: return v1 < v2; - case OpType::StrCmpEQCS: - return string_compare_case_sensitive(value_toStringPtr(&v1.data()), value_toStringPtr(&v2.data())) == 0; + case OpType::StrCmpEQCS: { + StringPtr *s1 = string_pool_new(); + StringPtr *s2 = string_pool_new(); + value_toStringPtr(&v1.data(), s1); + value_toStringPtr(&v2.data(), s2); - case OpType::StrCmpEQCI: - return string_compare_case_insensitive(value_toStringPtr(&v1.data()), value_toStringPtr(&v2.data())) == 0; + bool ret = string_compare_case_sensitive(s1, s2) == 0; + + string_pool_free(s1); + string_pool_free(s2); + return ret; + } + + case OpType::StrCmpEQCI: { + StringPtr *s1 = string_pool_new(); + StringPtr *s2 = string_pool_new(); + value_toStringPtr(&v1.data(), s1); + value_toStringPtr(&v2.data(), s2); + + bool ret = string_compare_case_insensitive(s1, s2) == 0; + + string_pool_free(s1); + string_pool_free(s2); + return ret; + } case OpType::And: return v1.toBool() && v2.toBool(); diff --git a/test/llvm/testfunctions.cpp b/test/llvm/testfunctions.cpp index 36d3d88e..a81af148 100644 --- a/test/llvm/testfunctions.cpp +++ b/test/llvm/testfunctions.cpp @@ -63,12 +63,12 @@ extern "C" std::cout << "no_args" << std::endl; } - StringPtr *test_function_no_args_ret(Target *target) + void test_function_no_args_ret(StringPtr *ret, Target *target) { target->isStage(); std::cout << "no_args_ret" << std::endl; Value v("no_args_output"); - return value_toStringPtr(&v.data()); + value_toStringPtr(&v.data(), ret); } void test_function_1_arg(Target *target, const StringPtr *arg1) @@ -77,12 +77,12 @@ extern "C" std::cout << "1_arg " << utf8::utf16to8(std::u16string(arg1->data)) << std::endl; } - StringPtr *test_function_1_arg_ret(Target *target, const StringPtr *arg1) + void test_function_1_arg_ret(StringPtr *ret, Target *target, const StringPtr *arg1) { target->isStage(); std::cout << "1_arg_ret " << utf8::utf16to8(std::u16string(arg1->data)) << std::endl; Value v("1_arg_output"); - return value_toStringPtr(&v.data()); + value_toStringPtr(&v.data(), ret); } void test_function_3_args(Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3) @@ -91,12 +91,12 @@ extern "C" std::cout << "3_args " << utf8::utf16to8(std::u16string(arg1->data)) << " " << utf8::utf16to8(std::u16string(arg2->data)) << " " << utf8::utf16to8(std::u16string(arg3->data)) << std::endl; } - StringPtr *test_function_3_args_ret(Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3) + void test_function_3_args_ret(StringPtr *ret, Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3) { target->isStage(); std::cout << "3_args " << utf8::utf16to8(std::u16string(arg1->data)) << " " << utf8::utf16to8(std::u16string(arg2->data)) << " " << utf8::utf16to8(std::u16string(arg3->data)) << std::endl; Value v("3_args_output"); - return value_toStringPtr(&v.data()); + value_toStringPtr(&v.data(), ret); } const void *test_function_1_ptr_arg_ret(Target *target, const int *arg1) @@ -132,11 +132,9 @@ extern "C" return v; } - StringPtr *test_const_string(const StringPtr *v) + void test_const_string(StringPtr *ret, const StringPtr *v) { - StringPtr *ret = string_pool_new(); string_assign(ret, v); - return ret; } ValueData test_const_unknown(const ValueData *v) diff --git a/test/llvm/testfunctions.h b/test/llvm/testfunctions.h index 0e83168f..c56162f4 100644 --- a/test/llvm/testfunctions.h +++ b/test/llvm/testfunctions.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace libscratchcpp { @@ -21,11 +23,11 @@ extern "C" void test_ctx_function(ExecutionContext *ctx, const StringPtr *arg); void test_function_no_args(Target *target); - StringPtr *test_function_no_args_ret(Target *target); + void test_function_no_args_ret(StringPtr *ret, Target *target); void test_function_1_arg(Target *target, const StringPtr *arg1); - StringPtr *test_function_1_arg_ret(Target *target, const StringPtr *arg1); + void test_function_1_arg_ret(StringPtr *ret, Target *target, const StringPtr *arg1); void test_function_3_args(Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3); - StringPtr *test_function_3_args_ret(Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3); + void test_function_3_args_ret(StringPtr *ret, Target *target, const StringPtr *arg1, const StringPtr *arg2, const StringPtr *arg3); const void *test_function_1_ptr_arg_ret(Target *target, const int *arg1); @@ -35,7 +37,7 @@ extern "C" double test_const_number(double v); bool test_const_bool(bool v); - StringPtr *test_const_string(const StringPtr *v); + void test_const_string(StringPtr *ret, const StringPtr *v); ValueData test_const_unknown(const ValueData *v); const void *test_const_pointer(const void *v); diff --git a/test/scratch_classes/list_functions_test.cpp b/test/scratch_classes/list_functions_test.cpp index 898757b7..289dc1a1 100644 --- a/test/scratch_classes/list_functions_test.cpp +++ b/test/scratch_classes/list_functions_test.cpp @@ -171,7 +171,8 @@ TEST(ListFunctionsTest, ToString) list.append("sit"); list.append("amet"); - StringPtr *str = list_to_string(&list); + StringPtr *str = string_pool_new(); + list_to_string(&list, str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "Lorem ipsum dolor sit amet"); } @@ -181,7 +182,8 @@ TEST(ListFunctionsTest, ToString) list.append(2); list.append(3); - StringPtr *str = list_to_string(&list); + StringPtr *str = string_pool_new(); + list_to_string(&list, str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "123"); } } diff --git a/test/scratch_classes/list_test.cpp b/test/scratch_classes/list_test.cpp index 257b8778..a69c3823 100644 --- a/test/scratch_classes/list_test.cpp +++ b/test/scratch_classes/list_test.cpp @@ -296,20 +296,23 @@ TEST(ListTest, ArrayIndexOperator) TEST(ListTest, ToString) { List list("", "test list"); - StringPtr *str = list.toStringPtr(); + StringPtr *str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), ""); ASSERT_EQ(str->size, 0); ASSERT_EQ(list.toString(), ""); list.append(""); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), ""); ASSERT_EQ(str->size, 0); ASSERT_EQ(list.toString(), ""); list.append(""); list.append(""); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), " "); ASSERT_EQ(str->size, 2); ASSERT_EQ(list.toString(), " "); @@ -318,7 +321,8 @@ TEST(ListTest, ToString) list.append("item1"); list.append("i t e m 2"); list.append("item 3"); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "item1 i t e m 2 item 3"); ASSERT_EQ(str->size, 22); ASSERT_EQ(list.toString(), "item1 i t e m 2 item 3"); @@ -328,7 +332,8 @@ TEST(ListTest, ToString) list.append("a "); list.append(" b"); list.append(" c "); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), " a b c "); ASSERT_EQ(str->size, 11); ASSERT_EQ(list.toString(), " a b c "); @@ -336,7 +341,8 @@ TEST(ListTest, ToString) list.clear(); list.append("áä"); list.append("ľ š"); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "áä ľ š"); ASSERT_EQ(str->size, 6); ASSERT_EQ(list.toString(), "áä ľ š"); @@ -345,7 +351,8 @@ TEST(ListTest, ToString) list.append(-2); list.append(5); list.append(8); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "-2 5 8"); ASSERT_EQ(str->size, 6); ASSERT_EQ(list.toString(), "-2 5 8"); @@ -354,7 +361,8 @@ TEST(ListTest, ToString) list.append(2); list.append(10); list.append(8); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "2 10 8"); ASSERT_EQ(str->size, 6); ASSERT_EQ(list.toString(), "2 10 8"); @@ -363,7 +371,8 @@ TEST(ListTest, ToString) list.append(0); list.append(9); list.append(8); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "098"); ASSERT_EQ(str->size, 3); ASSERT_EQ(list.toString(), "098"); @@ -371,7 +380,8 @@ TEST(ListTest, ToString) list.clear(); list.append("true"); list.append("false"); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "true false"); ASSERT_EQ(str->size, 10); ASSERT_EQ(list.toString(), "true false"); @@ -379,7 +389,8 @@ TEST(ListTest, ToString) list.clear(); list.append(true); list.append(false); - str = list.toStringPtr(); + str = string_pool_new(); + list.toStringPtr(str); ASSERT_EQ(utf8::utf16to8(std::u16string(str->data)), "true false"); ASSERT_EQ(str->size, 10); ASSERT_EQ(list.toString(), "true false"); diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index 6851fb89..fb5f3ec6 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -1477,230 +1477,342 @@ TEST(ValueTest, ToString) Value v = 2147483647; ASSERT_EQ(v.toString(), "2147483647"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + StringPtr *str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = -2147483647; ASSERT_EQ(v.toString(), "-2147483647"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 512L; ASSERT_EQ(v.toString(), "512"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = -512L; ASSERT_EQ(v.toString(), "-512"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 0.0; ASSERT_EQ(v.toString(), "0"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -0.0; ASSERT_EQ(v.toString(), "0"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 2.0; ASSERT_EQ(v.toString(), "2"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -2.0; ASSERT_EQ(v.toString(), "-2"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 2.54; ASSERT_EQ(v.toString(), "2.54"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -2.54; ASSERT_EQ(v.toString(), "-2.54"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 59.8; ASSERT_EQ(v.toString(), "59.8"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -59.8; ASSERT_EQ(v.toString(), "-59.8"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 5.3; ASSERT_EQ(v.toString(), "5.3"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -5.3; ASSERT_EQ(v.toString(), "-5.3"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 2550.625021000115; ASSERT_EQ(v.toString(), "2550.625021000115"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -2550.625021000115; ASSERT_EQ(v.toString(), "-2550.625021000115"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 9.4324e+20; ASSERT_EQ(v.toString(), "943240000000000000000"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -2.591e-2; ASSERT_EQ(v.toString(), "-0.02591"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 9.4324e+21; ASSERT_EQ(v.toString(), "9.4324e+21"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = -2.591e-13; ASSERT_EQ(v.toString(), "-2.591e-13"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 0.001; ASSERT_EQ(v.toString(), "0.001"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = -0.001; ASSERT_EQ(v.toString(), "-0.001"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 0.000001; ASSERT_EQ(v.toString(), "0.000001"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = -0.000001; ASSERT_EQ(v.toString(), "-0.000001"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = 0.0000001; ASSERT_EQ(v.toString(), "1e-7"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = -0.0000001; ASSERT_EQ(v.toString(), "-1e-7"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = false; ASSERT_EQ(v.toString(), "false"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = true; ASSERT_EQ(v.toString(), "true"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "2147483647"; ASSERT_EQ(v.toString(), "2147483647"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "-2147483647"; ASSERT_EQ(v.toString(), "-2147483647"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "999999999999999999"; ASSERT_EQ(v.toString(), "999999999999999999"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "-999999999999999999"; ASSERT_EQ(v.toString(), "-999999999999999999"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "255.625"; ASSERT_EQ(v.toString(), "255.625"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "-255.625"; ASSERT_EQ(v.toString(), "-255.625"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "9432.4e-12"; ASSERT_EQ(v.toString(), "9432.4e-12"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "-9432.4e-12"; ASSERT_EQ(v.toString(), "-9432.4e-12"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "9432.4e+6"; ASSERT_EQ(v.toString(), "9432.4e+6"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "-9432.4e+6"; ASSERT_EQ(v.toString(), "-9432.4e+6"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "false"; ASSERT_EQ(v.toString(), "false"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "true"; ASSERT_EQ(v.toString(), "true"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "Infinity"; ASSERT_TRUE(v.isInfinity()); ASSERT_EQ(v.toString(), "Infinity"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "-Infinity"; ASSERT_TRUE(v.isNegativeInfinity()); ASSERT_EQ(v.toString(), "-Infinity"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = "NaN"; ASSERT_TRUE(v.isNaN()); ASSERT_EQ(v.toString(), "NaN"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = "something"; ASSERT_EQ(v.toString(), "something"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); long var; v = &var; ASSERT_EQ(v.toString(), "0"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); v = std::numeric_limits::infinity(); ASSERT_EQ(v.toString(), "Infinity"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = -std::numeric_limits::infinity(); ASSERT_EQ(v.toString(), "-Infinity"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); + v = std::numeric_limits::quiet_NaN(); ASSERT_EQ(v.toString(), "NaN"); ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); - ASSERT_EQ(std::u16string(value_toStringPtr(&v.data())->data), v.toUtf16()); + str = string_pool_new(); + value_toStringPtr(&v.data(), str); + ASSERT_EQ(std::u16string(str->data), v.toUtf16()); std::setlocale(LC_NUMERIC, oldLocale.c_str()); } @@ -3211,79 +3323,105 @@ TEST(ValueTest, DoubleToStringPtr) // TODO: Use a custom comparison function StringPtr *ret; - ret = value_doubleToStringPtr(0.0); + + ret = string_pool_new(); + value_doubleToStringPtr(0.0, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "0"); - ret = value_doubleToStringPtr(-0.0); + ret = string_pool_new(); + value_doubleToStringPtr(-0.0, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "0"); - ret = value_doubleToStringPtr(2.0); + ret = string_pool_new(); + value_doubleToStringPtr(2.0, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "2"); - ret = value_doubleToStringPtr(-2.0); + ret = string_pool_new(); + value_doubleToStringPtr(-2.0, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-2"); - ret = value_doubleToStringPtr(2.54); + ret = string_pool_new(); + value_doubleToStringPtr(2.54, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "2.54"); - ret = value_doubleToStringPtr(-2.54); + ret = string_pool_new(); + value_doubleToStringPtr(-2.54, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-2.54"); - ret = value_doubleToStringPtr(59.8); + ret = string_pool_new(); + value_doubleToStringPtr(59.8, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "59.8"); - ret = value_doubleToStringPtr(-59.8); + ret = string_pool_new(); + value_doubleToStringPtr(-59.8, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-59.8"); - ret = value_doubleToStringPtr(5.3); + ret = string_pool_new(); + value_doubleToStringPtr(5.3, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "5.3"); - ret = value_doubleToStringPtr(-5.3); + ret = string_pool_new(); + value_doubleToStringPtr(-5.3, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-5.3"); - ret = value_doubleToStringPtr(2550.625021000115); + ret = string_pool_new(); + value_doubleToStringPtr(2550.625021000115, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "2550.625021000115"); - ret = value_doubleToStringPtr(-2550.625021000115); + ret = string_pool_new(); + value_doubleToStringPtr(-2550.625021000115, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-2550.625021000115"); - ret = value_doubleToStringPtr(9.4324e+20); + ret = string_pool_new(); + value_doubleToStringPtr(9.4324e+20, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "943240000000000000000"); - ret = value_doubleToStringPtr(-2.591e-2); + ret = string_pool_new(); + value_doubleToStringPtr(-2.591e-2, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-0.02591"); - ret = value_doubleToStringPtr(9.4324e+21); + ret = string_pool_new(); + value_doubleToStringPtr(9.4324e+21, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "9.4324e+21"); - ret = value_doubleToStringPtr(-2.591e-13); + ret = string_pool_new(); + value_doubleToStringPtr(-2.591e-13, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-2.591e-13"); - ret = value_doubleToStringPtr(0.001); + ret = string_pool_new(); + value_doubleToStringPtr(0.001, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "0.001"); - ret = value_doubleToStringPtr(-0.001); + ret = string_pool_new(); + value_doubleToStringPtr(-0.001, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-0.001"); - ret = value_doubleToStringPtr(0.000001); + ret = string_pool_new(); + value_doubleToStringPtr(0.000001, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "0.000001"); - ret = value_doubleToStringPtr(-0.000001); + ret = string_pool_new(); + value_doubleToStringPtr(-0.000001, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-0.000001"); - ret = value_doubleToStringPtr(0.0000001); + ret = string_pool_new(); + value_doubleToStringPtr(0.0000001, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "1e-7"); - ret = value_doubleToStringPtr(-0.0000001); + ret = string_pool_new(); + value_doubleToStringPtr(-0.0000001, ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-1e-7"); - ret = value_doubleToStringPtr(std::numeric_limits::infinity()); + ret = string_pool_new(); + value_doubleToStringPtr(std::numeric_limits::infinity(), ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "Infinity"); - ret = value_doubleToStringPtr(-std::numeric_limits::infinity()); + ret = string_pool_new(); + value_doubleToStringPtr(-std::numeric_limits::infinity(), ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "-Infinity"); - ret = value_doubleToStringPtr(std::numeric_limits::quiet_NaN()); + ret = string_pool_new(); + value_doubleToStringPtr(std::numeric_limits::quiet_NaN(), ret); ASSERT_EQ(utf8::utf16to8(std::u16string(ret->data)), "NaN"); std::setlocale(LC_NUMERIC, oldLocale.c_str());