blob: 7482ae1a618025408c62bd4bde07b1889f98e04f [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 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#include "v8.h"
29
30#include "bootstrapper.h"
31#include "codegen-inl.h"
32#include "compilation-cache.h"
33#include "compiler.h"
34#include "debug.h"
Leon Clarkeeab96aa2010-01-27 16:31:12 +000035#include "fast-codegen.h"
Leon Clarked91b9f72010-01-27 17:25:45 +000036#include "full-codegen.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000037#include "oprofile-agent.h"
38#include "rewriter.h"
39#include "scopes.h"
40#include "usage-analyzer.h"
41
42namespace v8 {
43namespace internal {
44
Steve Block3ce2e202009-11-05 08:53:23 +000045
Steve Blocka7e24c12009-10-30 11:49:00 +000046static Handle<Code> MakeCode(FunctionLiteral* literal,
47 Handle<Script> script,
48 Handle<Context> context,
Steve Blockd0582a62009-12-15 09:54:21 +000049 bool is_eval,
50 Handle<SharedFunctionInfo> shared) {
Steve Blocka7e24c12009-10-30 11:49:00 +000051 ASSERT(literal != NULL);
52
53 // Rewrite the AST by introducing .result assignments where needed.
54 if (!Rewriter::Process(literal) || !AnalyzeVariableUsage(literal)) {
55 // Signal a stack overflow by returning a null handle. The stack
56 // overflow exception will be thrown by the caller.
57 return Handle<Code>::null();
58 }
59
60 {
61 // Compute top scope and allocate variables. For lazy compilation
62 // the top scope only contains the single lazily compiled function,
63 // so this doesn't re-allocate variables repeatedly.
64 HistogramTimerScope timer(&Counters::variable_allocation);
65 Scope* top = literal->scope();
66 while (top->outer_scope() != NULL) top = top->outer_scope();
67 top->AllocateVariables(context);
68 }
69
70#ifdef DEBUG
71 if (Bootstrapper::IsActive() ?
72 FLAG_print_builtin_scopes :
73 FLAG_print_scopes) {
74 literal->scope()->Print();
75 }
76#endif
77
78 // Optimize the AST.
79 if (!Rewriter::Optimize(literal)) {
80 // Signal a stack overflow by returning a null handle. The stack
81 // overflow exception will be thrown by the caller.
82 return Handle<Code>::null();
83 }
84
Leon Clarked91b9f72010-01-27 17:25:45 +000085 // Generate code and return it. Code generator selection is governed by
86 // which backends are enabled and whether the function is considered
87 // run-once code or not:
88 //
89 // --full-compiler enables the dedicated backend for code we expect to be
90 // run once
91 // --fast-compiler enables a speculative optimizing backend (for
92 // non-run-once code)
93 //
94 // The normal choice of backend can be overridden with the flags
95 // --always-full-compiler and --always-fast-compiler, which are mutually
96 // incompatible.
97 CHECK(!FLAG_always_full_compiler || !FLAG_always_fast_compiler);
98
99 bool is_run_once = (shared.is_null())
100 ? literal->scope()->is_global_scope()
101 : (shared->is_toplevel() || shared->try_full_codegen());
102
103 if (FLAG_always_full_compiler || (FLAG_full_compiler && is_run_once)) {
104 FullCodeGenSyntaxChecker checker;
105 checker.Check(literal);
106 if (checker.has_supported_syntax()) {
107 return FullCodeGenerator::MakeCode(literal, script, is_eval);
Steve Block3ce2e202009-11-05 08:53:23 +0000108 }
Leon Clarked91b9f72010-01-27 17:25:45 +0000109 } else if (FLAG_always_fast_compiler ||
110 (FLAG_fast_compiler && !is_run_once)) {
111 FastCodeGenSyntaxChecker checker;
112 checker.Check(literal);
113 // Does not yet generate code.
Steve Block3ce2e202009-11-05 08:53:23 +0000114 }
Leon Clarked91b9f72010-01-27 17:25:45 +0000115
Steve Block3ce2e202009-11-05 08:53:23 +0000116 return CodeGenerator::MakeCode(literal, script, is_eval);
Steve Blocka7e24c12009-10-30 11:49:00 +0000117}
118
119
120static bool IsValidJSON(FunctionLiteral* lit) {
121 if (lit->body()->length() != 1)
122 return false;
123 Statement* stmt = lit->body()->at(0);
124 if (stmt->AsExpressionStatement() == NULL)
125 return false;
126 Expression* expr = stmt->AsExpressionStatement()->expression();
127 return expr->IsValidJSON();
128}
129
130
131static Handle<JSFunction> MakeFunction(bool is_global,
132 bool is_eval,
133 Compiler::ValidationState validate,
134 Handle<Script> script,
135 Handle<Context> context,
136 v8::Extension* extension,
137 ScriptDataImpl* pre_data) {
138 CompilationZoneScope zone_scope(DELETE_ON_EXIT);
139
140 PostponeInterruptsScope postpone;
141
142 ASSERT(!i::Top::global_context().is_null());
143 script->set_context_data((*i::Top::global_context())->data());
144
145#ifdef ENABLE_DEBUGGER_SUPPORT
146 bool is_json = (validate == Compiler::VALIDATE_JSON);
147 if (is_eval || is_json) {
148 script->set_compilation_type(
149 is_json ? Smi::FromInt(Script::COMPILATION_TYPE_JSON) :
150 Smi::FromInt(Script::COMPILATION_TYPE_EVAL));
151 // For eval scripts add information on the function from which eval was
152 // called.
153 if (is_eval) {
154 JavaScriptFrameIterator it;
Steve Blockd0582a62009-12-15 09:54:21 +0000155 script->set_eval_from_shared(
156 JSFunction::cast(it.frame()->function())->shared());
157 int offset = static_cast<int>(
158 it.frame()->pc() - it.frame()->code()->instruction_start());
Steve Blocka7e24c12009-10-30 11:49:00 +0000159 script->set_eval_from_instructions_offset(Smi::FromInt(offset));
160 }
161 }
162
163 // Notify debugger
164 Debugger::OnBeforeCompile(script);
165#endif
166
167 // Only allow non-global compiles for eval.
168 ASSERT(is_eval || is_global);
169
170 // Build AST.
171 FunctionLiteral* lit = MakeAST(is_global, script, extension, pre_data);
172
173 // Check for parse errors.
174 if (lit == NULL) {
175 ASSERT(Top::has_pending_exception());
176 return Handle<JSFunction>::null();
177 }
178
179 // When parsing JSON we do an ordinary parse and then afterwards
180 // check the AST to ensure it was well-formed. If not we give a
181 // syntax error.
182 if (validate == Compiler::VALIDATE_JSON && !IsValidJSON(lit)) {
183 HandleScope scope;
184 Handle<JSArray> args = Factory::NewJSArray(1);
185 Handle<Object> source(script->source());
186 SetElement(args, 0, source);
187 Handle<Object> result = Factory::NewSyntaxError("invalid_json", args);
188 Top::Throw(*result, NULL);
189 return Handle<JSFunction>::null();
190 }
191
192 // Measure how long it takes to do the compilation; only take the
193 // rest of the function into account to avoid overlap with the
194 // parsing statistics.
195 HistogramTimer* rate = is_eval
196 ? &Counters::compile_eval
197 : &Counters::compile;
198 HistogramTimerScope timer(rate);
199
200 // Compile the code.
Steve Blockd0582a62009-12-15 09:54:21 +0000201 Handle<Code> code = MakeCode(lit, script, context, is_eval,
202 Handle<SharedFunctionInfo>::null());
Steve Blocka7e24c12009-10-30 11:49:00 +0000203
204 // Check for stack-overflow exceptions.
205 if (code.is_null()) {
206 Top::StackOverflow();
207 return Handle<JSFunction>::null();
208 }
209
210#if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT
211 // Log the code generation for the script. Check explicit whether logging is
212 // to avoid allocating when not required.
213 if (Logger::is_logging() || OProfileAgent::is_enabled()) {
214 if (script->name()->IsString()) {
215 SmartPointer<char> data =
216 String::cast(script->name())->ToCString(DISALLOW_NULLS);
217 LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG,
218 *code, *data));
219 OProfileAgent::CreateNativeCodeRegion(*data,
220 code->instruction_start(),
221 code->instruction_size());
222 } else {
223 LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG,
224 *code, ""));
225 OProfileAgent::CreateNativeCodeRegion(is_eval ? "Eval" : "Script",
226 code->instruction_start(),
227 code->instruction_size());
228 }
229 }
230#endif
231
232 // Allocate function.
233 Handle<JSFunction> fun =
234 Factory::NewFunctionBoilerplate(lit->name(),
235 lit->materialized_literal_count(),
Steve Blocka7e24c12009-10-30 11:49:00 +0000236 code);
237
238 ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
Steve Blockd0582a62009-12-15 09:54:21 +0000239 Compiler::SetFunctionInfo(fun, lit, true, script);
Steve Blocka7e24c12009-10-30 11:49:00 +0000240
241 // Hint to the runtime system used when allocating space for initial
242 // property space by setting the expected number of properties for
243 // the instances of the function.
244 SetExpectedNofPropertiesFromEstimate(fun, lit->expected_property_count());
245
246#ifdef ENABLE_DEBUGGER_SUPPORT
247 // Notify debugger
248 Debugger::OnAfterCompile(script, fun);
249#endif
250
251 return fun;
252}
253
254
255static StaticResource<SafeStringInputBuffer> safe_string_input_buffer;
256
257
258Handle<JSFunction> Compiler::Compile(Handle<String> source,
259 Handle<Object> script_name,
260 int line_offset, int column_offset,
261 v8::Extension* extension,
262 ScriptDataImpl* input_pre_data) {
263 int source_length = source->length();
264 Counters::total_load_size.Increment(source_length);
265 Counters::total_compile_size.Increment(source_length);
266
267 // The VM is in the COMPILER state until exiting this function.
268 VMState state(COMPILER);
269
270 // Do a lookup in the compilation cache but not for extensions.
271 Handle<JSFunction> result;
272 if (extension == NULL) {
273 result = CompilationCache::LookupScript(source,
274 script_name,
275 line_offset,
276 column_offset);
277 }
278
279 if (result.is_null()) {
280 // No cache entry found. Do pre-parsing and compile the script.
281 ScriptDataImpl* pre_data = input_pre_data;
282 if (pre_data == NULL && source_length >= FLAG_min_preparse_length) {
283 Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
284 buf->Reset(source.location());
285 pre_data = PreParse(source, buf.value(), extension);
286 }
287
288 // Create a script object describing the script to be compiled.
289 Handle<Script> script = Factory::NewScript(source);
290 if (!script_name.is_null()) {
291 script->set_name(*script_name);
292 script->set_line_offset(Smi::FromInt(line_offset));
293 script->set_column_offset(Smi::FromInt(column_offset));
294 }
295
296 // Compile the function and add it to the cache.
297 result = MakeFunction(true,
298 false,
299 DONT_VALIDATE_JSON,
300 script,
301 Handle<Context>::null(),
302 extension,
303 pre_data);
304 if (extension == NULL && !result.is_null()) {
305 CompilationCache::PutScript(source, result);
306 }
307
308 // Get rid of the pre-parsing data (if necessary).
309 if (input_pre_data == NULL && pre_data != NULL) {
310 delete pre_data;
311 }
312 }
313
314 if (result.is_null()) Top::ReportPendingMessages();
315 return result;
316}
317
318
319Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
320 Handle<Context> context,
321 bool is_global,
322 ValidationState validate) {
323 // Note that if validation is required then no path through this
324 // function is allowed to return a value without validating that
325 // the input is legal json.
326
327 int source_length = source->length();
328 Counters::total_eval_size.Increment(source_length);
329 Counters::total_compile_size.Increment(source_length);
330
331 // The VM is in the COMPILER state until exiting this function.
332 VMState state(COMPILER);
333
334 // Do a lookup in the compilation cache; if the entry is not there,
335 // invoke the compiler and add the result to the cache. If we're
336 // evaluating json we bypass the cache since we can't be sure a
337 // potential value in the cache has been validated.
338 Handle<JSFunction> result;
339 if (validate == DONT_VALIDATE_JSON)
340 result = CompilationCache::LookupEval(source, context, is_global);
341
342 if (result.is_null()) {
343 // Create a script object describing the script to be compiled.
344 Handle<Script> script = Factory::NewScript(source);
345 result = MakeFunction(is_global,
346 true,
347 validate,
348 script,
349 context,
350 NULL,
351 NULL);
352 if (!result.is_null() && validate != VALIDATE_JSON) {
353 // For json it's unlikely that we'll ever see exactly the same
354 // string again so we don't use the compilation cache.
355 CompilationCache::PutEval(source, context, is_global, result);
356 }
357 }
358
359 return result;
360}
361
362
363bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared,
364 int loop_nesting) {
365 CompilationZoneScope zone_scope(DELETE_ON_EXIT);
366
367 // The VM is in the COMPILER state until exiting this function.
368 VMState state(COMPILER);
369
370 PostponeInterruptsScope postpone;
371
372 // Compute name, source code and script data.
373 Handle<String> name(String::cast(shared->name()));
374 Handle<Script> script(Script::cast(shared->script()));
375
376 int start_position = shared->start_position();
377 int end_position = shared->end_position();
378 bool is_expression = shared->is_expression();
379 Counters::total_compile_size.Increment(end_position - start_position);
380
381 // Generate the AST for the lazily compiled function. The AST may be
382 // NULL in case of parser stack overflow.
383 FunctionLiteral* lit = MakeLazyAST(script, name,
384 start_position,
385 end_position,
386 is_expression);
387
388 // Check for parse errors.
389 if (lit == NULL) {
390 ASSERT(Top::has_pending_exception());
391 return false;
392 }
393
394 // Update the loop nesting in the function literal.
395 lit->set_loop_nesting(loop_nesting);
396
397 // Measure how long it takes to do the lazy compilation; only take
398 // the rest of the function into account to avoid overlap with the
399 // lazy parsing statistics.
400 HistogramTimerScope timer(&Counters::compile_lazy);
401
402 // Compile the code.
Steve Blockd0582a62009-12-15 09:54:21 +0000403 Handle<Code> code = MakeCode(lit, script, Handle<Context>::null(), false,
404 shared);
Steve Blocka7e24c12009-10-30 11:49:00 +0000405
406 // Check for stack-overflow exception.
407 if (code.is_null()) {
408 Top::StackOverflow();
409 return false;
410 }
411
412#if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT
413 // Log the code generation. If source information is available include script
414 // name and line number. Check explicit whether logging is enabled as finding
415 // the line number is not for free.
416 if (Logger::is_logging() || OProfileAgent::is_enabled()) {
417 Handle<String> func_name(name->length() > 0 ?
418 *name : shared->inferred_name());
419 if (script->name()->IsString()) {
420 int line_num = GetScriptLineNumber(script, start_position) + 1;
421 LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, *code, *func_name,
422 String::cast(script->name()), line_num));
423 OProfileAgent::CreateNativeCodeRegion(*func_name,
424 String::cast(script->name()),
425 line_num,
426 code->instruction_start(),
427 code->instruction_size());
428 } else {
429 LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, *code, *func_name));
430 OProfileAgent::CreateNativeCodeRegion(*func_name,
431 code->instruction_start(),
432 code->instruction_size());
433 }
434 }
435#endif
436
437 // Update the shared function info with the compiled code.
438 shared->set_code(*code);
439
440 // Set the expected number of properties for instances.
441 SetExpectedNofPropertiesFromEstimate(shared, lit->expected_property_count());
442
443 // Set the optimication hints after performing lazy compilation, as these are
444 // not set when the function is set up as a lazily compiled function.
445 shared->SetThisPropertyAssignmentsInfo(
Steve Blocka7e24c12009-10-30 11:49:00 +0000446 lit->has_only_simple_this_property_assignments(),
447 *lit->this_property_assignments());
448
449 // Check the function has compiled code.
450 ASSERT(shared->is_compiled());
451 return true;
452}
453
454
Steve Blockd0582a62009-12-15 09:54:21 +0000455Handle<JSFunction> Compiler::BuildBoilerplate(FunctionLiteral* literal,
456 Handle<Script> script,
457 AstVisitor* caller) {
458#ifdef DEBUG
459 // We should not try to compile the same function literal more than
460 // once.
461 literal->mark_as_compiled();
462#endif
463
464 // Determine if the function can be lazily compiled. This is
465 // necessary to allow some of our builtin JS files to be lazily
466 // compiled. These builtins cannot be handled lazily by the parser,
467 // since we have to know if a function uses the special natives
468 // syntax, which is something the parser records.
469 bool allow_lazy = literal->AllowsLazyCompilation();
470
471 // Generate code
472 Handle<Code> code;
473 if (FLAG_lazy && allow_lazy) {
474 code = ComputeLazyCompile(literal->num_parameters());
475 } else {
476 // The bodies of function literals have not yet been visited by
477 // the AST optimizer/analyzer.
478 if (!Rewriter::Optimize(literal)) {
479 return Handle<JSFunction>::null();
480 }
481
Leon Clarked91b9f72010-01-27 17:25:45 +0000482 // Generate code and return it. The way that the compilation mode
483 // is controlled by the command-line flags is described in
484 // the static helper function MakeCode.
485 CHECK(!FLAG_always_full_compiler || !FLAG_always_fast_compiler);
486 bool is_run_once = literal->try_full_codegen();
Steve Blockd0582a62009-12-15 09:54:21 +0000487 bool is_compiled = false;
Leon Clarked91b9f72010-01-27 17:25:45 +0000488 if (FLAG_always_full_compiler || (FLAG_full_compiler && is_run_once)) {
489 FullCodeGenSyntaxChecker checker;
490 checker.Check(literal);
491 if (checker.has_supported_syntax()) {
492 code = FullCodeGenerator::MakeCode(literal,
Steve Blockd0582a62009-12-15 09:54:21 +0000493 script,
494 false); // Not eval.
495 is_compiled = true;
496 }
Leon Clarked91b9f72010-01-27 17:25:45 +0000497 } else if (FLAG_always_fast_compiler ||
498 (FLAG_fast_compiler && !is_run_once)) {
499 FastCodeGenSyntaxChecker checker;
500 checker.Check(literal);
501 // Generate no code.
Steve Blockd0582a62009-12-15 09:54:21 +0000502 }
503
504 if (!is_compiled) {
Leon Clarked91b9f72010-01-27 17:25:45 +0000505 // We fall back to the classic V8 code generator.
Steve Blockd0582a62009-12-15 09:54:21 +0000506 code = CodeGenerator::MakeCode(literal,
507 script,
508 false); // Not eval.
509 }
510
511 // Check for stack-overflow exception.
512 if (code.is_null()) {
513 caller->SetStackOverflow();
514 return Handle<JSFunction>::null();
515 }
516
517 // Function compilation complete.
518 LOG(CodeCreateEvent(Logger::FUNCTION_TAG, *code, *literal->name()));
519
520#ifdef ENABLE_OPROFILE_AGENT
Leon Clarkee46be812010-01-19 14:06:41 +0000521 OProfileAgent::CreateNativeCodeRegion(*literal->name(),
Steve Blockd0582a62009-12-15 09:54:21 +0000522 code->instruction_start(),
523 code->instruction_size());
524#endif
525 }
526
527 // Create a boilerplate function.
528 Handle<JSFunction> function =
529 Factory::NewFunctionBoilerplate(literal->name(),
530 literal->materialized_literal_count(),
531 code);
532 SetFunctionInfo(function, literal, false, script);
533
534#ifdef ENABLE_DEBUGGER_SUPPORT
535 // Notify debugger that a new function has been added.
536 Debugger::OnNewFunction(function);
537#endif
538
539 // Set the expected number of properties for instances and return
540 // the resulting function.
541 SetExpectedNofPropertiesFromEstimate(function,
542 literal->expected_property_count());
543 return function;
544}
545
546
547// Sets the function info on a function.
548// The start_position points to the first '(' character after the function name
549// in the full script source. When counting characters in the script source the
550// the first character is number 0 (not 1).
551void Compiler::SetFunctionInfo(Handle<JSFunction> fun,
552 FunctionLiteral* lit,
553 bool is_toplevel,
554 Handle<Script> script) {
555 fun->shared()->set_length(lit->num_parameters());
556 fun->shared()->set_formal_parameter_count(lit->num_parameters());
557 fun->shared()->set_script(*script);
558 fun->shared()->set_function_token_position(lit->function_token_position());
559 fun->shared()->set_start_position(lit->start_position());
560 fun->shared()->set_end_position(lit->end_position());
561 fun->shared()->set_is_expression(lit->is_expression());
562 fun->shared()->set_is_toplevel(is_toplevel);
563 fun->shared()->set_inferred_name(*lit->inferred_name());
564 fun->shared()->SetThisPropertyAssignmentsInfo(
565 lit->has_only_simple_this_property_assignments(),
566 *lit->this_property_assignments());
Leon Clarked91b9f72010-01-27 17:25:45 +0000567 fun->shared()->set_try_full_codegen(lit->try_full_codegen());
Steve Blockd0582a62009-12-15 09:54:21 +0000568}
569
570
Steve Blocka7e24c12009-10-30 11:49:00 +0000571} } // namespace v8::internal