Subversion Repositories HomeAutomation

Rev

Rev 2107 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed

Rev 2107 Rev 2109
1
/*
1
/*
2
 *
2
 *
3
 *  Copyright (C) 2010  Mattias Runge
3
 *  Copyright (C) 2010  Mattias Runge
4
 *
4
 *
5
 *  This program is free software; you can redistribute it and/or modify
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
8
 *  (at your option) any later version.
9
 *
9
 *
10
 *  This program is distributed in the hope that it will be useful,
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
13
 *  GNU General Public License for more details.
14
 *
14
 *
15
 *  You should have received a copy of the GNU General Public License along
15
 *  You should have received a copy of the GNU General Public License along
16
 *  with this program; if not, write to the Free Software Foundation, Inc.,
16
 *  with this program; if not, write to the Free Software Foundation, Inc.,
17
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
 *
18
 *
19
 */
19
 */
20
 
20
 
21
#include "Manager.h"
21
#include "Manager.h"
22
 
22
 
23
#include <fstream>
23
#include <fstream>
24
 
24
 
25
#include <boost/lexical_cast.hpp>
25
#include <boost/lexical_cast.hpp>
26
#include <boost/bind.hpp>
26
#include <boost/bind.hpp>
27
 
27
 
28
namespace atom {
28
namespace atom {
29
namespace vm {
29
namespace vm {
30
 
30
 
31
Manager::Pointer Manager::instance_;
31
Manager::Pointer Manager::instance_;
32
 
32
 
33
Manager::Manager() : LOG("vm::Manager")
33
Manager::Manager() : LOG("vm::Manager")
34
{
34
{
35
}
35
}
36
 
36
 
37
Manager::~Manager()
37
Manager::~Manager()
38
{
38
{
39
    this->io_service_.stop();
39
    this->io_service_.stop();
40
   
40
 
41
    this->plugins_.clear();
41
    this->plugins_.clear();
42
    this->context_.Dispose();
42
    this->context_.Dispose();
43
 
43
 
44
    for (ImportFunctionList::iterator it = this->functions_.begin(); it != this->functions_.end(); it++)
44
    for (ImportFunctionList::iterator it = this->functions_.begin(); it != this->functions_.end(); it++)
45
    {
45
    {
46
        it->second.Dispose();
46
        it->second.Dispose();
47
    }
47
    }
48
}
48
}
49
 
49
 
50
void Manager::Create()
50
void Manager::Create()
51
{
51
{
52
    Manager::instance_ = Manager::Pointer(new Manager());
52
    Manager::instance_ = Manager::Pointer(new Manager());
53
}
53
}
54
 
54
 
55
Manager::Pointer Manager::Instance()
55
Manager::Pointer Manager::Instance()
56
{
56
{
57
    return Manager::instance_;
57
    return Manager::instance_;
58
}
58
}
59
 
59
 
60
void Manager::Delete()
60
void Manager::Delete()
61
{
61
{
62
    Manager::instance_.reset();
62
    Manager::instance_.reset();
63
}
63
}
64
 
64
 
65
v8::Persistent<v8::Context>& Manager::GetContext()
65
v8::Persistent<v8::Context>& Manager::GetContext()
66
{
66
{
67
    return this->context_;
67
    return this->context_;
68
}
68
}
69
 
69
 
70
void Manager::AddPlugin(Plugin::Pointer plugin)
70
void Manager::AddPlugin(Plugin::Pointer plugin)
71
{
71
{
72
    this->plugins_.push_back(plugin);
72
    this->plugins_.push_back(plugin);
73
}
73
}
74
 
74
 
75
void Manager::Start(std::string script_path, std::string user_script_path)
75
void Manager::Start(std::string script_path, std::string user_script_path)
76
{
76
{
77
    this->script_path_ = script_path;
77
    this->script_path_ = script_path;
78
    this->user_script_path_ = user_script_path;
78
    this->user_script_path_ = user_script_path;
79
    this->io_service_.post(boost::bind(&Manager::StartHandler, this));
79
    this->io_service_.post(boost::bind(&Manager::StartHandler, this));
80
}
80
}
81
 
81
 
82
void Manager::CallOutput(std::string output)
82
void Manager::CallOutput(std::string output)
83
{
83
{
84
    if (this->current_plugin_name_ != "")
84
    if (this->current_plugin_name_ != "")
85
    {
85
    {
86
        for (unsigned int n = 0; n < this->plugins_.size(); n++)
86
        for (unsigned int n = 0; n < this->plugins_.size(); n++)
87
        {
87
        {
88
            if (this->plugins_[n]->GetName() == this->current_plugin_name_)
88
            if (this->plugins_[n]->GetName() == this->current_plugin_name_)
89
            {
89
            {
90
                this->plugins_[n]->CallOutput(this->current_request_id_, output);
90
                this->plugins_[n]->CallOutput(this->current_request_id_, output);
91
                return;
91
                return;
92
            }
92
            }
93
        }
93
        }
94
    }
94
    }
95
    else
95
    else
96
    {
96
    {
97
        LOG.Info(output);
97
        LOG.Info(output);
98
    }
98
    }
99
}
99
}
100
 
100
 
101
void Manager::CallResult(std::string output)
101
void Manager::CallResult(std::string output)
102
{
102
{
103
  if (this->current_plugin_name_ != "")
103
  if (this->current_plugin_name_ != "")
104
  {
104
  {
105
    for (unsigned int n = 0; n < this->plugins_.size(); n++)
105
    for (unsigned int n = 0; n < this->plugins_.size(); n++)
106
    {
106
    {
107
      if (this->plugins_[n]->GetName() == this->current_plugin_name_)
107
      if (this->plugins_[n]->GetName() == this->current_plugin_name_)
108
      {
108
      {
109
        this->plugins_[n]->CallResult(this->current_request_id_, output);
109
        this->plugins_[n]->CallResult(this->current_request_id_, output);
110
        return;
110
        return;
111
      }
111
      }
112
    }
112
    }
113
  }
113
  }
114
  else
114
  else
115
  {
115
  {
116
    LOG.Info(output);
116
    LOG.Info(output);
117
  }
117
  }
118
}
118
}
119
 
119
 
120
Value Manager::Export_Log(const v8::Arguments& args)
120
Value Manager::Export_Log(const v8::Arguments& args)
121
{
121
{
122
    v8::Locker lock;
122
    v8::Locker lock;
123
    v8::Context::Scope context_scope(Manager::Instance()->GetContext());
123
    v8::Context::Scope context_scope(Manager::Instance()->GetContext());
124
    v8::HandleScope handle_scope;
124
    v8::HandleScope handle_scope;
125
     
125
 
126
    //LOG.Debug(std::string(__FUNCTION__) + " called!");
126
    //LOG.Debug(std::string(__FUNCTION__) + " called!");
127
   
127
 
128
    if (args.Length() < 1)
128
    if (args.Length() < 1)
129
    {
129
    {
130
        Manager::instance_->LOG.Error(std::string(__FUNCTION__) + ": To few arguments to log.");
130
        Manager::instance_->LOG.Error(std::string(__FUNCTION__) + ": To few arguments to log.");
131
        return v8::Undefined();
131
        return v8::Undefined();
132
    }
132
    }
133
   
133
 
134
    v8::String::AsciiValue str(args[0]);
134
    v8::String::AsciiValue str(args[0]);
135
       
135
 
136
    Manager::instance_->CallOutput(*str);
136
    Manager::instance_->CallOutput(*str);
137
   
137
 
138
    return handle_scope.Close(v8::Undefined());
138
    return handle_scope.Close(v8::Undefined());
139
}
139
}
140
 
140
 
141
void Manager::Call(std::string plugin_name, unsigned int request_id, std::string name, ArgumentListPointer arguments)
141
void Manager::Call(std::string plugin_name, unsigned int request_id, std::string name, ArgumentListPointer arguments)
142
{
142
{
143
    this->io_service_.post(boost::bind(&Manager::CallHandler, this, plugin_name, request_id, name, arguments));
143
    this->io_service_.post(boost::bind(&Manager::CallHandler, this, plugin_name, request_id, name, arguments));
144
}
144
}
145
 
145
 
146
void Manager::Execute(std::string plugin_name, unsigned int request_id, std::string code)
146
void Manager::Execute(std::string plugin_name, unsigned int request_id, std::string code)
147
{
147
{
148
    this->io_service_.post(boost::bind(&Manager::ExecuteHandler, this, plugin_name, request_id, code));
148
    this->io_service_.post(boost::bind(&Manager::ExecuteHandler, this, plugin_name, request_id, code));
149
}
149
}
150
 
150
 
151
bool Manager::CallHandler(std::string plugin_name, unsigned int request_id, std::string name, ArgumentListPointer arguments)
151
bool Manager::CallHandler(std::string plugin_name, unsigned int request_id, std::string name, ArgumentListPointer arguments)
152
{
152
{
153
    v8::Locker lock;
153
    v8::Locker lock;
154
    v8::Context::Scope context_scope(this->context_);
154
    v8::Context::Scope context_scope(this->context_);
155
    v8::TryCatch try_catch;
155
    v8::TryCatch try_catch;
156
   
156
 
157
    this->current_plugin_name_ = plugin_name;
157
    this->current_plugin_name_ = plugin_name;
158
    this->current_request_id_ = request_id;
158
    this->current_request_id_ = request_id;
159
   
159
 
160
    if (this->functions_.find(name) == this->functions_.end())
160
    if (this->functions_.find(name) == this->functions_.end())
161
    {
161
    {
162
        this->CallOutput("No such function found, " + name);
162
        this->CallOutput("No such function found, " + name);
163
       
163
 
164
        this->current_plugin_name_ = "";
164
        this->current_plugin_name_ = "";
165
        this->current_request_id_ = 0;
165
        this->current_request_id_ = 0;
166
        return false;
166
        return false;
167
    }
167
    }
168
 
168
 
169
    //LOG.Debug("Call: " + name);
169
    //LOG.Debug("Call: " + name);
170
 
170
 
171
    Value result = this->functions_[name]->Call(this->context_->Global(), arguments->size(), arguments->data());
171
    Value result = this->functions_[name]->Call(this->context_->Global(), arguments->size(), arguments->data());
172
   
172
 
173
    if (result.IsEmpty())
173
    if (result.IsEmpty())
174
    {
174
    {
175
        this->CallOutput(this->FormatException(try_catch));
175
        this->CallOutput(this->FormatException(try_catch));
176
       
176
 
177
        this->current_plugin_name_ = "";
177
        this->current_plugin_name_ = "";
178
        this->current_request_id_ = 0;
178
        this->current_request_id_ = 0;
179
        return false;
179
        return false;
180
    }
180
    }
181
   
181
 
182
    //v8::String::AsciiValue response(result->ToString());
182
    //v8::String::AsciiValue response(result->ToString());
183
    //this->CallOutput(plugin_name, request_id, std::string(*response));
183
    //this->CallOutput(plugin_name, request_id, std::string(*response));
184
   
184
 
185
    this->current_plugin_name_ = "";
185
    this->current_plugin_name_ = "";
186
    this->current_request_id_ = 0;
186
    this->current_request_id_ = 0;
187
    return true;
187
    return true;
188
}
188
}
189
 
189
 
190
void Manager::StartHandler()
190
void Manager::StartHandler()
191
{
191
{
192
    v8::Locker lock;
192
    v8::Locker lock;
193
    v8::HandleScope handle_scope;
193
    v8::HandleScope handle_scope;
194
    v8::Handle<v8::ObjectTemplate> raw_template = v8::ObjectTemplate::New();
194
    v8::Handle<v8::ObjectTemplate> raw_template = v8::ObjectTemplate::New();
195
   
195
 
196
    raw_template->Set(v8::String::New("Log"), v8::FunctionTemplate::New(Manager::Export_Log));
196
    raw_template->Set(v8::String::New("Log"), v8::FunctionTemplate::New(Manager::Export_Log));
197
   
197
 
198
    for (unsigned int c = 0; c < this->plugins_.size(); c++)
198
    for (unsigned int c = 0; c < this->plugins_.size(); c++)
199
    {
199
    {
200
        ExportFunctionList export_functions = this->plugins_[c]->GetExportFunctions();
200
        ExportFunctionList export_functions = this->plugins_[c]->GetExportFunctions();
201
       
201
 
202
        for (ExportFunctionList::iterator it = export_functions.begin(); it != export_functions.end(); it++)
202
        for (ExportFunctionList::iterator it = export_functions.begin(); it != export_functions.end(); it++)
203
        {
203
        {
204
            raw_template->Set(v8::String::New(it->first.data()), v8::FunctionTemplate::New(it->second));
204
            raw_template->Set(v8::String::New(it->first.data()), v8::FunctionTemplate::New(it->second));
205
        }
205
        }
206
    }
206
    }
207
   
207
 
208
    this->context_ = v8::Context::New(NULL, raw_template);
208
    this->context_ = v8::Context::New(NULL, raw_template);
209
   
209
 
210
   
210
 
211
    v8::Context::Scope context_scope(this->context_);
211
    v8::Context::Scope context_scope(this->context_);
212
   
212
 
213
    for (unsigned int c = 0; c < this->plugins_.size(); c++)
213
    for (unsigned int c = 0; c < this->plugins_.size(); c++)
214
    {
214
    {
215
        this->LoadScript("plugin/" + this->plugins_[c]->GetName() + ".js");
215
        this->LoadScript("plugin/" + this->plugins_[c]->GetName() + ".js");
216
    }
216
    }
217
   
217
 
218
    this->object_template_ = v8::Persistent<v8::ObjectTemplate>::New(raw_template);
218
    this->object_template_ = v8::Persistent<v8::ObjectTemplate>::New(raw_template);
219
   
219
 
220
    for (unsigned int c = 0; c < this->plugins_.size(); c++)
220
    for (unsigned int c = 0; c < this->plugins_.size(); c++)
221
    {
221
    {
222
        this->plugins_[c]->InitializeDone();
222
        this->plugins_[c]->InitializeDone();
223
    }
223
    }
224
}
224
}
225
 
225
 
226
bool Manager::ImportFunction(std::string functionname)
226
bool Manager::ImportFunction(std::string functionname)
227
{
227
{
228
    v8::Locker lock;
228
    v8::Locker lock;
229
    v8::Context::Scope context_scope(this->context_);
229
    v8::Context::Scope context_scope(this->context_);
230
   
230
 
231
    v8::TryCatch try_catch;
231
    v8::TryCatch try_catch;
232
   
232
 
233
    v8::Handle<v8::String> process_name = v8::String::New(functionname.data());
233
    v8::Handle<v8::String> process_name = v8::String::New(functionname.data());
234
    v8::Handle<v8::Value> process_val = this->context_->Global()->Get(process_name);
234
    v8::Handle<v8::Value> process_val = this->context_->Global()->Get(process_name);
235
   
235
 
236
    if (!process_val->IsFunction())
236
    if (!process_val->IsFunction())
237
    {
237
    {
238
        LOG.Error(functionname + " was not found to be a function!");
238
        LOG.Error(functionname + " was not found to be a function!");
239
        return false;
239
        return false;
240
    }
240
    }
241
   
241
 
242
    v8::Handle<v8::Function> process_fun = v8::Handle<v8::Function>::Cast(process_val);
242
    v8::Handle<v8::Function> process_fun = v8::Handle<v8::Function>::Cast(process_val);
243
   
243
 
244
    this->functions_[functionname] = v8::Persistent<v8::Function>::New(process_fun);
244
    this->functions_[functionname] = v8::Persistent<v8::Function>::New(process_fun);
245
   
245
 
246
    return true;
246
    return true;
247
}
247
}
248
 
248
 
249
bool Manager::LoadScript(std::string scriptname)
249
bool Manager::LoadScript(std::string scriptname)
250
{
250
{
251
    for (unsigned int n = 0; n < this->loaded_scripts_.size(); n++)
251
    for (unsigned int n = 0; n < this->loaded_scripts_.size(); n++)
252
    {
252
    {
253
        if (this->loaded_scripts_[n] == scriptname)
253
        if (this->loaded_scripts_[n] == scriptname)
254
        {
254
        {
255
            LOG.Debug(scriptname + " is already loaded.");
255
            LOG.Debug(scriptname + " is already loaded.");
256
            return true;
256
            return true;
257
        }
257
        }
258
    }
258
    }
259
   
259
 
260
    std::string path = this->user_script_path_ + scriptname;
260
    std::string path = this->user_script_path_ + scriptname;
261
    std::ifstream file(path.data());
261
    std::ifstream file(path.data());
262
   
262
 
263
    if (!file.is_open())
263
    if (!file.is_open())
264
    {
264
    {
265
        path = this->script_path_ + scriptname;
265
        path = this->script_path_ + scriptname;
266
        file.open(path.data());
266
        file.open(path.data());
267
       
267
 
268
        if (!file.is_open())
268
        if (!file.is_open())
269
        {
269
        {
270
            file.close();
270
            file.close();
271
            LOG.Warning("Could not load " + scriptname + ".");
271
            LOG.Warning("Could not load " + scriptname + ".");
272
            return false;
272
            return false;
273
        }
273
        }
274
    }
274
    }
275
   
275
 
276
    std::string code = "";
276
    std::string code = "";
277
    std::string line;
277
    std::string line;
278
   
278
 
279
    while (!file.eof())
279
    while (!file.eof())
280
    {
280
    {
281
        std::getline(file, line);
281
        std::getline(file, line);
282
       
282
 
283
        if (file.eof())
283
        if (file.eof())
284
        {
284
        {
285
            break;
285
            break;
286
        }
286
        }
287
       
287
 
288
        code += line + "\n";
288
        code += line + "\n";
289
    }
289
    }
290
   
290
 
291
    code += "\n";
291
    code += "\n";
292
   
292
 
293
    file.close();
293
    file.close();
294
   
294
 
295
    v8::Locker lock;
295
    v8::Locker lock;
296
    v8::Context::Scope context_scope(this->context_);
296
    v8::Context::Scope context_scope(this->context_);
297
   
297
 
298
    v8::TryCatch try_catch;
298
    v8::TryCatch try_catch;
299
   
299
 
300
    v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(code.data(), code.length()),
300
    v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(code.data(), code.length()),
301
                                                        v8::String::New(scriptname.data(), scriptname.length()));
301
                                                        v8::String::New(scriptname.data(), scriptname.length()));
302
   
302
 
303
    if (script.IsEmpty())
303
    if (script.IsEmpty())
304
    {
304
    {
305
        LOG.Error(this->FormatException(try_catch));
305
        LOG.Error(this->FormatException(try_catch));
306
        return false;
306
        return false;
307
    }
307
    }
308
    else
308
    else
309
    {
309
    {
310
        Value result = script->Run();
310
        Value result = script->Run();
311
       
311
 
312
        if (result.IsEmpty())
312
        if (result.IsEmpty())
313
        {
313
        {
314
            LOG.Error(this->FormatException(try_catch));
314
            LOG.Error(this->FormatException(try_catch));
315
            return false;
315
            return false;
316
        }
316
        }
317
    }
317
    }
318
   
318
 
319
    this->loaded_scripts_.push_back(scriptname);
319
    this->loaded_scripts_.push_back(scriptname);
320
   
320
 
321
    LOG.Info("\033[32mLoaded " + scriptname + " successfully.\033[0m");
321
    LOG.Info("\033[32mLoaded " + scriptname + " successfully.\033[0m");
322
    return true;
322
    return true;
323
}
323
}
324
 
324
 
325
void Manager::ExecuteHandler(std::string plugin_name, unsigned int request_id, std::string code)
325
void Manager::ExecuteHandler(std::string plugin_name, unsigned int request_id, std::string code)
326
{
326
{
327
    std::string scriptname = "execute_" + plugin_name + ".js";
327
    std::string scriptname = "execute_" + plugin_name + ".js";
328
   
328
 
329
    v8::Locker lock;
329
    v8::Locker lock;
330
    v8::Context::Scope context_scope(this->context_);
330
    v8::Context::Scope context_scope(this->context_);
331
   
331
 
332
    v8::TryCatch try_catch;
332
    v8::TryCatch try_catch;
333
   
333
 
334
    v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(code.data(), code.length()),
334
    v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(code.data(), code.length()),
335
                                                        v8::String::New(scriptname.data(), scriptname.length()));
335
                                                        v8::String::New(scriptname.data(), scriptname.length()));
336
   
336
 
337
    this->current_plugin_name_ = plugin_name;
337
    this->current_plugin_name_ = plugin_name;
338
    this->current_request_id_ = request_id;
338
    this->current_request_id_ = request_id;
339
   
339
 
340
    if (script.IsEmpty())
340
    if (script.IsEmpty())
341
    {
341
    {
342
        this->CallOutput(this->FormatException(try_catch));
342
        this->CallOutput(this->FormatException(try_catch));
343
    }
343
    }
344
    else
344
    else
345
    {
345
    {
346
        Value result = script->Run();
346
        Value result = script->Run();
347
       
347
 
348
        if (result.IsEmpty())
348
        if (result.IsEmpty())
349
        {
349
        {
350
            this->CallOutput(this->FormatException(try_catch));
350
            this->CallOutput(this->FormatException(try_catch));
351
        }
351
        }
352
        else
352
        else
353
        {
353
        {
354
            v8::String::AsciiValue response(result->ToString());
354
            v8::String::AsciiValue response(result->ToString());
355
            this->CallResult(std::string(*response));
355
            this->CallResult(std::string(*response));
356
        }
356
        }
357
    }
357
    }
358
   
358
 
359
    this->current_plugin_name_ = "";
359
    this->current_plugin_name_ = "";
360
    this->current_request_id_ = 0;
360
    this->current_request_id_ = 0;
361
}
361
}
362
 
362
 
363
std::string Manager::FormatException(v8::TryCatch& try_catch)
363
std::string Manager::FormatException(v8::TryCatch& try_catch)
364
{
364
{
365
    v8::Locker lock;
365
    v8::Locker lock;
366
    v8::Context::Scope context_scope(this->context_);
366
    v8::Context::Scope context_scope(this->context_);
367
   
367
 
368
    std::string result;
368
    std::string result;
369
   
369
 
370
    v8::HandleScope handle_scope;
370
    v8::HandleScope handle_scope;
371
    v8::String::Utf8Value exception(try_catch.Exception());
371
    v8::String::Utf8Value exception(try_catch.Exception());
372
    v8::Handle<v8::Message> message = try_catch.Message();
372
    v8::Handle<v8::Message> message = try_catch.Message();
373
   
373
 
374
    if (message.IsEmpty())
374
    if (message.IsEmpty())
375
    {
375
    {
376
        result = *exception;
376
        result = *exception;
377
    }
377
    }
378
    else
378
    else
379
    {
379
    {
380
        v8::String::Utf8Value filename(message->GetScriptResourceName());
380
        v8::String::Utf8Value filename(message->GetScriptResourceName());
381
       
381
 
382
        result = std::string(*filename) + ":" + boost::lexical_cast<std::string>(message->GetLineNumber()) + ": " + std::string(*exception);
382
        result = std::string(*filename) + ":" + boost::lexical_cast<std::string>(message->GetLineNumber()) + ": " + std::string(*exception);
383
       
383
 
384
        /*v8::String::Utf8Value sourceline(message->GetSourceLine());
384
        /*v8::String::Utf8Value sourceline(message->GetSourceLine());
385
        string strSourceline = *sourceline;
385
        string strSourceline = *sourceline;
386
       
386
 
387
        result += strSourceline + "\n";
387
        result += strSourceline + "\n";
388
       
388
 
389
        string underline = rpad("", message->GetStartColumn(), ' ');
389
        string underline = rpad("", message->GetStartColumn(), ' ');
390
        underline = rpad(underline, message->GetEndColumn(), '^');
390
        underline = rpad(underline, message->GetEndColumn(), '^');
391
       
391
 
392
        result += underline + "\n";*/
392
        result += underline + "\n";*/
393
    }
393
    }
394
   
394
 
395
    return result;
395
    return result;
396
}
396
}
397
   
397
 
398
}; // namespace vm
398
}; // namespace vm
399
}; // namespace atom
399
}; // namespace atom
400
 
400