| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  |  * modification, are permitted provided that the following conditions are met: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 1. Redistributions of source code must retain the above copyright notice, this | 
					
						
							|  |  |  |  *    list of conditions and the following disclaimer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | 
					
						
							|  |  |  |  *    this list of conditions and the following disclaimer in the documentation | 
					
						
							|  |  |  |  *    and/or other materials provided with the distribution. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
					
						
							|  |  |  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
					
						
							|  |  |  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
					
						
							|  |  |  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
					
						
							|  |  |  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
					
						
							|  |  |  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
					
						
							|  |  |  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
					
						
							|  |  |  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
					
						
							|  |  |  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
					
						
							|  |  |  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-24 22:03:50 +01:00
										 |  |  | #include <AK/FlyString.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-09 21:13:55 +01:00
										 |  |  | #include <AK/HashMap.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-12 19:53:31 +01:00
										 |  |  | #include <AK/String.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <AK/Vector.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-23 12:53:09 -04:00
										 |  |  | #include <AK/Weakable.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-27 22:22:08 -07:00
										 |  |  | #include <LibJS/AST.h>
 | 
					
						
							| 
									
										
										
										
											2020-05-01 14:40:43 +02:00
										 |  |  | #include <LibJS/Console.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | #include <LibJS/Forward.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-16 14:20:30 +01:00
										 |  |  | #include <LibJS/Heap/Heap.h>
 | 
					
						
							| 
									
										
										
										
											2020-06-09 22:48:01 -07:00
										 |  |  | #include <LibJS/Runtime/ErrorTypes.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-24 22:03:50 +01:00
										 |  |  | #include <LibJS/Runtime/Exception.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-15 21:58:22 +02:00
										 |  |  | #include <LibJS/Runtime/LexicalEnvironment.h>
 | 
					
						
							| 
									
										
										
										
											2020-04-19 17:24:56 +02:00
										 |  |  | #include <LibJS/Runtime/MarkedValueList.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-16 14:20:30 +01:00
										 |  |  | #include <LibJS/Runtime/Value.h>
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace JS { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-11 21:09:20 +02:00
										 |  |  | enum class ScopeType { | 
					
						
							| 
									
										
										
										
											2020-03-23 19:19:03 +01:00
										 |  |  |     None, | 
					
						
							| 
									
										
										
										
											2020-03-11 21:09:20 +02:00
										 |  |  |     Function, | 
					
						
							|  |  |  |     Block, | 
					
						
							| 
									
										
										
										
											2020-03-24 14:37:39 +01:00
										 |  |  |     Try, | 
					
						
							| 
									
										
										
										
											2020-03-29 14:34:25 +02:00
										 |  |  |     Breakable, | 
					
						
							| 
									
										
										
										
											2020-04-05 00:22:42 +02:00
										 |  |  |     Continuable, | 
					
						
							| 
									
										
										
										
											2020-03-11 21:09:20 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | struct ScopeFrame { | 
					
						
							| 
									
										
										
										
											2020-03-11 21:09:20 +02:00
										 |  |  |     ScopeType type; | 
					
						
							| 
									
										
										
										
											2020-03-18 11:23:53 +01:00
										 |  |  |     NonnullRefPtr<ScopeNode> scope_node; | 
					
						
							| 
									
										
										
										
											2020-04-15 21:58:22 +02:00
										 |  |  |     bool pushed_environment { false }; | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  | struct CallFrame { | 
					
						
							| 
									
										
										
										
											2020-04-11 12:56:20 +01:00
										 |  |  |     FlyString function_name; | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  |     Value this_value; | 
					
						
							|  |  |  |     Vector<Value> arguments; | 
					
						
							| 
									
										
										
										
											2020-04-15 21:58:22 +02:00
										 |  |  |     LexicalEnvironment* environment { nullptr }; | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-12 19:53:31 +01:00
										 |  |  | struct Argument { | 
					
						
							| 
									
										
										
										
											2020-03-22 11:07:55 +01:00
										 |  |  |     FlyString name; | 
					
						
							| 
									
										
										
										
											2020-03-12 19:53:31 +01:00
										 |  |  |     Value value; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-06 19:22:12 +02:00
										 |  |  | typedef Vector<Argument, 8> ArgumentVector; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-23 12:53:09 -04:00
										 |  |  | class Interpreter : public Weakable<Interpreter> { | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2020-04-01 21:04:51 +02:00
										 |  |  |     template<typename GlobalObjectType, typename... Args> | 
					
						
							|  |  |  |     static NonnullOwnPtr<Interpreter> create(Args&&... args) | 
					
						
							| 
									
										
										
										
											2020-04-01 18:53:28 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-01 21:04:51 +02:00
										 |  |  |         auto interpreter = adopt_own(*new Interpreter); | 
					
						
							| 
									
										
										
										
											2020-06-20 15:40:48 +02:00
										 |  |  |         interpreter->m_global_object = interpreter->heap().allocate_without_global_object<GlobalObjectType>(forward<Args>(args)...); | 
					
						
							| 
									
										
										
										
											2020-04-18 13:18:06 +02:00
										 |  |  |         static_cast<GlobalObjectType*>(interpreter->m_global_object)->initialize(); | 
					
						
							| 
									
										
										
										
											2020-04-01 21:04:51 +02:00
										 |  |  |         return interpreter; | 
					
						
							| 
									
										
										
										
											2020-04-01 18:53:28 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 21:04:51 +02:00
										 |  |  |     ~Interpreter(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 21:25:16 +02:00
										 |  |  |     Value run(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block); | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 11:05:38 +02:00
										 |  |  |     GlobalObject& global_object(); | 
					
						
							|  |  |  |     const GlobalObject& global_object() const; | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-08 19:23:58 +01:00
										 |  |  |     Heap& heap() { return m_heap; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-28 13:36:59 -07:00
										 |  |  |     void unwind(ScopeType type, FlyString label = {}) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_unwind_until = type; | 
					
						
							|  |  |  |         m_unwind_until_label = label; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-05 00:09:48 +02:00
										 |  |  |     void stop_unwind() { m_unwind_until = ScopeType::None; } | 
					
						
							| 
									
										
										
										
											2020-05-28 13:36:59 -07:00
										 |  |  |     bool should_unwind_until(ScopeType type, FlyString label) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_unwind_until_label.is_null()) | 
					
						
							|  |  |  |             return m_unwind_until == type; | 
					
						
							|  |  |  |         return m_unwind_until == type && m_unwind_until_label == label; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-29 14:34:25 +02:00
										 |  |  |     bool should_unwind() const { return m_unwind_until != ScopeType::None; } | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 21:25:16 +02:00
										 |  |  |     Value get_variable(const FlyString& name, GlobalObject&); | 
					
						
							|  |  |  |     void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false); | 
					
						
							| 
									
										
										
										
											2020-03-09 21:13:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-27 12:37:27 +02:00
										 |  |  |     Reference get_reference(const FlyString& name); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-06 16:57:22 -07:00
										 |  |  |     Symbol* get_global_symbol(const String& description); | 
					
						
							|  |  |  |     Symbol* get_well_known_symbol(const String& description) const; | 
					
						
							|  |  |  |     const HashMap<String, Symbol*>& get_well_known_symbol_map(Badge<SymbolConstructor>) const { return m_well_known_symbol_map; }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-15 15:12:34 +01:00
										 |  |  |     void gather_roots(Badge<Heap>, HashTable<Cell*>&); | 
					
						
							| 
									
										
										
										
											2020-03-09 21:29:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 21:25:16 +02:00
										 |  |  |     void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&); | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  |     void exit_scope(const ScopeNode&); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 13:41:54 +01:00
										 |  |  |     Value call(Function&, Value this_value, Optional<MarkedValueList> arguments = {}); | 
					
						
							| 
									
										
										
										
											2020-06-20 17:53:33 +02:00
										 |  |  |     Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject&); | 
					
						
							| 
									
										
										
										
											2020-03-18 15:20:49 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-22 11:07:55 +01:00
										 |  |  |     CallFrame& push_call_frame() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-15 21:58:22 +02:00
										 |  |  |         m_call_stack.append({ {}, js_undefined(), {}, nullptr }); | 
					
						
							| 
									
										
										
										
											2020-03-22 11:07:55 +01:00
										 |  |  |         return m_call_stack.last(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  |     void pop_call_frame() { m_call_stack.take_last(); } | 
					
						
							| 
									
										
										
										
											2020-03-28 22:48:35 +01:00
										 |  |  |     const CallFrame& call_frame() { return m_call_stack.last(); } | 
					
						
							| 
									
										
										
										
											2020-04-15 21:58:22 +02:00
										 |  |  |     const Vector<CallFrame>& call_stack() { return m_call_stack; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void push_environment(LexicalEnvironment*); | 
					
						
							|  |  |  |     void pop_environment(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const LexicalEnvironment* current_environment() const { return m_call_stack.last().environment; } | 
					
						
							|  |  |  |     LexicalEnvironment* current_environment() { return m_call_stack.last().environment; } | 
					
						
							| 
									
										
										
										
											2020-03-28 22:48:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-27 22:22:08 -07:00
										 |  |  |     bool in_strict_mode() const { return m_scope_stack.last().scope_node->in_strict_mode(); } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-25 14:49:56 -07:00
										 |  |  |     template<typename Callback> | 
					
						
							|  |  |  |     void for_each_argument(Callback callback) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_call_stack.is_empty()) | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         for (auto& value : m_call_stack.last().arguments) | 
					
						
							|  |  |  |             callback(value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 22:38:59 +02:00
										 |  |  |     size_t argument_count() const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_call_stack.is_empty()) | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         return m_call_stack.last().arguments.size(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Value argument(size_t index) const | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (m_call_stack.is_empty()) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         auto& arguments = m_call_stack.last().arguments; | 
					
						
							|  |  |  |         return index < arguments.size() ? arguments[index] : js_undefined(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 21:10:22 +02:00
										 |  |  |     Value this_value(Object& global_object) const | 
					
						
							| 
									
										
										
										
											2020-03-15 15:01:10 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  |         if (m_call_stack.is_empty()) | 
					
						
							| 
									
										
										
										
											2020-06-08 21:10:22 +02:00
										 |  |  |             return &global_object; | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  |         return m_call_stack.last().this_value; | 
					
						
							| 
									
										
										
										
											2020-03-15 15:01:10 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-10 14:06:52 +02:00
										 |  |  |     Exception* exception() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return m_exception; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-24 14:37:39 +01:00
										 |  |  |     void clear_exception() { m_exception = nullptr; } | 
					
						
							| 
									
										
										
										
											2020-03-24 22:03:50 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     template<typename T, typename... Args> | 
					
						
							|  |  |  |     Value throw_exception(Args&&... args) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-17 19:31:48 +02:00
										 |  |  |         return throw_exception(T::create(global_object(), forward<Args>(args)...)); | 
					
						
							| 
									
										
										
										
											2020-03-24 22:03:50 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Value throw_exception(Exception*); | 
					
						
							|  |  |  |     Value throw_exception(Value value) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-06-20 15:40:48 +02:00
										 |  |  |         return throw_exception(heap().allocate<Exception>(global_object(), value)); | 
					
						
							| 
									
										
										
										
											2020-03-24 22:03:50 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-15 15:11:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-09 22:48:01 -07:00
										 |  |  |     template<typename T, typename... Args> | 
					
						
							|  |  |  |     Value throw_exception(ErrorType type, Args&&... args) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return throw_exception(T::create(global_object(), String::format(type.message(), forward<Args>(args)...))); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 23:45:13 +02:00
										 |  |  |     Value last_value() const { return m_last_value; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 12:08:35 +01:00
										 |  |  |     bool underscore_is_last_value() const { return m_underscore_is_last_value; } | 
					
						
							|  |  |  |     void set_underscore_is_last_value(bool b) { m_underscore_is_last_value = b; } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-01 14:40:43 +02:00
										 |  |  |     Console& console() { return m_console; } | 
					
						
							| 
									
										
										
										
											2020-05-04 14:18:15 +02:00
										 |  |  |     const Console& console() const { return m_console; } | 
					
						
							| 
									
										
										
										
											2020-05-01 07:14:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-04 14:18:15 +02:00
										 |  |  |     String join_arguments() const; | 
					
						
							| 
									
										
										
										
											2020-05-04 12:34:49 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 13:31:21 -05:00
										 |  |  |     Value resolve_this_binding() const; | 
					
						
							|  |  |  |     const LexicalEnvironment* get_this_environment() const; | 
					
						
							|  |  |  |     Value get_new_target() const; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-14 13:56:49 +02:00
										 |  |  | private: | 
					
						
							| 
									
										
										
										
											2020-04-01 21:04:51 +02:00
										 |  |  |     Interpreter(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-08 19:23:58 +01:00
										 |  |  |     Heap m_heap; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 23:45:13 +02:00
										 |  |  |     Value m_last_value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  |     Vector<ScopeFrame> m_scope_stack; | 
					
						
							| 
									
										
										
										
											2020-03-17 11:00:09 +01:00
										 |  |  |     Vector<CallFrame> m_call_stack; | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Object* m_global_object { nullptr }; | 
					
						
							| 
									
										
										
										
											2020-04-10 12:42:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-06 16:57:22 -07:00
										 |  |  |     HashMap<String, Symbol*> m_well_known_symbol_map; | 
					
						
							|  |  |  |     HashMap<String, Symbol*> m_global_symbol_map; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-24 22:03:50 +01:00
										 |  |  |     Exception* m_exception { nullptr }; | 
					
						
							| 
									
										
										
										
											2020-03-23 19:19:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ScopeType m_unwind_until { ScopeType::None }; | 
					
						
							| 
									
										
										
										
											2020-05-28 13:36:59 -07:00
										 |  |  |     FlyString m_unwind_until_label; | 
					
						
							| 
									
										
										
										
											2020-05-01 07:14:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-08 12:08:35 +01:00
										 |  |  |     bool m_underscore_is_last_value { false }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-01 14:40:43 +02:00
										 |  |  |     Console m_console; | 
					
						
							| 
									
										
										
										
											2020-03-07 19:42:11 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |