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