From 5c19f1f546e56effbf1562f30c24042206a248fa Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Thu, 5 May 2016 17:33:40 +0200 Subject: [PATCH] Limit scope of modeline search (#2967) Only matches the first and last 5 lines against the modeline regular expressions --- lib/linguist/strategy/modeline.rb | 8 +- samples/C/main.c | 397 ++++++++++++++++++++++++++++++ test/test_modelines.rb | 1 + 3 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 samples/C/main.c diff --git a/lib/linguist/strategy/modeline.rb b/lib/linguist/strategy/modeline.rb index eb5a8a5f..4e16a03c 100644 --- a/lib/linguist/strategy/modeline.rb +++ b/lib/linguist/strategy/modeline.rb @@ -15,6 +15,10 @@ module Linguist MODELINES = [EMACS_MODELINE, VIM_MODELINE_1, VIM_MODELINE_2] + # Scope of the search for modelines + # Number of lines to check at the beginning and at the end of the file + SEARCH_SCOPE = 5 + # Public: Detects language based on Vim and Emacs modelines # # blob - An object that quacks like a blob. @@ -26,7 +30,9 @@ module Linguist # Returns an Array with one Language if the blob has a Vim or Emacs modeline # that matches a Language name or alias. Returns an empty array if no match. def self.call(blob, _ = nil) - Array(Language.find_by_alias(modeline(blob.data))) + header = blob.lines.first(SEARCH_SCOPE).join("\n") + footer = blob.lines.last(SEARCH_SCOPE).join("\n") + Array(Language.find_by_alias(modeline(header + footer))) end # Public: Get the modeline from the first n-lines of the file diff --git a/samples/C/main.c b/samples/C/main.c new file mode 100644 index 00000000..3b5eec22 --- /dev/null +++ b/samples/C/main.c @@ -0,0 +1,397 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/runtime/runtime-utils.h" + +#include "src/arguments.h" +#include "src/compiler.h" +#include "src/deoptimizer.h" +#include "src/frames-inl.h" +#include "src/full-codegen/full-codegen.h" +#include "src/isolate-inl.h" +#include "src/messages.h" +#include "src/v8threads.h" +#include "src/vm-state-inl.h" + +namespace v8 { +namespace internal { + +RUNTIME_FUNCTION(Runtime_CompileLazy) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + +#ifdef DEBUG + if (FLAG_trace_lazy && !function->shared()->is_compiled()) { + PrintF("[unoptimized: "); + function->PrintName(); + PrintF("]\n"); + } +#endif + + StackLimitCheck check(isolate); + if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); + if (!Compiler::Compile(function, Compiler::KEEP_EXCEPTION)) { + return isolate->heap()->exception(); + } + DCHECK(function->is_compiled()); + return function->code(); +} + +RUNTIME_FUNCTION(Runtime_CompileBaseline) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + StackLimitCheck check(isolate); + if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); + if (!Compiler::CompileBaseline(function)) { + return isolate->heap()->exception(); + } + DCHECK(function->is_compiled()); + return function->code(); +} + +RUNTIME_FUNCTION(Runtime_CompileOptimized_Concurrent) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + StackLimitCheck check(isolate); + if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); + if (!Compiler::CompileOptimized(function, Compiler::CONCURRENT)) { + return isolate->heap()->exception(); + } + DCHECK(function->is_compiled()); + return function->code(); +} + + +RUNTIME_FUNCTION(Runtime_CompileOptimized_NotConcurrent) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + StackLimitCheck check(isolate); + if (check.JsHasOverflowed(1 * KB)) return isolate->StackOverflow(); + if (!Compiler::CompileOptimized(function, Compiler::NOT_CONCURRENT)) { + return isolate->heap()->exception(); + } + DCHECK(function->is_compiled()); + return function->code(); +} + + +RUNTIME_FUNCTION(Runtime_NotifyStubFailure) { + HandleScope scope(isolate); + DCHECK(args.length() == 0); + Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate); + DCHECK(AllowHeapAllocation::IsAllowed()); + delete deoptimizer; + return isolate->heap()->undefined_value(); +} + + +class ActivationsFinder : public ThreadVisitor { +public: + Code* code_; + bool has_code_activations_; + + explicit ActivationsFinder(Code* code) + : code_(code), has_code_activations_(false) {} + + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { + JavaScriptFrameIterator it(isolate, top); + VisitFrames(&it); + } + + void VisitFrames(JavaScriptFrameIterator* it) { + for (; !it->done(); it->Advance()) { + JavaScriptFrame* frame = it->frame(); + if (code_->contains(frame->pc())) has_code_activations_ = true; + } + } +}; + + +RUNTIME_FUNCTION(Runtime_NotifyDeoptimized) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_SMI_ARG_CHECKED(type_arg, 0); + Deoptimizer::BailoutType type = + static_cast(type_arg); + Deoptimizer* deoptimizer = Deoptimizer::Grab(isolate); + DCHECK(AllowHeapAllocation::IsAllowed()); + TimerEventScope timer(isolate); + TRACE_EVENT0("v8", "V8.DeoptimizeCode"); + + Handle function = deoptimizer->function(); + Handle optimized_code = deoptimizer->compiled_code(); + + DCHECK(optimized_code->kind() == Code::OPTIMIZED_FUNCTION); + DCHECK(type == deoptimizer->bailout_type()); + + // Make sure to materialize objects before causing any allocation. + JavaScriptFrameIterator it(isolate); + deoptimizer->MaterializeHeapObjects(&it); + delete deoptimizer; + + // Ensure the context register is updated for materialized objects. + JavaScriptFrameIterator top_it(isolate); + JavaScriptFrame* top_frame = top_it.frame(); + isolate->set_context(Context::cast(top_frame->context())); + + if (type == Deoptimizer::LAZY) { + return isolate->heap()->undefined_value(); + } + + // Search for other activations of the same optimized code. + // At this point {it} is at the topmost frame of all the frames materialized + // by the deoptimizer. Note that this frame does not necessarily represent + // an activation of {function} because of potential inlined tail-calls. + ActivationsFinder activations_finder(*optimized_code); + activations_finder.VisitFrames(&it); + isolate->thread_manager()->IterateArchivedThreads(&activations_finder); + + if (!activations_finder.has_code_activations_) { + if (function->code() == *optimized_code) { + if (FLAG_trace_deopt) { + PrintF("[removing optimized code for: "); + function->PrintName(); + PrintF("]\n"); + } + function->ReplaceCode(function->shared()->code()); + } + // Evict optimized code for this function from the cache so that it + // doesn't get used for new closures. + function->shared()->EvictFromOptimizedCodeMap(*optimized_code, "notify deoptimized"); + } else { + // TODO(titzer): we should probably do DeoptimizeCodeList(code) + // unconditionally if the code is not already marked for deoptimization. + // If there is an index by shared function info, all the better. + Deoptimizer::DeoptimizeFunction(*function); + } + + return isolate->heap()->undefined_value(); +} + + +static bool IsSuitableForOnStackReplacement( + Isolate* isolate, + Handle function +) { + // Keep track of whether we've succeeded in optimizing. + if (function->shared()->optimization_disabled()) return false; + // If we are trying to do OSR when there are already optimized + // activations of the function, it means (a) the function is directly or + // indirectly recursive and (b) an optimized invocation has been + // deoptimized so that we are currently in an unoptimized activation. + // Check for optimized activations of this function. + for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) { + JavaScriptFrame* frame = it.frame(); + if (frame->is_optimized() && frame->function() == *function) return false; + } + + return true; +} + + +RUNTIME_FUNCTION(Runtime_CompileForOnStackReplacement) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + Handle caller_code(function->shared()->code()); + + // We're not prepared to handle a function with arguments object. + DCHECK(!function->shared()->uses_arguments()); + + RUNTIME_ASSERT(FLAG_use_osr); + + // Passing the PC in the javascript frame from the caller directly is + // not GC safe, so we walk the stack to get it. + JavaScriptFrameIterator it(isolate); + JavaScriptFrame* frame = it.frame(); + if (!caller_code->contains(frame->pc())) { + // Code on the stack may not be the code object referenced by the shared + // function info. It may have been replaced to include deoptimization data. + caller_code = Handle(frame->LookupCode()); + } + + uint32_t pc_offset = + static_cast(frame->pc() - caller_code->instruction_start()); + +#ifdef DEBUG + DCHECK_EQ(frame->function(), *function); + DCHECK_EQ(frame->LookupCode(), *caller_code); + DCHECK(caller_code->contains(frame->pc())); +#endif // DEBUG + + BailoutId ast_id = caller_code->TranslatePcOffsetToAstId(pc_offset); + DCHECK(!ast_id.IsNone()); + + MaybeHandle maybe_result; + if (IsSuitableForOnStackReplacement(isolate, function)) { + if (FLAG_trace_osr) { + PrintF("[OSR - Compiling: "); + function->PrintName(); + PrintF(" at -*- scheme -*- %d]\n", ast_id.ToInt()); + } + maybe_result = Compiler::GetOptimizedCodeForOSR(function, ast_id, frame); + } + + // Revert the patched back edge table, regardless of whether OSR succeeds. + BackEdgeTable::Revert(isolate, *caller_code); + + // Check whether we ended up with usable optimized code. + Handle result; + if (maybe_result.ToHandle(&result) + && result->kind() == Code::OPTIMIZED_FUNCTION) { + DeoptimizationInputData* data = + DeoptimizationInputData::cast(result->deoptimization_data()); + + if (data->OsrPcOffset()->value() >= 0) { + DCHECK(BailoutId(data->OsrAstId()->value()) == ast_id); + if (FLAG_trace_osr) { + PrintF("[OSR - Entry at AST id %d, offset %d in optimized code]\n", + ast_id.ToInt(), data->OsrPcOffset()->value()); + } + // TODO(titzer): this is a massive hack to make the deopt counts + // match. Fix heuristics for reenabling optimizations! + function->shared()->increment_deopt_count(); + + if (result->is_turbofanned()) { + // TurboFanned OSR code cannot be installed into the function. + // But the function is obviously hot, so optimize it next time. + function->ReplaceCode( + isolate->builtins()->builtin(Builtins::kCompileOptimized)); + } else { + // Crankshafted OSR code can be installed into the function. + function->ReplaceCode(*result); + } + return *result; + } + } + + // Failed. + if (FLAG_trace_osr) { + PrintF("[OSR - Failed: "); + function->PrintName(); + PrintF(" at AST id %d]\n", ast_id.ToInt()); + } + + if (!function->IsOptimized()) { + function->ReplaceCode(function->shared()->code()); + } + return NULL; +} + + +RUNTIME_FUNCTION(Runtime_TryInstallOptimizedCode) { + HandleScope scope(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + + // First check if this is a real stack overflow. + StackLimitCheck check(isolate); + if (check.JsHasOverflowed()) { + SealHandleScope shs(isolate); + return isolate->StackOverflow(); + } + + isolate->optimizing_compile_dispatcher()->InstallOptimizedFunctions(); + return (function->IsOptimized()) + ? function->code() + : function->shared()->code(); +} + + +bool CodeGenerationFromStringsAllowed( + Isolate* isolate, + Handle context +){ + DCHECK(context->allow_code_gen_from_strings()->IsFalse()); + // Check with callback if set. + AllowCodeGenerationFromStringsCallback callback = + isolate->allow_code_gen_callback(); + if (callback == NULL) { + // No callback set and code generation disallowed. + return false; + } else { + // Callback set. Let it decide if code generation is allowed. + VMState state(isolate); + return callback(v8::Utils::ToLocal(context)); + } +} + +static Object* CompileGlobalEval( + Isolate* isolate, + Handle source, + Handle outer_info, + LanguageMode language_mode, + int eval_scope_position, + int eval_position +){ + Handle context = Handle(isolate->context()); + Handle native_context = Handle(context->native_context()); + + // Check if native context allows code generation from + // strings. Throw an exception if it doesn't. + if (native_context->allow_code_gen_from_strings()->IsFalse() && + !CodeGenerationFromStringsAllowed(isolate, native_context)) { + Handle error_message = + native_context->ErrorMessageForCodeGenerationFromStrings(); + Handle error; + MaybeHandle maybe_error = isolate->factory()->NewEvalError( + MessageTemplate::kCodeGenFromStrings, error_message); + if (maybe_error.ToHandle(&error)) isolate->Throw(*error); + return isolate->heap()->exception(); + } + + // Deal with a normal eval call with a string argument. Compile it + // and return the compiled function bound in the local context. + static const ParseRestriction restriction = NO_PARSE_RESTRICTION; + Handle compiled; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, compiled, + Compiler::GetFunctionFromEval( + source, outer_info, context, language_mode, + restriction, eval_scope_position, eval_position + ), + isolate->heap()->exception() + ); + return *compiled; +} + + +RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) { + HandleScope scope(isolate); + DCHECK(args.length() == 6); + + Handle callee = args.at(0); + + // If "eval" didn't refer to the original GlobalEval, it's not a + // direct call to eval. + // (And even if it is, but the first argument isn't a string, just let + // execution default to an indirect call to eval, which will also return + // the first argument without doing anything). + if (*callee != isolate->native_context()->global_eval_fun() || !args[1]->IsString()) { + return *callee; + } + + DCHECK(args[3]->IsSmi()); + DCHECK(is_valid_language_mode(args.smi_at(3))); + LanguageMode language_mode = static_cast(args.smi_at(3)); + DCHECK(args[4]->IsSmi()); + Handle outer_info(args.at(2)->shared(), isolate); + return CompileGlobalEval( + isolate, + args.at(1), + outer_info, + language_mode, + args.smi_at(4), + args.smi_at(5) + ); +} +} // namespace internal +} // namespace v8 + +/* vim: set shiftwidth=4 softtabstop=0 cindent cinoptions={1s: */ + diff --git a/test/test_modelines.rb b/test/test_modelines.rb index 85718955..192da9d4 100644 --- a/test/test_modelines.rb +++ b/test/test_modelines.rb @@ -31,6 +31,7 @@ class TestModelines < Minitest::Test assert_modeline Language["Prolog"], fixture_blob("Data/Modelines/not_perl.pl") assert_modeline Language["Smalltalk"], fixture_blob("Data/Modelines/example_smalltalk.md") assert_modeline Language["PHP"], fixture_blob("Data/Modelines/iamphp.inc") + assert_modeline nil, sample_blob("C/main.c") end def test_modeline_languages