1 // 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
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.
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.
30 #include "bootstrapper.h"
31 #include "codegen-inl.h"
32 #include "compilation-cache.h"
35 #include "fast-codegen.h"
36 #include "full-codegen.h"
37 #include "oprofile-agent.h"
40 #include "usage-analyzer.h"
46 static Handle<Code> MakeCode(FunctionLiteral* literal,
47 Handle<Script> script,
48 Handle<Context> context,
50 CompilationInfo* info) {
51 ASSERT(literal != NULL);
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();
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);
71 if (Bootstrapper::IsActive() ?
72 FLAG_print_builtin_scopes :
74 literal->scope()->Print();
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();
85 // 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:
89 // --full-compiler enables the dedicated backend for code we expect to be
91 // --fast-compiler enables a speculative optimizing backend (for
94 // The normal choice of backend can be overridden with the flags
95 // --always-full-compiler and --always-fast-compiler, which are mutually
97 CHECK(!FLAG_always_full_compiler || !FLAG_always_fast_compiler);
99 Handle<SharedFunctionInfo> shared = info->shared_info();
100 bool is_run_once = (shared.is_null())
101 ? literal->scope()->is_global_scope()
102 : (shared->is_toplevel() || shared->try_full_codegen());
104 if (FLAG_always_full_compiler || (FLAG_full_compiler && is_run_once)) {
105 FullCodeGenSyntaxChecker checker;
106 checker.Check(literal);
107 if (checker.has_supported_syntax()) {
108 return FullCodeGenerator::MakeCode(literal, script, is_eval);
110 } else if (FLAG_always_fast_compiler ||
111 (FLAG_fast_compiler && !is_run_once)) {
112 FastCodeGenSyntaxChecker checker;
113 checker.Check(literal, info);
114 if (checker.has_supported_syntax()) {
115 return FastCodeGenerator::MakeCode(literal, script, is_eval, info);
119 return CodeGenerator::MakeCode(literal, script, is_eval, info);
123 static Handle<JSFunction> MakeFunction(bool is_global,
125 Compiler::ValidationState validate,
126 Handle<Script> script,
127 Handle<Context> context,
128 v8::Extension* extension,
129 ScriptDataImpl* pre_data) {
130 CompilationZoneScope zone_scope(DELETE_ON_EXIT);
132 PostponeInterruptsScope postpone;
134 ASSERT(!i::Top::global_context().is_null());
135 script->set_context_data((*i::Top::global_context())->data());
137 bool is_json = (validate == Compiler::VALIDATE_JSON);
138 #ifdef ENABLE_DEBUGGER_SUPPORT
139 if (is_eval || is_json) {
140 script->set_compilation_type(
141 is_json ? Smi::FromInt(Script::COMPILATION_TYPE_JSON) :
142 Smi::FromInt(Script::COMPILATION_TYPE_EVAL));
143 // For eval scripts add information on the function from which eval was
146 StackTraceFrameIterator it;
148 script->set_eval_from_shared(
149 JSFunction::cast(it.frame()->function())->shared());
150 int offset = static_cast<int>(
151 it.frame()->pc() - it.frame()->code()->instruction_start());
152 script->set_eval_from_instructions_offset(Smi::FromInt(offset));
158 Debugger::OnBeforeCompile(script);
161 // Only allow non-global compiles for eval.
162 ASSERT(is_eval || is_global);
165 FunctionLiteral* lit =
166 MakeAST(is_global, script, extension, pre_data, is_json);
168 // Check for parse errors.
170 ASSERT(Top::has_pending_exception());
171 return Handle<JSFunction>::null();
174 // Measure how long it takes to do the compilation; only take the
175 // rest of the function into account to avoid overlap with the
176 // parsing statistics.
177 HistogramTimer* rate = is_eval
178 ? &Counters::compile_eval
179 : &Counters::compile;
180 HistogramTimerScope timer(rate);
183 CompilationInfo info(Handle<SharedFunctionInfo>::null(),
184 Handle<Object>::null(), // No receiver.
185 0); // Not nested in a loop.
186 Handle<Code> code = MakeCode(lit, script, context, is_eval, &info);
188 // Check for stack-overflow exceptions.
189 if (code.is_null()) {
190 Top::StackOverflow();
191 return Handle<JSFunction>::null();
194 #if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT
195 // Log the code generation for the script. Check explicit whether logging is
196 // to avoid allocating when not required.
197 if (Logger::is_logging() || OProfileAgent::is_enabled()) {
198 if (script->name()->IsString()) {
199 SmartPointer<char> data =
200 String::cast(script->name())->ToCString(DISALLOW_NULLS);
201 LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG,
203 OProfileAgent::CreateNativeCodeRegion(*data,
204 code->instruction_start(),
205 code->instruction_size());
207 LOG(CodeCreateEvent(is_eval ? Logger::EVAL_TAG : Logger::SCRIPT_TAG,
209 OProfileAgent::CreateNativeCodeRegion(is_eval ? "Eval" : "Script",
210 code->instruction_start(),
211 code->instruction_size());
216 // Allocate function.
217 Handle<JSFunction> fun =
218 Factory::NewFunctionBoilerplate(lit->name(),
219 lit->materialized_literal_count(),
222 ASSERT_EQ(RelocInfo::kNoPosition, lit->function_token_position());
223 Compiler::SetFunctionInfo(fun, lit, true, script);
225 // Hint to the runtime system used when allocating space for initial
226 // property space by setting the expected number of properties for
227 // the instances of the function.
228 SetExpectedNofPropertiesFromEstimate(fun, lit->expected_property_count());
230 #ifdef ENABLE_DEBUGGER_SUPPORT
232 Debugger::OnAfterCompile(script, fun);
239 static StaticResource<SafeStringInputBuffer> safe_string_input_buffer;
242 Handle<JSFunction> Compiler::Compile(Handle<String> source,
243 Handle<Object> script_name,
244 int line_offset, int column_offset,
245 v8::Extension* extension,
246 ScriptDataImpl* input_pre_data) {
247 int source_length = source->length();
248 Counters::total_load_size.Increment(source_length);
249 Counters::total_compile_size.Increment(source_length);
251 // The VM is in the COMPILER state until exiting this function.
252 VMState state(COMPILER);
254 // Do a lookup in the compilation cache but not for extensions.
255 Handle<JSFunction> result;
256 if (extension == NULL) {
257 result = CompilationCache::LookupScript(source,
263 if (result.is_null()) {
264 // No cache entry found. Do pre-parsing and compile the script.
265 ScriptDataImpl* pre_data = input_pre_data;
266 if (pre_data == NULL && source_length >= FLAG_min_preparse_length) {
267 Access<SafeStringInputBuffer> buf(&safe_string_input_buffer);
268 buf->Reset(source.location());
269 pre_data = PreParse(source, buf.value(), extension);
272 // Create a script object describing the script to be compiled.
273 Handle<Script> script = Factory::NewScript(source);
274 if (!script_name.is_null()) {
275 script->set_name(*script_name);
276 script->set_line_offset(Smi::FromInt(line_offset));
277 script->set_column_offset(Smi::FromInt(column_offset));
280 // Compile the function and add it to the cache.
281 result = MakeFunction(true,
285 Handle<Context>::null(),
288 if (extension == NULL && !result.is_null()) {
289 CompilationCache::PutScript(source, result);
292 // Get rid of the pre-parsing data (if necessary).
293 if (input_pre_data == NULL && pre_data != NULL) {
298 if (result.is_null()) Top::ReportPendingMessages();
303 Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
304 Handle<Context> context,
306 ValidationState validate) {
307 // Note that if validation is required then no path through this
308 // function is allowed to return a value without validating that
309 // the input is legal json.
311 int source_length = source->length();
312 Counters::total_eval_size.Increment(source_length);
313 Counters::total_compile_size.Increment(source_length);
315 // The VM is in the COMPILER state until exiting this function.
316 VMState state(COMPILER);
318 // Do a lookup in the compilation cache; if the entry is not there,
319 // invoke the compiler and add the result to the cache. If we're
320 // evaluating json we bypass the cache since we can't be sure a
321 // potential value in the cache has been validated.
322 Handle<JSFunction> result;
323 if (validate == DONT_VALIDATE_JSON)
324 result = CompilationCache::LookupEval(source, context, is_global);
326 if (result.is_null()) {
327 // Create a script object describing the script to be compiled.
328 Handle<Script> script = Factory::NewScript(source);
329 result = MakeFunction(is_global,
336 if (!result.is_null() && validate != VALIDATE_JSON) {
337 // For json it's unlikely that we'll ever see exactly the same
338 // string again so we don't use the compilation cache.
339 CompilationCache::PutEval(source, context, is_global, result);
347 bool Compiler::CompileLazy(CompilationInfo* info) {
348 CompilationZoneScope zone_scope(DELETE_ON_EXIT);
350 // The VM is in the COMPILER state until exiting this function.
351 VMState state(COMPILER);
353 PostponeInterruptsScope postpone;
355 // Compute name, source code and script data.
356 Handle<SharedFunctionInfo> shared = info->shared_info();
357 Handle<String> name(String::cast(shared->name()));
358 Handle<Script> script(Script::cast(shared->script()));
360 int start_position = shared->start_position();
361 int end_position = shared->end_position();
362 bool is_expression = shared->is_expression();
363 Counters::total_compile_size.Increment(end_position - start_position);
365 // Generate the AST for the lazily compiled function. The AST may be
366 // NULL in case of parser stack overflow.
367 FunctionLiteral* lit = MakeLazyAST(script, name,
372 // Check for parse errors.
374 ASSERT(Top::has_pending_exception());
378 // Measure how long it takes to do the lazy compilation; only take
379 // the rest of the function into account to avoid overlap with the
380 // lazy parsing statistics.
381 HistogramTimerScope timer(&Counters::compile_lazy);
384 Handle<Code> code = MakeCode(lit,
386 Handle<Context>::null(),
390 // Check for stack-overflow exception.
391 if (code.is_null()) {
392 Top::StackOverflow();
396 #if defined ENABLE_LOGGING_AND_PROFILING || defined ENABLE_OPROFILE_AGENT
397 // Log the code generation. If source information is available include script
398 // name and line number. Check explicit whether logging is enabled as finding
399 // the line number is not for free.
400 if (Logger::is_logging() || OProfileAgent::is_enabled()) {
401 Handle<String> func_name(name->length() > 0 ?
402 *name : shared->inferred_name());
403 if (script->name()->IsString()) {
404 int line_num = GetScriptLineNumber(script, start_position) + 1;
405 LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, *code, *func_name,
406 String::cast(script->name()), line_num));
407 OProfileAgent::CreateNativeCodeRegion(*func_name,
408 String::cast(script->name()),
410 code->instruction_start(),
411 code->instruction_size());
413 LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, *code, *func_name));
414 OProfileAgent::CreateNativeCodeRegion(*func_name,
415 code->instruction_start(),
416 code->instruction_size());
421 // Update the shared function info with the compiled code.
422 shared->set_code(*code);
424 // Set the expected number of properties for instances.
425 SetExpectedNofPropertiesFromEstimate(shared, lit->expected_property_count());
427 // Set the optimication hints after performing lazy compilation, as these are
428 // not set when the function is set up as a lazily compiled function.
429 shared->SetThisPropertyAssignmentsInfo(
430 lit->has_only_simple_this_property_assignments(),
431 *lit->this_property_assignments());
433 // Check the function has compiled code.
434 ASSERT(shared->is_compiled());
439 Handle<JSFunction> Compiler::BuildBoilerplate(FunctionLiteral* literal,
440 Handle<Script> script,
441 AstVisitor* caller) {
443 // We should not try to compile the same function literal more than
445 literal->mark_as_compiled();
448 // Determine if the function can be lazily compiled. This is
449 // necessary to allow some of our builtin JS files to be lazily
450 // compiled. These builtins cannot be handled lazily by the parser,
451 // since we have to know if a function uses the special natives
452 // syntax, which is something the parser records.
453 bool allow_lazy = literal->AllowsLazyCompilation();
457 if (FLAG_lazy && allow_lazy) {
458 code = ComputeLazyCompile(literal->num_parameters());
460 // The bodies of function literals have not yet been visited by
461 // the AST optimizer/analyzer.
462 if (!Rewriter::Optimize(literal)) {
463 return Handle<JSFunction>::null();
466 // Generate code and return it. The way that the compilation mode
467 // is controlled by the command-line flags is described in
468 // the static helper function MakeCode.
469 CompilationInfo info(Handle<SharedFunctionInfo>::null(),
470 Handle<Object>::null(), // No receiver.
471 0); // Not nested in a loop.
473 CHECK(!FLAG_always_full_compiler || !FLAG_always_fast_compiler);
474 bool is_run_once = literal->try_full_codegen();
475 bool is_compiled = false;
476 if (FLAG_always_full_compiler || (FLAG_full_compiler && is_run_once)) {
477 FullCodeGenSyntaxChecker checker;
478 checker.Check(literal);
479 if (checker.has_supported_syntax()) {
480 code = FullCodeGenerator::MakeCode(literal,
485 } else if (FLAG_always_fast_compiler ||
486 (FLAG_fast_compiler && !is_run_once)) {
487 // Since we are not lazily compiling we do not have a receiver to
489 FastCodeGenSyntaxChecker checker;
490 checker.Check(literal, &info);
491 if (checker.has_supported_syntax()) {
492 code = FastCodeGenerator::MakeCode(literal, script, false, &info);
498 // We fall back to the classic V8 code generator.
499 code = CodeGenerator::MakeCode(literal,
505 // Check for stack-overflow exception.
506 if (code.is_null()) {
507 caller->SetStackOverflow();
508 return Handle<JSFunction>::null();
511 // Function compilation complete.
512 LOG(CodeCreateEvent(Logger::FUNCTION_TAG, *code, *literal->name()));
514 #ifdef ENABLE_OPROFILE_AGENT
515 OProfileAgent::CreateNativeCodeRegion(*literal->name(),
516 code->instruction_start(),
517 code->instruction_size());
521 // Create a boilerplate function.
522 Handle<JSFunction> function =
523 Factory::NewFunctionBoilerplate(literal->name(),
524 literal->materialized_literal_count(),
526 SetFunctionInfo(function, literal, false, script);
528 #ifdef ENABLE_DEBUGGER_SUPPORT
529 // Notify debugger that a new function has been added.
530 Debugger::OnNewFunction(function);
533 // Set the expected number of properties for instances and return
534 // the resulting function.
535 SetExpectedNofPropertiesFromEstimate(function,
536 literal->expected_property_count());
541 // Sets the function info on a function.
542 // The start_position points to the first '(' character after the function name
543 // in the full script source. When counting characters in the script source the
544 // the first character is number 0 (not 1).
545 void Compiler::SetFunctionInfo(Handle<JSFunction> fun,
546 FunctionLiteral* lit,
548 Handle<Script> script) {
549 fun->shared()->set_length(lit->num_parameters());
550 fun->shared()->set_formal_parameter_count(lit->num_parameters());
551 fun->shared()->set_script(*script);
552 fun->shared()->set_function_token_position(lit->function_token_position());
553 fun->shared()->set_start_position(lit->start_position());
554 fun->shared()->set_end_position(lit->end_position());
555 fun->shared()->set_is_expression(lit->is_expression());
556 fun->shared()->set_is_toplevel(is_toplevel);
557 fun->shared()->set_inferred_name(*lit->inferred_name());
558 fun->shared()->SetThisPropertyAssignmentsInfo(
559 lit->has_only_simple_this_property_assignments(),
560 *lit->this_property_assignments());
561 fun->shared()->set_try_full_codegen(lit->try_full_codegen());
565 } } // namespace v8::internal