OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/debug/debug-interface.h" | 5 #include "src/debug/debug-interface.h" |
6 #include "src/property-descriptor.h" | |
6 #include "src/wasm/wasm-macro-gen.h" | 7 #include "src/wasm/wasm-macro-gen.h" |
7 #include "src/wasm/wasm-objects.h" | 8 #include "src/wasm/wasm-objects.h" |
8 | 9 |
9 #include "test/cctest/cctest.h" | 10 #include "test/cctest/cctest.h" |
10 #include "test/cctest/compiler/value-helper.h" | 11 #include "test/cctest/compiler/value-helper.h" |
11 #include "test/cctest/wasm/wasm-run-utils.h" | 12 #include "test/cctest/wasm/wasm-run-utils.h" |
12 #include "test/common/wasm/test-signatures.h" | 13 #include "test/common/wasm/test-signatures.h" |
13 | 14 |
14 using namespace v8::internal; | 15 using namespace v8::internal; |
15 using namespace v8::internal::wasm; | 16 using namespace v8::internal::wasm; |
(...skipping 27 matching lines...) Expand all Loading... | |
43 } | 44 } |
44 } | 45 } |
45 void CheckLocationsFail(WasmCompiledModule *compiled_module, | 46 void CheckLocationsFail(WasmCompiledModule *compiled_module, |
46 debug::Location start, debug::Location end) { | 47 debug::Location start, debug::Location end) { |
47 std::vector<debug::Location> locations; | 48 std::vector<debug::Location> locations; |
48 bool success = | 49 bool success = |
49 compiled_module->GetPossibleBreakpoints(start, end, &locations); | 50 compiled_module->GetPossibleBreakpoints(start, end, &locations); |
50 CHECK(!success); | 51 CHECK(!success); |
51 } | 52 } |
52 | 53 |
54 class CountBreaks { | |
55 public: | |
56 explicit CountBreaks(Isolate *isolate) : isolate_(isolate) { | |
57 current_handler = this; | |
58 isolate->debug()->SetMessageHandler(&HandleMessage); | |
59 } | |
60 ~CountBreaks() { | |
61 CHECK_EQ(this, current_handler); | |
62 current_handler = nullptr; | |
63 isolate_->debug()->SetMessageHandler(nullptr); | |
64 } | |
65 | |
66 int count() const { return count_; } | |
67 | |
68 private: | |
69 Isolate *isolate_; | |
70 int count_ = 0; | |
71 | |
72 static CountBreaks *current_handler; | |
titzer
2017/01/16 13:55:27
Type* instead of Type *, here and elsewhere.
Clemens Hammacher
2017/01/16 18:51:31
Same.
| |
73 | |
74 static void HandleMessage(const v8::Debug::Message &message) { | |
75 // Ignore responses. | |
76 if (!message.IsEvent()) return; | |
77 // Ignore everything except break events. | |
78 if (message.GetEvent() != v8::DebugEvent::Break) return; | |
79 | |
80 printf("break!\n"); | |
81 CHECK_NOT_NULL(current_handler); | |
82 current_handler->count_ += 1; | |
83 // Don't run into an endless loop. | |
84 CHECK_GT(100, current_handler->count_); | |
85 | |
86 const char command[] = "{\"type\":\"request\", \"command\":\"continue\"}"; | |
87 uint16_t command_u16[arraysize(command) - 1]; | |
88 for (unsigned i = 0; i < arraysize(command) - 1; ++i) { | |
89 command_u16[i] = command[i]; | |
90 } | |
91 current_handler->isolate_->debug()->EnqueueCommandMessage( | |
92 ArrayVector(command_u16), message.GetClientData()); | |
93 } | |
94 }; | |
95 | |
96 // static | |
97 CountBreaks *CountBreaks::current_handler = nullptr; | |
98 | |
99 Handle<JSObject> MakeFakeBreakpoint(Isolate *isolate, int position) { | |
100 Handle<JSObject> obj = | |
101 isolate->factory()->NewJSObject(isolate->object_function()); | |
102 auto SetProperty = [obj, isolate](const char *name, Object *value) { | |
titzer
2017/01/16 13:55:27
Not sure why you need a lambda here, since this is
Clemens Hammacher
2017/01/16 18:51:31
Oh, right. It was more properties before. Removed
| |
103 PropertyDescriptor desc; | |
104 desc.set_value(handle(value, isolate)); | |
105 CHECK(JSObject::DefineOwnProperty( | |
106 isolate, obj, | |
107 isolate->factory()->InternalizeUtf8String(CStrVector(name)), | |
108 &desc, Object::DONT_THROW) | |
109 .FromMaybe(false)); | |
110 }; | |
111 // Generate an "isTriggered" method that always returns true. | |
112 // This can/must be refactored once we remove remaining JS parts from the | |
113 // debugger (bug 5530). | |
114 Handle<String> source = isolate->factory()->NewStringFromStaticChars("true"); | |
115 Handle<Context> context(isolate->context(), isolate); | |
116 Handle<JSFunction> triggered_fun = | |
117 Compiler::GetFunctionFromString(context, source, NO_PARSE_RESTRICTION) | |
118 .ToHandleChecked(); | |
119 SetProperty("isTriggered", *triggered_fun); | |
120 return obj; | |
121 } | |
122 | |
123 void SetBreakpoint(WasmRunnerBase &runner, int function_index, int byte_offset, | |
124 int expected_set_byte_offset = -1) { | |
125 int func_offset = | |
126 runner.module().module->functions[function_index].code_start_offset; | |
127 int code_offset = func_offset + byte_offset; | |
128 if (expected_set_byte_offset == -1) expected_set_byte_offset = byte_offset; | |
129 Handle<WasmInstanceObject> instance = runner.module().instance_object(); | |
130 Handle<WasmCompiledModule> compiled_module(instance->compiled_module()); | |
131 Handle<JSObject> fake_breakpoint_object = | |
132 MakeFakeBreakpoint(runner.main_isolate(), code_offset); | |
133 CHECK(WasmCompiledModule::SetBreakPoint(compiled_module, &code_offset, | |
134 fake_breakpoint_object)); | |
135 int set_byte_offset = code_offset - func_offset; | |
136 CHECK_EQ(expected_set_byte_offset, set_byte_offset); | |
137 // Also set breakpoint on the debug info of the instance directly, since the | |
138 // instance chain is not setup properly in tests. | |
139 Handle<WasmDebugInfo> debug_info = | |
140 WasmInstanceObject::GetOrCreateDebugInfo(instance); | |
141 WasmDebugInfo::SetBreakpoint(debug_info, function_index, set_byte_offset); | |
142 } | |
143 | |
53 } // namespace | 144 } // namespace |
54 | 145 |
55 TEST(CollectPossibleBreakpoints) { | 146 TEST(CollectPossibleBreakpoints) { |
56 WasmRunner<int> runner(kExecuteCompiled); | 147 WasmRunner<int> runner(kExecuteCompiled); |
57 | 148 |
58 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE)); | 149 BUILD(runner, WASM_NOP, WASM_I32_ADD(WASM_ZERO, WASM_ONE)); |
59 | 150 |
60 Handle<WasmInstanceObject> instance = runner.module().instance_object(); | 151 Handle<WasmInstanceObject> instance = runner.module().instance_object(); |
61 std::vector<debug::Location> locations; | 152 std::vector<debug::Location> locations; |
62 CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, | 153 CheckLocations(instance->compiled_module(), {0, 0}, {1, 0}, |
63 {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); | 154 {{0, 1}, {0, 2}, {0, 4}, {0, 6}, {0, 7}}); |
64 CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); | 155 CheckLocations(instance->compiled_module(), {0, 2}, {0, 4}, {{0, 2}}); |
65 CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); | 156 CheckLocations(instance->compiled_module(), {0, 2}, {0, 5}, {{0, 2}, {0, 4}}); |
66 CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); | 157 CheckLocations(instance->compiled_module(), {0, 7}, {0, 8}, {{0, 7}}); |
67 CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); | 158 CheckLocations(instance->compiled_module(), {0, 7}, {1, 0}, {{0, 7}}); |
68 CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); | 159 CheckLocations(instance->compiled_module(), {0, 8}, {1, 0}, {}); |
69 CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); | 160 CheckLocationsFail(instance->compiled_module(), {0, 9}, {1, 0}); |
70 } | 161 } |
162 | |
163 TEST(TestSimpleBreak) { | |
164 WasmRunner<int> runner(kExecuteCompiled); | |
165 WasmFunctionCompiler &f2 = runner.NewFunction<int>(); | |
166 | |
167 BUILD(f2, WASM_NOP, WASM_I32_ADD(WASM_I32V_1(11), WASM_I32V_1(3))); | |
168 BUILD(runner, WASM_CALL_FUNCTION0(f2.function_index())); | |
169 | |
170 SetBreakpoint(runner, f2.function_index(), 4, 4); | |
171 Handle<JSFunction> main_fun_wrapper = runner.module().WrapCode(0); | |
172 Isolate *isolate = runner.main_isolate(); | |
173 | |
174 CountBreaks count_breaks(isolate); | |
175 CHECK_EQ(0, count_breaks.count()); | |
176 | |
177 Handle<Object> global(isolate->context()->global_object(), isolate); | |
178 MaybeHandle<Object> retval = | |
179 Execution::Call(isolate, main_fun_wrapper, global, 0, nullptr); | |
180 CHECK(!retval.is_null()); | |
181 int result; | |
182 CHECK(retval.ToHandleChecked()->ToInt32(&result)); | |
183 CHECK_EQ(14, result); | |
184 | |
185 CHECK_EQ(1, count_breaks.count()); | |
186 } | |
187 | |
188 TEST(TestArgumentPassing) { | |
titzer
2017/01/16 13:55:27
This test is a bit too complicated, IMO.
It is te
Clemens Hammacher
2017/01/16 18:51:31
Counting the number of breaks is basically only th
| |
189 // The second and third argument will be combined to an i64. | |
190 WasmRunner<double, int, int, int, float, double> runner(kExecuteCompiled); | |
191 WasmFunctionCompiler &f2 = | |
192 runner.NewFunction<double, int, int64_t, float, double>(); | |
193 | |
194 // Convert all arguments to double, add them and return the sum. | |
195 BUILD(f2, | |
196 WASM_F64_ADD( // <0+1+2> + <3> | |
197 WASM_F64_ADD( // <0+1> + <2> | |
198 WASM_F64_ADD( // <0> + <1> | |
199 WASM_F64_SCONVERT_I32(WASM_GET_LOCAL(0)), // <0> to double | |
200 WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(1))), // <1> to double | |
201 WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double | |
202 WASM_GET_LOCAL(3))); // <3> | |
203 | |
204 BUILD(runner, WASM_GET_LOCAL(0), // first arg | |
205 WASM_I64_IOR(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(1)), // second arg | |
206 WASM_I64_SHL(WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(2)), | |
207 WASM_I64V_1(32))), | |
208 WASM_GET_LOCAL(3), // third arg | |
209 WASM_GET_LOCAL(4), // fourth arg | |
210 WASM_CALL_FUNCTION0(f2.function_index())); | |
211 | |
212 // Set a breakpoint to check that we really execute in the interpreter. | |
213 SetBreakpoint(runner, f2.function_index(), 1, 1); | |
214 | |
215 Handle<JSFunction> main_fun_wrapper = runner.module().WrapCode(0); | |
216 Isolate *isolate = runner.main_isolate(); | |
217 Handle<Object> global(isolate->context()->global_object(), isolate); | |
218 | |
219 CountBreaks count_breaks(isolate); | |
220 int num_breaks = 0; | |
221 | |
222 auto CheckCall = [&](int32_t a, int64_t b, float c, double d) { | |
223 Handle<Object> args[5] = { | |
224 isolate->factory()->NewNumberFromInt(a), | |
225 isolate->factory()->NewNumberFromInt((b & 0xffffffff)), | |
226 isolate->factory()->NewNumberFromInt((b >> 32) & 0xffffffff), | |
227 isolate->factory()->NewHeapNumber(c), | |
228 isolate->factory()->NewHeapNumber(d)}; | |
229 | |
230 CHECK_EQ(num_breaks, count_breaks.count()); | |
231 MaybeHandle<Object> retval = Execution::Call(isolate, main_fun_wrapper, | |
232 global, arraysize(args), args); | |
233 CHECK_EQ(++num_breaks, count_breaks.count()); | |
234 double result = retval.ToHandleChecked()->Number(); | |
235 double sum = static_cast<double>(a) + static_cast<double>(b) + | |
236 static_cast<double>(c) + static_cast<double>(d); | |
237 CHECK_EQ(sum, result); | |
238 }; | |
239 | |
240 CheckCall(1, 2, 3, 4); | |
241 CheckCall(1, 2, 4, 8); | |
242 CheckCall(std::numeric_limits<int32_t>::max(), | |
243 std::numeric_limits<int64_t>::max(), 0, 0); | |
244 CheckCall(0, 0, 0.1f, 0.3); | |
245 CheckCall(0x0ff00000, 0, 0, 0); | |
246 CheckCall(0x00000ff0, 0, 0, 0); | |
247 CheckCall(0, 0x0000000000000ff0, 0, 0); | |
248 CheckCall(0, 0x000000000ff00000, 0, 0); | |
249 CheckCall(0, 0x00000ff000000000, 0, 0); | |
250 CheckCall(0, 0x0ff0000000000000, 0, 0); | |
251 CheckCall(-3, -11, 0, 0); | |
252 CheckCall(0, 0, -3, -11); | |
253 } | |
OLD | NEW |