File indexing completed on 2024-05-12 11:32:23
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-or-later 0003 SPDX-FileCopyrightText: 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com> 0004 SPDX-FileCopyrightText: 2023 Alexander Semke <alexander.semke@web.de> 0005 */ 0006 #include "juliaserver.h" 0007 0008 #include <julia_version.h> 0009 0010 #include <iostream> 0011 #include <QFileInfo> 0012 #include <QDir> 0013 #include <QTemporaryFile> 0014 #include <QDebug> 0015 0016 QStringList JuliaServer::INTERNAL_VARIABLES = 0017 QStringList() << QLatin1String("__originalSTDOUT__") << QLatin1String("__originalSTDERR__"); 0018 0019 JuliaServer::JuliaServer(QObject *parent) : QObject(parent), m_was_exception(false) 0020 { 0021 } 0022 0023 JuliaServer::~JuliaServer() 0024 { 0025 /* strongly recommended: notify Julia that the 0026 program is about to terminate. this allows 0027 Julia time to cleanup pending write requests 0028 and run all finalizers 0029 */ 0030 jl_atexit_hook(0); 0031 } 0032 0033 int JuliaServer::login() 0034 { 0035 /* required: setup the julia context */ 0036 jl_init(); 0037 0038 jl_eval_string("import REPL;"); 0039 0040 return 0; 0041 } 0042 0043 void JuliaServer::runJuliaCommand(const QString &command) 0044 { 0045 // Redirect stdout, stderr to temporary files 0046 QTemporaryFile output, error; 0047 if (!output.open() || !error.open()) { 0048 qFatal("Unable to create temporary files for stdout/stderr"); 0049 return; 0050 } 0051 jl_eval_string("const __originalSTDOUT__ = stdout"); 0052 jl_eval_string("const __originalSTDERR__ = stderr"); 0053 jl_eval_string( 0054 QString::fromLatin1("redirect_stdout(open(\"%1\", \"w\"))") 0055 .arg(output.fileName()).toLatin1().constData() 0056 ); 0057 jl_eval_string( 0058 QString::fromLatin1("redirect_stderr(open(\"%1\", \"w\"))") 0059 .arg(error.fileName()).toLatin1().constData() 0060 ); 0061 0062 jl_module_t* jl_repl_module = (jl_module_t*)(jl_eval_string("REPL")); 0063 jl_function_t* jl_ends_func = jl_get_function(jl_repl_module, "ends_with_semicolon"); 0064 bool isEndsWithSemicolon = jl_unbox_bool(jl_call1(jl_ends_func, jl_cstr_to_string(command.toStdString().c_str()))); 0065 0066 // Run command 0067 jl_value_t *val = static_cast<jl_value_t *>( 0068 jl_eval_string(command.toUtf8().constData()) 0069 ); 0070 0071 if (jl_exception_occurred()) { // If exception occurred 0072 // Show it to user in stderr 0073 #if QT_VERSION_CHECK(JULIA_VERSION_MAJOR, JULIA_VERSION_MINOR, 0) >= QT_VERSION_CHECK(1, 7, 0) 0074 jl_value_t *ex = jl_current_task->ptls->previous_exception; 0075 #elif QT_VERSION_CHECK(JULIA_VERSION_MAJOR, JULIA_VERSION_MINOR, 0) >= QT_VERSION_CHECK(1, 1, 0) 0076 jl_value_t *ex = jl_get_ptls_states()->previous_exception; 0077 #else 0078 jl_value_t *ex = jl_exception_in_transit; 0079 #endif 0080 jl_printf(JL_STDERR, "error during run:\n"); 0081 jl_function_t *showerror = 0082 jl_get_function(jl_base_module, "showerror"); 0083 jl_value_t *bt = static_cast<jl_value_t *>( 0084 jl_eval_string("catch_backtrace()") 0085 ); 0086 jl_value_t *err_stream = static_cast<jl_value_t *>( 0087 jl_eval_string("stderr") 0088 ); 0089 jl_call3(showerror, err_stream, ex, bt); 0090 jl_exception_clear(); 0091 m_was_exception = true; 0092 } else if (val && !isEndsWithSemicolon) { // no exception occurred 0093 // If last result is not nothing, show it 0094 jl_function_t *equality = jl_get_function(jl_base_module, "=="); 0095 jl_value_t *nothing = 0096 static_cast<jl_value_t *>(jl_eval_string("nothing")); 0097 bool is_nothing = jl_unbox_bool( 0098 static_cast<jl_value_t *>(jl_call2(equality, nothing, val)) 0099 ); 0100 if (!is_nothing) { 0101 jl_value_t *out_display = static_cast<jl_value_t *>(jl_eval_string("TextDisplay(stdout)")); 0102 jl_function_t *display = jl_get_function(jl_base_module, "display"); 0103 jl_call2(display, out_display, val); 0104 } 0105 m_was_exception = false; 0106 } 0107 // Clean up streams and files 0108 jl_eval_string("flush(stdout)"); 0109 jl_eval_string("flush(stderr)"); 0110 jl_eval_string("redirect_stdout(__originalSTDOUT__)"); 0111 jl_eval_string("redirect_stderr(__originalSTDERR__)"); 0112 0113 // Clean up variables 0114 auto vars_to_remove = { 0115 "__originalSTDOUT__", "__originalSTDERR__" 0116 }; 0117 0118 for (const auto &var : vars_to_remove) { 0119 jl_eval_string( 0120 QString::fromLatin1("%1 = 0").arg(QLatin1String(var)) 0121 .toLatin1().constData() 0122 ); 0123 } 0124 0125 m_output = QString::fromUtf8(output.readAll()); 0126 m_error = QString::fromUtf8(error.readAll()); 0127 } 0128 0129 QString JuliaServer::getError() const 0130 { 0131 return m_error; 0132 } 0133 0134 QString JuliaServer::getOutput() const 0135 { 0136 return m_output; 0137 } 0138 0139 bool JuliaServer::getWasException() const 0140 { 0141 return m_was_exception; 0142 } 0143 0144 #if QT_VERSION_CHECK(JULIA_VERSION_MAJOR, JULIA_VERSION_MINOR, 0) >= QT_VERSION_CHECK(1, 1, 0) 0145 #define JL_MAIN_MODULE jl_main_module 0146 #else 0147 #define JL_MAIN_MODULE jl_internal_main_module 0148 #endif 0149 0150 void JuliaServer::parseModules(bool variableManagement) 0151 { 0152 parseJlModule(JL_MAIN_MODULE, variableManagement); 0153 } 0154 0155 void JuliaServer::parseJlModule(jl_module_t* module, bool parseValue) 0156 { 0157 jl_function_t* jl_string_function = jl_get_function(jl_base_module, "string"); 0158 jl_function_t* jl_sizeof_function = jl_get_function(jl_base_module, "sizeof"); 0159 0160 if (module != JL_MAIN_MODULE) 0161 { 0162 const QString& moduleName = fromJuliaString(jl_call1(jl_string_function, (jl_value_t*)(module->name))); 0163 if (parsedModules.contains(moduleName)) 0164 return; 0165 else 0166 parsedModules.append(moduleName); 0167 } 0168 0169 jl_function_t* jl_names_function = jl_get_function(jl_base_module, "names"); 0170 jl_value_t* names = jl_call1(jl_names_function, (jl_value_t*)module); 0171 jl_value_t **data = (jl_value_t**)jl_array_data(names); 0172 for (size_t i = 0; i < jl_array_len(names); i++) 0173 { 0174 bool isBindingResolved = (bool)jl_binding_resolved_p(module, (jl_sym_t*)(data[i])); 0175 if (isBindingResolved) 0176 { 0177 0178 const QString& name = fromJuliaString(jl_call1(jl_string_function, data[i])); 0179 jl_value_t* value = jl_get_binding_or_error(module, (jl_sym_t*)(data[i]))->value; 0180 jl_datatype_t* datetype = (jl_datatype_t*)jl_typeof(value); 0181 QString type = QString::fromUtf8(jl_typeof_str(value)); 0182 0183 // Module 0184 if (jl_is_module(value)) 0185 { 0186 if (module == JL_MAIN_MODULE && (jl_module_t*)value != JL_MAIN_MODULE) 0187 parseJlModule((jl_module_t*)value, parseValue); 0188 } 0189 // Function 0190 else if (type.startsWith(QLatin1String("#")) || type == QLatin1String("Function")) 0191 { 0192 if (!m_functions.contains(name)) 0193 m_functions.append(name); 0194 } 0195 // Variable 0196 else if (datetype != jl_datatype_type) // Not type 0197 { 0198 if (module == JL_MAIN_MODULE && !INTERNAL_VARIABLES.contains(name)) 0199 { 0200 const QString& size = fromJuliaString(jl_call1(jl_string_function, jl_call1(jl_sizeof_function, value))); 0201 //const QString& type = fromJuliaString(jl_call1(jl_string_function, jl_call1(jl_typeof_function, value))); 0202 if (parseValue) 0203 { 0204 const QString& valueString = fromJuliaString(jl_call1(jl_string_function, value)); 0205 if (m_variables.contains(name)) 0206 { 0207 int i = m_variables.indexOf(name); 0208 m_variableValues[i] = valueString; 0209 m_variableSizes[i] = size; 0210 m_variableTypes[i] = type; 0211 } 0212 else 0213 { 0214 m_variables.append(name); 0215 m_variableValues.append(valueString); 0216 m_variableSizes.append(size); 0217 m_variableTypes.append(type); 0218 } 0219 } 0220 else 0221 { 0222 if (m_variables.contains(name)) 0223 { 0224 int i = m_variables.indexOf(name); 0225 m_variableSizes[i] = size; 0226 m_variableTypes[i] = type; 0227 } 0228 else 0229 { 0230 m_variables.append(name); 0231 m_variableSizes.append(size); 0232 m_variableTypes.append(type); 0233 } 0234 } 0235 } 0236 } 0237 } 0238 } 0239 } 0240 0241 QString JuliaServer::fromJuliaString(const jl_value_t* value) 0242 { 0243 return QString::fromUtf8(jl_string_data(value)); 0244 } 0245 0246 QStringList JuliaServer::variablesList() 0247 { 0248 return m_variables; 0249 } 0250 0251 QStringList JuliaServer::variableValuesList() 0252 { 0253 return m_variableValues; 0254 } 0255 0256 QStringList JuliaServer::variableSizesList() 0257 { 0258 return m_variableSizes; 0259 } 0260 0261 QStringList JuliaServer::variableTypesList() 0262 { 0263 return m_variableTypes; 0264 } 0265 0266 QStringList JuliaServer::functionsList() 0267 { 0268 return m_functions; 0269 }