Commit 76aad1a0 authored by yangguo@chromium.org's avatar yangguo@chromium.org

Fixed a known issue in D8 (read file), enabled D8 shared library build on Windows.

Review URL: http://codereview.chromium.org/7586001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8853 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 528a3301
......@@ -1394,10 +1394,12 @@ def BuildSpecific(env, mode, env_overrides, tools):
env['SONAME'] = soname
# Build the object files by invoking SCons recursively.
d8_env = Environment(tools=tools)
d8_env.Replace(**context.flags['d8'])
(object_files, shell_files, mksnapshot, preparser_files) = env.SConscript(
join('src', 'SConscript'),
build_dir=join('obj', target_id),
exports='context tools',
exports='context tools d8_env',
duplicate=False
)
......@@ -1426,8 +1428,6 @@ def BuildSpecific(env, mode, env_overrides, tools):
context.library_targets.append(library)
context.library_targets.append(preparser_library)
d8_env = Environment(tools=tools)
d8_env.Replace(**context.flags['d8'])
context.ApplyEnvOverrides(d8_env)
if context.options['library'] == 'static':
shell = d8_env.Program('d8' + suffix, object_files + shell_files)
......
......@@ -3606,7 +3606,7 @@ class V8EXPORT Locker {
/**
* Returns whether v8::Locker is being used by this V8 instance.
*/
static bool IsActive() { return active_; }
static bool IsActive();
private:
bool has_lock_;
......
......@@ -32,6 +32,7 @@ sys.path.append(join(root_dir, 'tools'))
import js2c
Import('context')
Import('tools')
Import('d8_env')
SOURCES = {
......@@ -340,7 +341,7 @@ def ConfigureObjectFiles():
else:
d8_files = context.GetRelevantSources(D8_FULL_FILES)
d8_objs = [d8_js_obj]
d8_objs.append(context.ConfigureObject(env, [d8_files]))
d8_objs.append(context.ConfigureObject(d8_env, [d8_files]))
# Combine the JavaScript library files into a single C++ file and
# compile it.
......
......@@ -26,8 +26,8 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef V8_SHARED
#define USING_V8_SHARED
#ifdef USING_V8_SHARED // Defined when linking against shared lib on Windows.
#define V8_SHARED
#endif
#ifdef COMPRESS_STARTUP_DATA_BZ2
......@@ -37,15 +37,16 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
#include <assert.h>
#include "../include/v8-testing.h"
#endif // USING_V8_SHARED
#endif // V8_SHARED
#include "d8.h"
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
#include "api.h"
#include "checks.h"
#include "d8-debug.h"
......@@ -53,20 +54,20 @@
#include "natives.h"
#include "platform.h"
#include "v8.h"
#endif // USING_V8_SHARED
#endif // V8_SHARED
#if !defined(_WIN32) && !defined(_WIN64)
#include <unistd.h> // NOLINT
#endif
#ifdef USING_V8_SHARED
#ifndef ASSERT
#define ASSERT(condition) assert(condition)
#endif // USING_V8_SHARED
#endif
namespace v8 {
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
LineEditor *LineEditor::first_ = NULL;
const char* Shell::kHistoryFileName = ".d8_history";
......@@ -116,20 +117,20 @@ CounterCollection Shell::local_counters_;
CounterCollection* Shell::counters_ = &local_counters_;
i::Mutex* Shell::context_mutex_(i::OS::CreateMutex());
Persistent<Context> Shell::utility_context_;
#endif // USING_V8_SHARED
#endif // V8_SHARED
Persistent<Context> Shell::evaluation_context_;
ShellOptions Shell::options;
const char* Shell::kPrompt = "d8> ";
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
bool CounterMap::Match(void* key1, void* key2) {
const char* name1 = reinterpret_cast<const char*>(key1);
const char* name2 = reinterpret_cast<const char*>(key2);
return strcmp(name1, name2) == 0;
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
// Converts a V8 value to a C string.
......@@ -143,11 +144,11 @@ bool Shell::ExecuteString(Handle<String> source,
Handle<Value> name,
bool print_result,
bool report_exceptions) {
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
bool FLAG_debugger = i::FLAG_debugger;
#else
bool FLAG_debugger = false;
#endif // USING_V8_SHARED
#endif // V8_SHARED
HandleScope handle_scope;
TryCatch try_catch;
options.script_executed = true;
......@@ -276,9 +277,9 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args,
String::New("Array constructor needs one parameter."));
}
static const int kMaxLength = 0x3fffffff;
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
ASSERT(kMaxLength == i::ExternalArray::kMaxLength);
#endif // USING_V8_SHARED
#endif // V8_SHARED
size_t length = 0;
if (args[0]->IsUint32()) {
length = args[0]->Uint32Value();
......@@ -378,9 +379,9 @@ Handle<Value> Shell::Yield(const Arguments& args) {
Handle<Value> Shell::Quit(const Arguments& args) {
int exit_code = args[0]->Int32Value();
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
OnExit();
#endif // USING_V8_SHARED
#endif // V8_SHARED
exit(exit_code);
return Undefined();
}
......@@ -429,7 +430,7 @@ void Shell::ReportException(v8::TryCatch* try_catch) {
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
HandleScope handle_scope;
Context::Scope context_scope(utility_context_);
......@@ -464,10 +465,10 @@ Handle<Value> Shell::DebugCommandToJSONRequest(Handle<String> command) {
return val;
}
#endif // ENABLE_DEBUGGER_SUPPORT
#endif // USING_V8_SHARED
#endif // V8_SHARED
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
int32_t* Counter::Bind(const char* name, bool is_histogram) {
int i;
for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
......@@ -615,7 +616,7 @@ void Shell::InstallUtilityScript() {
}
#endif // ENABLE_DEBUGGER_SUPPORT
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
#ifdef COMPRESS_STARTUP_DATA_BZ2
......@@ -682,11 +683,11 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() {
global_template->Set(String::New("lol_is_enabled"), Boolean::New(false));
#endif
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
Handle<ObjectTemplate> os_templ = ObjectTemplate::New();
AddOSMethods(os_templ);
global_template->Set(String::New("os"), os_templ);
#endif // USING_V8_SHARED
#endif // V8_SHARED
return global_template;
}
......@@ -702,7 +703,7 @@ void Shell::Initialize() {
}
#endif
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
Shell::counter_map_ = new CounterMap();
// Set up counters
if (i::StrLength(i::FLAG_map_counters) != 0)
......@@ -712,10 +713,10 @@ void Shell::Initialize() {
V8::SetCreateHistogramFunction(CreateHistogram);
V8::SetAddHistogramSampleFunction(AddHistogramSample);
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
if (options.test_shell) return;
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
Locker lock;
HandleScope scope;
Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
......@@ -727,21 +728,21 @@ void Shell::Initialize() {
v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true);
}
#endif // ENABLE_DEBUGGER_SUPPORT
#endif // USING_V8_SHARED
#endif // V8_SHARED
}
Persistent<Context> Shell::CreateEvaluationContext() {
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
// This needs to be a critical section since this is not thread-safe
i::ScopedLock lock(context_mutex_);
#endif // USING_V8_SHARED
#endif // V8_SHARED
// Initialize the global objects
Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
Persistent<Context> context = Context::New(NULL, global_template);
Context::Scope scope(context);
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
i::JSArguments js_args = i::FLAG_js_arguments;
i::Handle<i::FixedArray> arguments_array =
FACTORY->NewFixedArray(js_args.argc());
......@@ -754,12 +755,12 @@ Persistent<Context> Shell::CreateEvaluationContext() {
FACTORY->NewJSArrayWithElements(arguments_array);
context->Global()->Set(String::New("arguments"),
Utils::ToLocal(arguments_jsarray));
#endif // USING_V8_SHARED
#endif // V8_SHARED
return context;
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
void Shell::OnExit() {
if (i::FLAG_dump_counters) {
printf("+----------------------------------------+-------------+\n");
......@@ -779,18 +780,34 @@ void Shell::OnExit() {
if (counters_file_ != NULL)
delete counters_file_;
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
static FILE* FOpen(const char* path, const char* mode) {
#if (defined(_WIN32) || defined(_WIN64))
FILE* result;
if (fopen_s(&result, path, mode) == 0) {
return result;
} else {
return NULL;
}
#else
FILE* file = fopen(path, mode);
if (file == NULL) return NULL;
struct stat file_stat;
if (fstat(fileno(file), &file_stat) != 0) return NULL;
bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
if (is_regular_file) return file;
fclose(file);
return NULL;
#endif
}
static char* ReadChars(const char* name, int* size_out) {
// Release the V8 lock while reading files.
v8::Unlocker unlocker(Isolate::GetCurrent());
#ifndef USING_V8_SHARED
FILE* file = i::OS::FOpen(name, "rb");
#else
// TODO(yangguo@chromium.org): reading from a directory hangs!
FILE* file = fopen(name, "rb");
#endif // USING_V8_SHARED
FILE* file = FOpen(name, "rb");
if (file == NULL) return NULL;
fseek(file, 0, SEEK_END);
......@@ -809,7 +826,7 @@ static char* ReadChars(const char* name, int* size_out) {
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
static char* ReadToken(char* data, char token) {
char* next = i::OS::StrChr(data, token);
if (next != NULL) {
......@@ -829,7 +846,7 @@ static char* ReadLine(char* data) {
static char* ReadWord(char* data) {
return ReadToken(data, ' ');
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
// Reads a file into a v8 string.
......@@ -848,7 +865,7 @@ void Shell::RunShell() {
Context::Scope context_scope(evaluation_context_);
HandleScope handle_scope;
Handle<String> name = String::New("(d8)");
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
LineEditor* editor = LineEditor::Get();
printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
if (i::FLAG_debugger) {
......@@ -871,12 +888,12 @@ void Shell::RunShell() {
if (fgets(buffer, kBufferSize, stdin) == NULL) break;
ExecuteString(String::New(buffer), name, true, true);
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
printf("\n");
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
class ShellThread : public i::Thread {
public:
ShellThread(int no, i::Vector<const char> files)
......@@ -929,7 +946,7 @@ void ShellThread::Run() {
ptr = next_line;
}
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
void SourceGroup::ExitShell(int exit_code) {
......@@ -976,32 +993,16 @@ void SourceGroup::Execute() {
Handle<String> SourceGroup::ReadFile(const char* name) {
#ifndef USING_V8_SHARED
FILE* file = i::OS::FOpen(name, "rb");
#else
// TODO(yangguo@chromium.org): reading from a directory hangs!
FILE* file = fopen(name, "rb");
#endif // USING_V8_SHARED
if (file == NULL) return Handle<String>();
fseek(file, 0, SEEK_END);
int size = ftell(file);
rewind(file);
char* chars = new char[size + 1];
chars[size] = '\0';
for (int i = 0; i < size;) {
int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
i += read;
}
fclose(file);
int size;
const char* chars = ReadChars(name, &size);
if (chars == NULL) return Handle<String>();
Handle<String> result = String::New(chars, size);
delete[] chars;
return result;
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
i::Thread::Options SourceGroup::GetThreadOptions() {
i::Thread::Options options;
options.name = "IsolateThread";
......@@ -1053,7 +1054,7 @@ void SourceGroup::WaitForThread() {
done_semaphore_->Wait();
}
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
bool Shell::SetOptions(int argc, char* argv[]) {
......@@ -1075,23 +1076,23 @@ bool Shell::SetOptions(int argc, char* argv[]) {
options.test_shell = true;
argv[i] = NULL;
} else if (strcmp(argv[i], "--preemption") == 0) {
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
printf("D8 with shared library does not support multi-threading\n");
return false;
#else
options.use_preemption = true;
argv[i] = NULL;
#endif // USING_V8_SHARED
#endif // V8_SHARED
} else if (strcmp(argv[i], "--no-preemption") == 0) {
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
printf("D8 with shared library does not support multi-threading\n");
return false;
#else
options.use_preemption = false;
argv[i] = NULL;
#endif // USING_V8_SHARED
#endif // V8_SHARED
} else if (strcmp(argv[i], "--preemption-interval") == 0) {
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
printf("D8 with shared library does not support multi-threading\n");
return false;
#else
......@@ -1110,19 +1111,19 @@ bool Shell::SetOptions(int argc, char* argv[]) {
printf("Missing value for --preemption-interval\n");
return false;
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
} else if (strcmp(argv[i], "-f") == 0) {
// Ignore any -f flags for compatibility with other stand-alone
// JavaScript engines.
continue;
} else if (strcmp(argv[i], "--isolate") == 0) {
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
printf("D8 with shared library does not support multi-threading\n");
return false;
#endif // USING_V8_SHARED
#endif // V8_SHARED
options.num_isolates++;
}
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
else if (strcmp(argv[i], "--dump-counters") == 0) {
printf("D8 with shared library does not include counters\n");
return false;
......@@ -1133,10 +1134,10 @@ bool Shell::SetOptions(int argc, char* argv[]) {
printf("Javascript debugger not included\n");
return false;
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
// Run parallel threads if we are not using --isolate
for (int i = 1; i < argc; i++) {
if (argv[i] == NULL) continue;
......@@ -1157,9 +1158,10 @@ bool Shell::SetOptions(int argc, char* argv[]) {
}
argv[i] = NULL;
options.parallel_files->Add(i::Vector<const char>(files, size));
delete[] files;
}
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
......@@ -1184,7 +1186,7 @@ bool Shell::SetOptions(int argc, char* argv[]) {
int Shell::RunMain(int argc, char* argv[]) {
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
i::List<i::Thread*> threads(1);
if (options.parallel_files != NULL)
for (int i = 0; i < options.parallel_files->length(); i++) {
......@@ -1197,7 +1199,7 @@ int Shell::RunMain(int argc, char* argv[]) {
for (int i = 1; i < options.num_isolates; ++i) {
options.isolate_sources[i].StartExecuteInThread();
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
{ // NOLINT
Locker lock;
HandleScope scope;
......@@ -1213,17 +1215,17 @@ int Shell::RunMain(int argc, char* argv[]) {
context.Dispose();
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
// Start preemption if threads have been created and preemption is enabled.
if (options.parallel_files != NULL
&& threads.length() > 0
&& options.use_preemption) {
Locker::StartPreemption(options.preemption_interval);
}
#endif // USING_V8_SHARED
#endif // V8_SHARED
}
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
for (int i = 1; i < options.num_isolates; ++i) {
options.isolate_sources[i].WaitForThread();
}
......@@ -1236,7 +1238,7 @@ int Shell::RunMain(int argc, char* argv[]) {
}
OnExit();
#endif // USING_V8_SHARED
#endif // V8_SHARED
return 0;
}
......@@ -1264,14 +1266,14 @@ int Shell::Main(int argc, char* argv[]) {
}
#if !defined(USING_V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
// Run remote debugger if requested, but never on --test
if (i::FLAG_remote_debugger && !options.test_shell) {
InstallUtilityScript();
RunRemoteDebugger(i::FLAG_debugger_port);
return 0;
}
#endif // !USING_V8_SHARED && ENABLE_DEBUGGER_SUPPORT
#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
// Run interactive shell if explicitly requested or if no script has been
// executed, but never on --test
......@@ -1279,9 +1281,9 @@ int Shell::Main(int argc, char* argv[]) {
if (( options.interactive_shell
|| !options.script_executed )
&& !options.test_shell ) {
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
InstallUtilityScript();
#endif // USING_V8_SHARED
#endif // V8_SHARED
RunShell();
}
......
......@@ -29,22 +29,22 @@
#define V8_D8_H_
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
#include "v8.h"
#include "allocation.h"
#include "hashmap.h"
#else
#include "../include/v8.h"
#endif // USING_V8_SHARED
#endif // V8_SHARED
namespace v8 {
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
namespace i = v8::internal;
#endif // USING_V8_SHARED
#endif // V8_SHARED
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
// A single counter in a counter collection.
class Counter {
public:
......@@ -117,17 +117,17 @@ class CounterMap {
static bool Match(void* key1, void* key2);
i::HashMap hash_map_;
};
#endif // USING_V8_SHARED
#endif // V8_SHARED
class SourceGroup {
public:
SourceGroup() :
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
next_semaphore_(v8::internal::OS::CreateSemaphore(0)),
done_semaphore_(v8::internal::OS::CreateSemaphore(0)),
thread_(NULL),
#endif // USING_V8_SHARED
#endif // V8_SHARED
argv_(NULL),
begin_offset_(0),
end_offset_(0) { }
......@@ -141,7 +141,7 @@ class SourceGroup {
void Execute();
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
void StartExecuteInThread();
void WaitForThread();
......@@ -165,7 +165,7 @@ class SourceGroup {
i::Semaphore* next_semaphore_;
i::Semaphore* done_semaphore_;
i::Thread* thread_;
#endif // USING_V8_SHARED
#endif // V8_SHARED
void ExitShell(int exit_code);
Handle<String> ReadFile(const char* name);
......@@ -179,11 +179,11 @@ class SourceGroup {
class ShellOptions {
public:
ShellOptions() :
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
use_preemption(true),
preemption_interval(10),
parallel_files(NULL),
#endif // USING_V8_SHARED
#endif // V8_SHARED
script_executed(false),
last_run(true),
stress_opt(false),
......@@ -193,11 +193,11 @@ class ShellOptions {
num_isolates(1),
isolate_sources(NULL) { }
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
bool use_preemption;
int preemption_interval;
i::List< i::Vector<const char> >* parallel_files;
#endif // USING_V8_SHARED
#endif // V8_SHARED
bool script_executed;
bool last_run;
bool stress_opt;
......@@ -208,11 +208,11 @@ class ShellOptions {
SourceGroup* isolate_sources;
};
#ifdef USING_V8_SHARED
#ifdef V8_SHARED
class Shell {
#else
class Shell : public i::AllStatic {
#endif // USING_V8_SHARED
#endif // V8_SHARED
public:
static bool ExecuteString(Handle<String> source,
Handle<Value> name,
......@@ -225,7 +225,7 @@ class Shell : public i::AllStatic {
static int RunMain(int argc, char* argv[]);
static int Main(int argc, char* argv[]);
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
static Handle<Array> GetCompletions(Handle<String> text,
Handle<String> full);
static void OnExit();
......@@ -236,7 +236,7 @@ class Shell : public i::AllStatic {
size_t buckets);
static void AddHistogramSample(void* histogram, int sample);
static void MapCounters(const char* name);
#endif // USING_V8_SHARED
#endif // V8_SHARED
#ifdef ENABLE_DEBUGGER_SUPPORT
static Handle<Object> DebugMessageDetails(Handle<String> message);
......@@ -300,15 +300,15 @@ class Shell : public i::AllStatic {
static Handle<Value> RemoveDirectory(const Arguments& args);
static void AddOSMethods(Handle<ObjectTemplate> os_template);
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
static const char* kHistoryFileName;
#endif // USING_V8_SHARED
#endif // V8_SHARED
static const char* kPrompt;
static ShellOptions options;
private:
static Persistent<Context> evaluation_context_;
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
static Persistent<Context> utility_context_;
static CounterMap* counter_map_;
// We statically allocate a set of local counters to be used if we
......@@ -320,7 +320,7 @@ class Shell : public i::AllStatic {
static Counter* GetCounter(const char* name, bool is_histogram);
static void InstallUtilityScript();
#endif // USING_V8_SHARED
#endif // V8_SHARED
static void Initialize();
static void RunShell();
static bool SetOptions(int argc, char* argv[]);
......@@ -332,7 +332,7 @@ class Shell : public i::AllStatic {
};
#ifndef USING_V8_SHARED
#ifndef V8_SHARED
class LineEditor {
public:
enum Type { DUMB = 0, READLINE = 1 };
......@@ -352,7 +352,7 @@ class LineEditor {
LineEditor* next_;
static LineEditor* first_;
};
#endif // USING_V8_SHARED
#endif // V8_SHARED
} // namespace v8
......
......@@ -94,6 +94,11 @@ bool Locker::IsLocked(v8::Isolate* isolate) {
}
bool Locker::IsActive() {
return active_;
}
Locker::~Locker() {
ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
if (has_lock_) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment