Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include <memory>
6 :
7 : #include "include/v8.h"
8 : #include "src/api-inl.h"
9 : #include "src/ast/ast.h"
10 : #include "src/ast/scopes.h"
11 : #include "src/base/platform/semaphore.h"
12 : #include "src/base/template-utils.h"
13 : #include "src/compiler.h"
14 : #include "src/flags.h"
15 : #include "src/isolate-inl.h"
16 : #include "src/objects/smi.h"
17 : #include "src/parsing/parse-info.h"
18 : #include "src/parsing/parser.h"
19 : #include "src/parsing/preparse-data.h"
20 : #include "src/v8.h"
21 : #include "src/zone/zone-list-inl.h"
22 : #include "test/unittests/test-helpers.h"
23 : #include "test/unittests/test-utils.h"
24 : #include "testing/gtest/include/gtest/gtest.h"
25 :
26 : namespace v8 {
27 : namespace internal {
28 :
29 : class BackgroundCompileTaskTest : public TestWithNativeContext {
30 : public:
31 14 : BackgroundCompileTaskTest() : allocator_(isolate()->allocator()) {}
32 14 : ~BackgroundCompileTaskTest() override = default;
33 :
34 : AccountingAllocator* allocator() { return allocator_; }
35 :
36 7 : static void SetUpTestCase() {
37 7 : CHECK_NULL(save_flags_);
38 7 : save_flags_ = new SaveFlags();
39 : TestWithNativeContext::SetUpTestCase();
40 7 : }
41 :
42 7 : static void TearDownTestCase() {
43 : TestWithNativeContext::TearDownTestCase();
44 7 : CHECK_NOT_NULL(save_flags_);
45 7 : delete save_flags_;
46 7 : save_flags_ = nullptr;
47 7 : }
48 :
49 7 : BackgroundCompileTask* NewBackgroundCompileTask(
50 : Isolate* isolate, Handle<SharedFunctionInfo> shared,
51 : size_t stack_size = FLAG_stack_size) {
52 : std::unique_ptr<ParseInfo> outer_parse_info =
53 14 : test::OuterParseInfoForShared(isolate, shared);
54 : AstValueFactory* ast_value_factory =
55 7 : outer_parse_info->GetOrCreateAstValueFactory();
56 : AstNodeFactory ast_node_factory(ast_value_factory,
57 7 : outer_parse_info->zone());
58 :
59 : const AstRawString* function_name =
60 7 : ast_value_factory->GetOneByteString("f");
61 : DeclarationScope* script_scope = new (outer_parse_info->zone())
62 7 : DeclarationScope(outer_parse_info->zone(), ast_value_factory);
63 : DeclarationScope* function_scope =
64 : new (outer_parse_info->zone()) DeclarationScope(
65 7 : outer_parse_info->zone(), script_scope, FUNCTION_SCOPE);
66 14 : function_scope->set_start_position(shared->StartPosition());
67 14 : function_scope->set_end_position(shared->EndPosition());
68 : std::vector<void*> buffer;
69 : ScopedPtrList<Statement> statements(&buffer);
70 : const FunctionLiteral* function_literal =
71 21 : ast_node_factory.NewFunctionLiteral(
72 : function_name, function_scope, statements, -1, -1, -1,
73 : FunctionLiteral::kNoDuplicateParameters,
74 : FunctionLiteral::kAnonymousExpression,
75 : FunctionLiteral::kShouldEagerCompile, shared->StartPosition(), true,
76 7 : shared->FunctionLiteralId(isolate), nullptr);
77 :
78 : return new BackgroundCompileTask(
79 : allocator(), outer_parse_info.get(), function_name, function_literal,
80 : isolate->counters()->worker_thread_runtime_call_stats(),
81 14 : isolate->counters()->compile_function_on_background(), FLAG_stack_size);
82 : }
83 :
84 : private:
85 : AccountingAllocator* allocator_;
86 : static SaveFlags* save_flags_;
87 :
88 : DISALLOW_COPY_AND_ASSIGN(BackgroundCompileTaskTest);
89 : };
90 :
91 : SaveFlags* BackgroundCompileTaskTest::save_flags_ = nullptr;
92 :
93 15445 : TEST_F(BackgroundCompileTaskTest, Construct) {
94 : Handle<SharedFunctionInfo> shared =
95 1 : test::CreateSharedFunctionInfo(isolate(), nullptr);
96 2 : ASSERT_FALSE(shared->is_compiled());
97 : std::unique_ptr<BackgroundCompileTask> task(
98 3 : NewBackgroundCompileTask(isolate(), shared));
99 : }
100 :
101 15445 : TEST_F(BackgroundCompileTaskTest, SyntaxError) {
102 1 : test::ScriptResource* script = new test::ScriptResource("^^^", strlen("^^^"));
103 : Handle<SharedFunctionInfo> shared =
104 1 : test::CreateSharedFunctionInfo(isolate(), script);
105 : std::unique_ptr<BackgroundCompileTask> task(
106 3 : NewBackgroundCompileTask(isolate(), shared));
107 :
108 1 : task->Run();
109 2 : ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
110 : task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
111 1 : ASSERT_TRUE(isolate()->has_pending_exception());
112 :
113 : isolate()->clear_pending_exception();
114 : }
115 :
116 15445 : TEST_F(BackgroundCompileTaskTest, CompileAndRun) {
117 : const char raw_script[] =
118 : "function g() {\n"
119 : " f = function(a) {\n"
120 : " for (var i = 0; i < 3; i++) { a += 20; }\n"
121 : " return a;\n"
122 : " }\n"
123 : " return f;\n"
124 : "}\n"
125 1 : "g();";
126 : test::ScriptResource* script =
127 1 : new test::ScriptResource(raw_script, strlen(raw_script));
128 : Handle<JSFunction> f = RunJS<JSFunction>(script);
129 : Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
130 2 : ASSERT_FALSE(shared->is_compiled());
131 : std::unique_ptr<BackgroundCompileTask> task(
132 3 : NewBackgroundCompileTask(isolate(), shared));
133 :
134 1 : task->Run();
135 2 : ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
136 : task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
137 2 : ASSERT_TRUE(shared->is_compiled());
138 :
139 : Smi value = Smi::cast(*RunJS("f(100);"));
140 1 : ASSERT_TRUE(value == Smi::FromInt(160));
141 : }
142 :
143 15445 : TEST_F(BackgroundCompileTaskTest, CompileFailure) {
144 1 : std::string raw_script("() { var a = ");
145 20001 : for (int i = 0; i < 10000; i++) {
146 : // TODO(leszeks): Figure out a more "unit-test-y" way of forcing an analysis
147 : // failure than a binop stack overflow.
148 :
149 : // Alternate + and - to avoid n-ary operation nodes.
150 : raw_script += "'x' + 'x' - ";
151 : }
152 : raw_script += " 'x'; }";
153 : test::ScriptResource* script =
154 1 : new test::ScriptResource(raw_script.c_str(), strlen(raw_script.c_str()));
155 : Handle<SharedFunctionInfo> shared =
156 1 : test::CreateSharedFunctionInfo(isolate(), script);
157 : std::unique_ptr<BackgroundCompileTask> task(
158 2 : NewBackgroundCompileTask(isolate(), shared, 100));
159 :
160 1 : task->Run();
161 2 : ASSERT_FALSE(Compiler::FinalizeBackgroundCompileTask(
162 : task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
163 1 : ASSERT_TRUE(isolate()->has_pending_exception());
164 :
165 : isolate()->clear_pending_exception();
166 : }
167 :
168 : class CompileTask : public Task {
169 : public:
170 : CompileTask(BackgroundCompileTask* task, base::Semaphore* semaphore)
171 1 : : task_(task), semaphore_(semaphore) {}
172 2 : ~CompileTask() override = default;
173 :
174 1 : void Run() override {
175 1 : task_->Run();
176 1 : semaphore_->Signal();
177 1 : }
178 :
179 : private:
180 : BackgroundCompileTask* task_;
181 : base::Semaphore* semaphore_;
182 : DISALLOW_COPY_AND_ASSIGN(CompileTask);
183 : };
184 :
185 15445 : TEST_F(BackgroundCompileTaskTest, CompileOnBackgroundThread) {
186 : const char* raw_script =
187 : "(a, b) {\n"
188 : " var c = a + b;\n"
189 : " function bar() { return b }\n"
190 : " var d = { foo: 100, bar : bar() }\n"
191 : " return bar;"
192 : "}";
193 : test::ScriptResource* script =
194 1 : new test::ScriptResource(raw_script, strlen(raw_script));
195 : Handle<SharedFunctionInfo> shared =
196 1 : test::CreateSharedFunctionInfo(isolate(), script);
197 : std::unique_ptr<BackgroundCompileTask> task(
198 3 : NewBackgroundCompileTask(isolate(), shared));
199 :
200 2 : base::Semaphore semaphore(0);
201 : auto background_task = base::make_unique<CompileTask>(task.get(), &semaphore);
202 :
203 3 : V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(background_task));
204 1 : semaphore.Wait();
205 2 : ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
206 : task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
207 2 : ASSERT_TRUE(shared->is_compiled());
208 : }
209 :
210 15445 : TEST_F(BackgroundCompileTaskTest, EagerInnerFunctions) {
211 : const char raw_script[] =
212 : "function g() {\n"
213 : " f = function() {\n"
214 : " // Simulate an eager IIFE with brackets.\n "
215 : " var e = (function () { return 42; });\n"
216 : " return e;\n"
217 : " }\n"
218 : " return f;\n"
219 : "}\n"
220 1 : "g();";
221 : test::ScriptResource* script =
222 1 : new test::ScriptResource(raw_script, strlen(raw_script));
223 : Handle<JSFunction> f = RunJS<JSFunction>(script);
224 : Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
225 2 : ASSERT_FALSE(shared->is_compiled());
226 : std::unique_ptr<BackgroundCompileTask> task(
227 3 : NewBackgroundCompileTask(isolate(), shared));
228 :
229 1 : task->Run();
230 2 : ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
231 : task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
232 2 : ASSERT_TRUE(shared->is_compiled());
233 :
234 : Handle<JSFunction> e = RunJS<JSFunction>("f();");
235 :
236 2 : ASSERT_TRUE(e->shared()->is_compiled());
237 : }
238 :
239 15445 : TEST_F(BackgroundCompileTaskTest, LazyInnerFunctions) {
240 : const char raw_script[] =
241 : "function g() {\n"
242 : " f = function() {\n"
243 : " function e() { return 42; };\n"
244 : " return e;\n"
245 : " }\n"
246 : " return f;\n"
247 : "}\n"
248 1 : "g();";
249 : test::ScriptResource* script =
250 1 : new test::ScriptResource(raw_script, strlen(raw_script));
251 : Handle<JSFunction> f = RunJS<JSFunction>(script);
252 : Handle<SharedFunctionInfo> shared = handle(f->shared(), isolate());
253 2 : ASSERT_FALSE(shared->is_compiled());
254 : std::unique_ptr<BackgroundCompileTask> task(
255 3 : NewBackgroundCompileTask(isolate(), shared));
256 :
257 1 : task->Run();
258 2 : ASSERT_TRUE(Compiler::FinalizeBackgroundCompileTask(
259 : task.get(), shared, isolate(), Compiler::KEEP_EXCEPTION));
260 2 : ASSERT_TRUE(shared->is_compiled());
261 :
262 : Handle<JSFunction> e = RunJS<JSFunction>("f();");
263 :
264 2 : ASSERT_FALSE(e->shared()->is_compiled());
265 : }
266 :
267 : } // namespace internal
268 9264 : } // namespace v8
|