Index: src/debug/debug-evaluate.cc |
diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc |
index e93b5952c876ad2ffc7ae80579cf7ed08520efbb..8f068284a71b863f55581ffe18b1903a8ea77411 100644 |
--- a/src/debug/debug-evaluate.cc |
+++ b/src/debug/debug-evaluate.cc |
@@ -12,6 +12,8 @@ |
#include "src/debug/debug.h" |
#include "src/frames-inl.h" |
#include "src/globals.h" |
+#include "src/interpreter/bytecode-array-iterator.h" |
+#include "src/interpreter/bytecodes.h" |
#include "src/isolate-inl.h" |
namespace v8 { |
@@ -92,9 +94,13 @@ MaybeHandle<Object> DebugEvaluate::Evaluate( |
Object); |
Handle<Object> result; |
- ASSIGN_RETURN_ON_EXCEPTION( |
- isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), |
- Object); |
+ { |
+ NoSideEffectScope no_side_effect(isolate, |
+ FLAG_side_effect_free_debug_evaluate); |
+ ASSIGN_RETURN_ON_EXCEPTION( |
+ isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), |
+ Object); |
+ } |
// Skip the global proxy as it has no properties and always delegates to the |
// real global object. |
@@ -249,5 +255,145 @@ void DebugEvaluate::ContextBuilder::MaterializeReceiver( |
JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check(); |
} |
+namespace { |
+ |
+bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { |
+ DCHECK_EQ(Runtime::INLINE, Runtime::FunctionForId(id)->intrinsic_type); |
+ switch (id) { |
+ // Whitelist for intrinsics. |
+ case Runtime::kInlineToObject: |
+ return true; |
+ default: |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n", |
+ Runtime::FunctionForId(id)->name); |
+ } |
+ return false; |
+ } |
+} |
+ |
+bool RuntimeFunctionHasNoSideEffect(Runtime::FunctionId id) { |
+ DCHECK_EQ(Runtime::RUNTIME, Runtime::FunctionForId(id)->intrinsic_type); |
+ switch (id) { |
+ // Whitelist for runtime functions. |
+ case Runtime::kToObject: |
+ case Runtime::kLoadLookupSlotForCall: |
+ case Runtime::kThrowReferenceError: |
+ return true; |
+ default: |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] runtime %s may cause side effect.\n", |
+ Runtime::FunctionForId(id)->name); |
+ } |
+ return false; |
+ } |
+} |
+ |
+bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { |
+ typedef interpreter::Bytecode Bytecode; |
+ typedef interpreter::Bytecodes Bytecodes; |
+ if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true; |
+ if (Bytecodes::IsCallOrNew(bytecode)) return true; |
+ switch (bytecode) { |
+ // Whitelist for bytecodes. |
+ case Bytecode::kStackCheck: |
+ case Bytecode::kLdaLookupSlot: |
+ case Bytecode::kLdaGlobal: |
+ case Bytecode::kLdaNamedProperty: |
+ case Bytecode::kLdaKeyedProperty: |
+ case Bytecode::kAdd: |
+ case Bytecode::kReturn: |
+ case Bytecode::kCreateCatchContext: |
+ case Bytecode::kSetPendingMessage: |
+ return true; |
+ default: |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] bytecode %s may cause side effect.\n", |
+ Bytecodes::ToString(bytecode)); |
+ } |
+ return false; |
+ } |
+} |
+ |
+bool BuiltinHasNoSideEffect(Builtins::Name id) { |
+ switch (id) { |
+ // Whitelist for builtins. |
+ case Builtins::kMathSin: |
+ return true; |
+ default: |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] built-in %s may cause side effect.\n", |
+ Builtins::name(id)); |
+ } |
+ return false; |
+ } |
+} |
+ |
+static const Address accessors_with_no_side_effect[] = { |
+ // Whitelist for accessors. |
+ FUNCTION_ADDR(Accessors::StringLengthGetter), |
+ FUNCTION_ADDR(Accessors::ArrayLengthGetter)}; |
+ |
+} // anonymous namespace |
+ |
+// static |
+bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) { |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] Checking function %s for side effect.\n", |
+ info->DebugName()->ToCString().get()); |
+ } |
+ |
+ DCHECK(info->is_compiled()); |
+ |
+ if (info->HasBytecodeArray()) { |
+ // Check bytecodes against whitelist. |
+ Handle<BytecodeArray> bytecode_array(info->bytecode_array()); |
+ if (FLAG_trace_side_effect_free_debug_evaluate) bytecode_array->Print(); |
+ for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done(); |
+ it.Advance()) { |
+ interpreter::Bytecode bytecode = it.current_bytecode(); |
+ |
+ if (interpreter::Bytecodes::IsCallRuntime(bytecode)) { |
+ if (bytecode == interpreter::Bytecode::kInvokeIntrinsic) { |
+ Runtime::FunctionId id = it.GetIntrinsicIdOperand(0); |
+ if (IntrinsicHasNoSideEffect(id)) continue; |
+ } else { |
+ Runtime::FunctionId id = it.GetRuntimeIdOperand(0); |
+ if (RuntimeFunctionHasNoSideEffect(id)) continue; |
+ } |
+ return false; |
+ } |
+ |
+ if (BytecodeHasNoSideEffect(bytecode)) continue; |
+ |
+ // Did not match whitelist. |
+ return false; |
+ } |
+ return true; |
+ } else { |
+ // Check built-ins against whitelist. |
+ int builtin_index = info->code()->builtin_index(); |
+ if (builtin_index >= 0 && builtin_index < Builtins::builtin_count && |
+ BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) { |
+ return true; |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+// static |
+bool DebugEvaluate::CallbackHasNoSideEffect(Address function_addr) { |
+ for (size_t i = 0; i < arraysize(accessors_with_no_side_effect); i++) { |
+ if (function_addr == accessors_with_no_side_effect[i]) return true; |
+ } |
+ |
+ if (FLAG_trace_side_effect_free_debug_evaluate) { |
+ PrintF("[debug-evaluate] API Callback at %p may cause side effect.\n", |
+ reinterpret_cast<void*>(function_addr)); |
+ } |
+ return false; |
+} |
+ |
} // namespace internal |
} // namespace v8 |