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