blob: 03a6ab6495d02f2f8c15316e7565978dcd09e3a6 [file] [log] [blame]
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001// Copyright 2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
ager@chromium.orga74f0da2008-12-03 16:05:52 +000029#include <stdlib.h>
30
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000031#include "d8.h"
32#include "debug.h"
33#include "api.h"
34#include "natives.h"
35
36
37namespace v8 {
38
39
40const char* Shell::kHistoryFileName = ".d8_history";
41const char* Shell::kPrompt = "d8> ";
42
43
44LineEditor *LineEditor::first_ = NULL;
45
46
47LineEditor::LineEditor(Type type, const char* name)
48 : type_(type),
49 name_(name),
50 next_(first_) {
51 first_ = this;
52}
53
54
55LineEditor* LineEditor::Get() {
56 LineEditor* current = first_;
57 LineEditor* best = current;
58 while (current != NULL) {
59 if (current->type_ > best->type_)
60 best = current;
61 current = current->next_;
62 }
63 return best;
64}
65
66
67class DumbLineEditor: public LineEditor {
68 public:
69 DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { }
70 virtual i::SmartPointer<char> Prompt(const char* prompt);
71};
72
73
74static DumbLineEditor dumb_line_editor;
75
76
77i::SmartPointer<char> DumbLineEditor::Prompt(const char* prompt) {
78 static const int kBufferSize = 256;
79 char buffer[kBufferSize];
80 printf("%s", prompt);
81 char* str = fgets(buffer, kBufferSize, stdin);
ager@chromium.orga74f0da2008-12-03 16:05:52 +000082 return i::SmartPointer<char>(str ? i::StrDup(str) : str);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000083}
84
85
86Shell::CounterMap Shell::counter_map_;
ager@chromium.orga74f0da2008-12-03 16:05:52 +000087i::OS::MemoryMappedFile* Shell::counters_file_ = NULL;
88CounterCollection Shell::local_counters_;
89CounterCollection* Shell::counters_ = &local_counters_;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000090Persistent<Context> Shell::utility_context_;
91Persistent<Context> Shell::evaluation_context_;
92
93
94// Executes a string within the current v8 context.
95bool Shell::ExecuteString(Handle<String> source,
96 Handle<Value> name,
97 bool print_result,
98 bool report_exceptions) {
99 HandleScope handle_scope;
100 TryCatch try_catch;
101 Handle<Script> script = Script::Compile(source, name);
102 if (script.IsEmpty()) {
103 // Print errors that happened during compilation.
104 if (report_exceptions)
105 ReportException(&try_catch);
106 return false;
107 } else {
108 Handle<Value> result = script->Run();
109 if (result.IsEmpty()) {
110 // Print errors that happened during execution.
111 if (report_exceptions)
112 ReportException(&try_catch);
113 return false;
114 } else {
115 if (print_result && !result->IsUndefined()) {
116 // If all went well and the result wasn't undefined then print
117 // the returned value.
118 String::Utf8Value str(result);
119 printf("%s\n", *str);
120 }
121 return true;
122 }
123 }
124}
125
126
127Handle<Value> Shell::Print(const Arguments& args) {
128 bool first = true;
129 for (int i = 0; i < args.Length(); i++) {
130 HandleScope handle_scope;
131 if (first) {
132 first = false;
133 } else {
134 printf(" ");
135 }
136 String::Utf8Value str(args[i]);
137 printf("%s", *str);
138 }
139 printf("\n");
140 return Undefined();
141}
142
143
144Handle<Value> Shell::Load(const Arguments& args) {
145 for (int i = 0; i < args.Length(); i++) {
146 HandleScope handle_scope;
147 String::Utf8Value file(args[i]);
148 Handle<String> source = ReadFile(*file);
149 if (source.IsEmpty()) {
150 return ThrowException(String::New("Error loading file"));
151 }
152 if (!ExecuteString(source, String::New(*file), false, false)) {
153 return ThrowException(String::New("Error executing file"));
154 }
155 }
156 return Undefined();
157}
158
159
160Handle<Value> Shell::Quit(const Arguments& args) {
161 int exit_code = args[0]->Int32Value();
162 OnExit();
163 exit(exit_code);
164 return Undefined();
165}
166
167
168Handle<Value> Shell::Version(const Arguments& args) {
169 return String::New(V8::GetVersion());
170}
171
172
173void Shell::ReportException(v8::TryCatch* try_catch) {
174 HandleScope handle_scope;
175 String::Utf8Value exception(try_catch->Exception());
176 Handle<Message> message = try_catch->Message();
177 if (message.IsEmpty()) {
178 // V8 didn't provide any extra information about this error; just
179 // print the exception.
180 printf("%s\n", *exception);
181 } else {
182 // Print (filename):(line number): (message).
183 String::Utf8Value filename(message->GetScriptResourceName());
184 int linenum = message->GetLineNumber();
185 printf("%s:%i: %s\n", *filename, linenum, *exception);
186 // Print line of source code.
187 String::Utf8Value sourceline(message->GetSourceLine());
188 printf("%s\n", *sourceline);
189 // Print wavy underline (GetUnderline is deprecated).
190 int start = message->GetStartColumn();
191 for (int i = 0; i < start; i++) {
192 printf(" ");
193 }
194 int end = message->GetEndColumn();
195 for (int i = start; i < end; i++) {
196 printf("^");
197 }
198 printf("\n");
199 }
200}
201
202
203Handle<Array> Shell::GetCompletions(Handle<String> text, Handle<String> full) {
204 HandleScope handle_scope;
205 Context::Scope context_scope(utility_context_);
206 Handle<Object> global = utility_context_->Global();
207 Handle<Value> fun = global->Get(String::New("GetCompletions"));
208 static const int kArgc = 3;
209 Handle<Value> argv[kArgc] = { evaluation_context_->Global(), text, full };
210 Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
211 return handle_scope.Close(Handle<Array>::Cast(val));
212}
213
214
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000215int32_t* Counter::Bind(const char* name) {
216 int i;
217 for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
218 name_[i] = static_cast<char>(name[i]);
219 name_[i] = '\0';
220 return &counter_;
221}
222
223
224CounterCollection::CounterCollection() {
225 magic_number_ = 0xDEADFACE;
226 max_counters_ = kMaxCounters;
227 max_name_size_ = Counter::kMaxNameSize;
228 counters_in_use_ = 0;
229}
230
231
232Counter* CounterCollection::GetNextCounter() {
233 if (counters_in_use_ == kMaxCounters) return NULL;
234 return &counters_[counters_in_use_++];
235}
236
237
238void Shell::MapCounters(const char* name) {
239 counters_file_ = i::OS::MemoryMappedFile::create(name,
240 sizeof(CounterCollection), &local_counters_);
241 void* memory = (counters_file_ == NULL) ?
242 NULL : counters_file_->memory();
243 if (memory == NULL) {
244 printf("Could not map counters file %s\n", name);
245 exit(1);
246 }
247 counters_ = static_cast<CounterCollection*>(memory);
248 V8::SetCounterFunction(LookupCounter);
249}
250
251
252int* Shell::LookupCounter(const char* name) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000253 CounterMap::iterator item = counter_map_.find(name);
254 if (item != counter_map_.end()) {
255 Counter* result = (*item).second;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000256 return result->ptr();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000257 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000258 Counter* result = counters_->GetNextCounter();
259 if (result == NULL) return NULL;
260 return result->Bind(name);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000261}
262
263
264void Shell::Initialize() {
265 // Set up counters
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000266 if (i::FLAG_map_counters != NULL)
267 MapCounters(i::FLAG_map_counters);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000268 if (i::FLAG_dump_counters)
269 V8::SetCounterFunction(LookupCounter);
270 // Initialize the global objects
271 HandleScope scope;
272 Handle<ObjectTemplate> global_template = ObjectTemplate::New();
273 global_template->Set(String::New("print"), FunctionTemplate::New(Print));
274 global_template->Set(String::New("load"), FunctionTemplate::New(Load));
275 global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
276 global_template->Set(String::New("version"), FunctionTemplate::New(Version));
277
278 utility_context_ = Context::New(NULL, global_template);
279 utility_context_->SetSecurityToken(Undefined());
280 Context::Scope utility_scope(utility_context_);
281
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000282 i::JSArguments js_args = i::FLAG_js_arguments;
283 i::Handle<i::FixedArray> arguments_array =
284 i::Factory::NewFixedArray(js_args.argc());
285 for (int j = 0; j < js_args.argc(); j++) {
286 i::Handle<i::String> arg =
287 i::Factory::NewStringFromUtf8(i::CStrVector(js_args[j]));
288 arguments_array->set(j, *arg);
289 }
290 i::Handle<i::JSArray> arguments_jsarray =
291 i::Factory::NewJSArrayWithElements(arguments_array);
292 global_template->Set(String::New("arguments"),
293 Utils::ToLocal(arguments_jsarray));
294
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000295 // Install the debugger object in the utility scope
296 i::Debug::Load();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000297 i::JSObject* debug = i::Debug::debug_context()->global();
298 utility_context_->Global()->Set(String::New("$debug"),
299 Utils::ToLocal(&debug));
300
301 // Run the d8 shell utility script in the utility context
302 int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
303 i::Vector<const char> shell_source
304 = i::NativesCollection<i::D8>::GetScriptSource(source_index);
305 i::Vector<const char> shell_source_name
306 = i::NativesCollection<i::D8>::GetScriptName(source_index);
307 Handle<String> source = String::New(shell_source.start(),
308 shell_source.length());
309 Handle<String> name = String::New(shell_source_name.start(),
310 shell_source_name.length());
311 Script::Compile(source, name)->Run();
312
313 // Create the evaluation context
314 evaluation_context_ = Context::New(NULL, global_template);
315 evaluation_context_->SetSecurityToken(Undefined());
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000316
317 // Set the security token of the debug context to allow access.
318 i::Debug::debug_context()->set_security_token(i::Heap::undefined_value());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000319}
320
321
322void Shell::OnExit() {
323 if (i::FLAG_dump_counters) {
324 ::printf("+----------------------------------------+----------+\n");
325 ::printf("| Name | Value |\n");
326 ::printf("+----------------------------------------+----------+\n");
327 for (CounterMap::iterator i = counter_map_.begin();
328 i != counter_map_.end();
329 i++) {
330 Counter* counter = (*i).second;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000331 ::printf("| %-38s | %8i |\n", (*i).first, counter->value());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000332 }
333 ::printf("+----------------------------------------+----------+\n");
334 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000335 if (counters_file_ != NULL)
336 delete counters_file_;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000337}
338
339
340// Reads a file into a v8 string.
341Handle<String> Shell::ReadFile(const char* name) {
342 FILE* file = i::OS::FOpen(name, "rb");
343 if (file == NULL) return Handle<String>();
344
345 fseek(file, 0, SEEK_END);
346 int size = ftell(file);
347 rewind(file);
348
349 char* chars = new char[size + 1];
350 chars[size] = '\0';
351 for (int i = 0; i < size;) {
352 int read = fread(&chars[i], 1, size - i, file);
353 i += read;
354 }
355 fclose(file);
356 Handle<String> result = String::New(chars, size);
357 delete[] chars;
358 return result;
359}
360
361
362void Shell::RunShell() {
363 LineEditor* editor = LineEditor::Get();
364 printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
365 editor->Open();
366 while (true) {
367 HandleScope handle_scope;
368 i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt);
369 if (input.is_empty())
370 break;
371 editor->AddHistory(*input);
372 Handle<String> name = String::New("(d8)");
373 ExecuteString(String::New(*input), name, true, true);
374 }
375 editor->Close();
376 printf("\n");
377}
378
379
380int Shell::Main(int argc, char* argv[]) {
381 i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000382 if (i::FLAG_help) {
383 return 1;
384 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000385 Initialize();
386 bool run_shell = (argc == 1);
387 Context::Scope context_scope(evaluation_context_);
388 for (int i = 1; i < argc; i++) {
389 char* str = argv[i];
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000390 if (strcmp(str, "-f") == 0) {
391 // Ignore any -f flags for compatibility with other stand-alone
392 // JavaScript engines.
393 continue;
394 } else if (strncmp(str, "--", 2) == 0) {
395 printf("Warning: unknown flag %s.\nTry --help for options\n", str);
396 } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
397 // Execute argument given to -e option directly.
398 v8::HandleScope handle_scope;
399 v8::Handle<v8::String> file_name = v8::String::New("unnamed");
400 v8::Handle<v8::String> source = v8::String::New(argv[i + 1]);
401 if (!ExecuteString(source, file_name, false, true))
402 return 1;
403 i++;
404 } else {
405 // Use all other arguments as names of files to load and run.
406 HandleScope handle_scope;
407 Handle<String> file_name = v8::String::New(str);
408 Handle<String> source = ReadFile(str);
409 if (source.IsEmpty()) {
410 printf("Error reading '%s'\n", str);
411 return 1;
412 }
413 if (!ExecuteString(source, file_name, false, true))
414 return 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000415 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000416 }
417 if (run_shell)
418 RunShell();
419 OnExit();
420 return 0;
421}
422
423
424} // namespace v8
425
426
427int main(int argc, char* argv[]) {
428 return v8::Shell::Main(argc, argv);
429}