OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 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-evaluate.h" | 5 #include "src/debug/debug-evaluate.h" |
6 | 6 |
7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
8 #include "src/compiler.h" | 8 #include "src/compiler.h" |
9 #include "src/contexts.h" | 9 #include "src/contexts.h" |
10 #include "src/debug/debug-frames.h" | 10 #include "src/debug/debug-frames.h" |
11 #include "src/debug/debug-scopes.h" | 11 #include "src/debug/debug-scopes.h" |
12 #include "src/debug/debug.h" | 12 #include "src/debug/debug.h" |
13 #include "src/frames-inl.h" | 13 #include "src/frames-inl.h" |
14 #include "src/globals.h" | 14 #include "src/globals.h" |
| 15 #include "src/interpreter/bytecode-array-iterator.h" |
| 16 #include "src/interpreter/bytecodes.h" |
15 #include "src/isolate-inl.h" | 17 #include "src/isolate-inl.h" |
16 | 18 |
17 namespace v8 { | 19 namespace v8 { |
18 namespace internal { | 20 namespace internal { |
19 | 21 |
20 static inline bool IsDebugContext(Isolate* isolate, Context* context) { | 22 static inline bool IsDebugContext(Isolate* isolate, Context* context) { |
21 return context->native_context() == *isolate->debug()->debug_context(); | 23 return context->native_context() == *isolate->debug()->debug_context(); |
22 } | 24 } |
23 | 25 |
24 MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, | 26 MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
85 Handle<Context> context, Handle<Object> receiver, Handle<String> source) { | 87 Handle<Context> context, Handle<Object> receiver, Handle<String> source) { |
86 Handle<JSFunction> eval_fun; | 88 Handle<JSFunction> eval_fun; |
87 ASSIGN_RETURN_ON_EXCEPTION( | 89 ASSIGN_RETURN_ON_EXCEPTION( |
88 isolate, eval_fun, | 90 isolate, eval_fun, |
89 Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY, | 91 Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY, |
90 NO_PARSE_RESTRICTION, kNoSourcePosition, | 92 NO_PARSE_RESTRICTION, kNoSourcePosition, |
91 kNoSourcePosition), | 93 kNoSourcePosition), |
92 Object); | 94 Object); |
93 | 95 |
94 Handle<Object> result; | 96 Handle<Object> result; |
95 ASSIGN_RETURN_ON_EXCEPTION( | 97 { |
96 isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), | 98 NoSideEffectScope no_side_effect(isolate, |
97 Object); | 99 FLAG_side_effect_free_debug_evaluate); |
| 100 ASSIGN_RETURN_ON_EXCEPTION( |
| 101 isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), |
| 102 Object); |
| 103 } |
98 | 104 |
99 // Skip the global proxy as it has no properties and always delegates to the | 105 // Skip the global proxy as it has no properties and always delegates to the |
100 // real global object. | 106 // real global object. |
101 if (result->IsJSGlobalProxy()) { | 107 if (result->IsJSGlobalProxy()) { |
102 PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result)); | 108 PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result)); |
103 // TODO(verwaest): This will crash when the global proxy is detached. | 109 // TODO(verwaest): This will crash when the global proxy is detached. |
104 result = PrototypeIterator::GetCurrent<JSObject>(iter); | 110 result = PrototypeIterator::GetCurrent<JSObject>(iter); |
105 } | 111 } |
106 | 112 |
107 return result; | 113 return result; |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 // 'this' is allocated in an outer context and is is already being | 248 // 'this' is allocated in an outer context and is is already being |
243 // referenced by the current function, so it can be correctly resolved. | 249 // referenced by the current function, so it can be correctly resolved. |
244 return; | 250 return; |
245 } else if (local_function->shared()->scope_info()->HasReceiver() && | 251 } else if (local_function->shared()->scope_info()->HasReceiver() && |
246 !frame_->receiver()->IsTheHole(isolate_)) { | 252 !frame_->receiver()->IsTheHole(isolate_)) { |
247 recv = handle(frame_->receiver(), isolate_); | 253 recv = handle(frame_->receiver(), isolate_); |
248 } | 254 } |
249 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check(); | 255 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check(); |
250 } | 256 } |
251 | 257 |
| 258 namespace { |
| 259 |
| 260 bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { |
| 261 DCHECK_EQ(Runtime::INLINE, Runtime::FunctionForId(id)->intrinsic_type); |
| 262 switch (id) { |
| 263 // Whitelist for intrinsics. |
| 264 case Runtime::kInlineToObject: |
| 265 return true; |
| 266 default: |
| 267 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 268 PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n", |
| 269 Runtime::FunctionForId(id)->name); |
| 270 } |
| 271 return false; |
| 272 } |
| 273 } |
| 274 |
| 275 bool RuntimeFunctionHasNoSideEffect(Runtime::FunctionId id) { |
| 276 DCHECK_EQ(Runtime::RUNTIME, Runtime::FunctionForId(id)->intrinsic_type); |
| 277 switch (id) { |
| 278 // Whitelist for runtime functions. |
| 279 case Runtime::kToObject: |
| 280 case Runtime::kLoadLookupSlotForCall: |
| 281 case Runtime::kThrowReferenceError: |
| 282 return true; |
| 283 default: |
| 284 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 285 PrintF("[debug-evaluate] runtime %s may cause side effect.\n", |
| 286 Runtime::FunctionForId(id)->name); |
| 287 } |
| 288 return false; |
| 289 } |
| 290 } |
| 291 |
| 292 bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { |
| 293 typedef interpreter::Bytecode Bytecode; |
| 294 typedef interpreter::Bytecodes Bytecodes; |
| 295 if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true; |
| 296 if (Bytecodes::IsCallOrNew(bytecode)) return true; |
| 297 switch (bytecode) { |
| 298 // Whitelist for bytecodes. |
| 299 case Bytecode::kStackCheck: |
| 300 case Bytecode::kLdaLookupSlot: |
| 301 case Bytecode::kLdaGlobal: |
| 302 case Bytecode::kLdaNamedProperty: |
| 303 case Bytecode::kLdaKeyedProperty: |
| 304 case Bytecode::kAdd: |
| 305 case Bytecode::kReturn: |
| 306 case Bytecode::kCreateCatchContext: |
| 307 case Bytecode::kSetPendingMessage: |
| 308 return true; |
| 309 default: |
| 310 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 311 PrintF("[debug-evaluate] bytecode %s may cause side effect.\n", |
| 312 Bytecodes::ToString(bytecode)); |
| 313 } |
| 314 return false; |
| 315 } |
| 316 } |
| 317 |
| 318 bool BuiltinHasNoSideEffect(Builtins::Name id) { |
| 319 switch (id) { |
| 320 // Whitelist for builtins. |
| 321 case Builtins::kMathSin: |
| 322 return true; |
| 323 default: |
| 324 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 325 PrintF("[debug-evaluate] built-in %s may cause side effect.\n", |
| 326 Builtins::name(id)); |
| 327 } |
| 328 return false; |
| 329 } |
| 330 } |
| 331 |
| 332 static const Address accessors_with_no_side_effect[] = { |
| 333 // Whitelist for accessors. |
| 334 FUNCTION_ADDR(Accessors::StringLengthGetter), |
| 335 FUNCTION_ADDR(Accessors::ArrayLengthGetter)}; |
| 336 |
| 337 } // anonymous namespace |
| 338 |
| 339 // static |
| 340 bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) { |
| 341 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 342 PrintF("[debug-evaluate] Checking function %s for side effect.\n", |
| 343 info->DebugName()->ToCString().get()); |
| 344 } |
| 345 |
| 346 DCHECK(info->is_compiled()); |
| 347 |
| 348 if (info->HasBytecodeArray()) { |
| 349 // Check bytecodes against whitelist. |
| 350 Handle<BytecodeArray> bytecode_array(info->bytecode_array()); |
| 351 if (FLAG_trace_side_effect_free_debug_evaluate) bytecode_array->Print(); |
| 352 for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done(); |
| 353 it.Advance()) { |
| 354 interpreter::Bytecode bytecode = it.current_bytecode(); |
| 355 |
| 356 if (interpreter::Bytecodes::IsCallRuntime(bytecode)) { |
| 357 if (bytecode == interpreter::Bytecode::kInvokeIntrinsic) { |
| 358 Runtime::FunctionId id = it.GetIntrinsicIdOperand(0); |
| 359 if (IntrinsicHasNoSideEffect(id)) continue; |
| 360 } else { |
| 361 Runtime::FunctionId id = it.GetRuntimeIdOperand(0); |
| 362 if (RuntimeFunctionHasNoSideEffect(id)) continue; |
| 363 } |
| 364 return false; |
| 365 } |
| 366 |
| 367 if (BytecodeHasNoSideEffect(bytecode)) continue; |
| 368 |
| 369 // Did not match whitelist. |
| 370 return false; |
| 371 } |
| 372 return true; |
| 373 } else { |
| 374 // Check built-ins against whitelist. |
| 375 int builtin_index = info->code()->builtin_index(); |
| 376 if (builtin_index >= 0 && builtin_index < Builtins::builtin_count && |
| 377 BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) { |
| 378 return true; |
| 379 } |
| 380 } |
| 381 |
| 382 return false; |
| 383 } |
| 384 |
| 385 // static |
| 386 bool DebugEvaluate::CallbackHasNoSideEffect(Address function_addr) { |
| 387 for (size_t i = 0; i < arraysize(accessors_with_no_side_effect); i++) { |
| 388 if (function_addr == accessors_with_no_side_effect[i]) return true; |
| 389 } |
| 390 |
| 391 if (FLAG_trace_side_effect_free_debug_evaluate) { |
| 392 PrintF("[debug-evaluate] API Callback at %p may cause side effect.\n", |
| 393 reinterpret_cast<void*>(function_addr)); |
| 394 } |
| 395 return false; |
| 396 } |
| 397 |
252 } // namespace internal | 398 } // namespace internal |
253 } // namespace v8 | 399 } // namespace v8 |
OLD | NEW |