Home | History | Annotate | Download | only in debug
      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
      3 // found in the LICENSE file.
      4 
      5 #include "src/debug/debug-evaluate.h"
      6 
      7 #include "src/accessors.h"
      8 #include "src/compiler.h"
      9 #include "src/contexts.h"
     10 #include "src/debug/debug-frames.h"
     11 #include "src/debug/debug-scopes.h"
     12 #include "src/debug/debug.h"
     13 #include "src/frames-inl.h"
     14 #include "src/globals.h"
     15 #include "src/interpreter/bytecode-array-iterator.h"
     16 #include "src/interpreter/bytecodes.h"
     17 #include "src/isolate-inl.h"
     18 
     19 namespace v8 {
     20 namespace internal {
     21 
     22 static inline bool IsDebugContext(Isolate* isolate, Context* context) {
     23   return context->native_context() == *isolate->debug()->debug_context();
     24 }
     25 
     26 MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
     27                                           Handle<String> source) {
     28   // Handle the processing of break.
     29   DisableBreak disable_break_scope(isolate->debug());
     30 
     31   // Enter the top context from before the debugger was invoked.
     32   SaveContext save(isolate);
     33   SaveContext* top = &save;
     34   while (top != NULL && IsDebugContext(isolate, *top->context())) {
     35     top = top->prev();
     36   }
     37   if (top != NULL) isolate->set_context(*top->context());
     38 
     39   // Get the native context now set to the top context from before the
     40   // debugger was invoked.
     41   Handle<Context> context = isolate->native_context();
     42   Handle<JSObject> receiver(context->global_proxy());
     43   Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
     44   return Evaluate(isolate, outer_info, context, receiver, source, false);
     45 }
     46 
     47 MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
     48                                          StackFrame::Id frame_id,
     49                                          int inlined_jsframe_index,
     50                                          Handle<String> source,
     51                                          bool throw_on_side_effect) {
     52   // Handle the processing of break.
     53   DisableBreak disable_break_scope(isolate->debug());
     54 
     55   // Get the frame where the debugging is performed.
     56   StackTraceFrameIterator it(isolate, frame_id);
     57   if (!it.is_javascript()) return isolate->factory()->undefined_value();
     58   JavaScriptFrame* frame = it.javascript_frame();
     59 
     60   // Traverse the saved contexts chain to find the active context for the
     61   // selected frame.
     62   SaveContext* save =
     63       DebugFrameHelper::FindSavedContextForFrame(isolate, frame);
     64   SaveContext savex(isolate);
     65   isolate->set_context(*(save->context()));
     66 
     67   // This is not a lot different than DebugEvaluate::Global, except that
     68   // variables accessible by the function we are evaluating from are
     69   // materialized and included on top of the native context. Changes to
     70   // the materialized object are written back afterwards.
     71   // Note that the native context is taken from the original context chain,
     72   // which may not be the current native context of the isolate.
     73   ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
     74   if (isolate->has_pending_exception()) return MaybeHandle<Object>();
     75 
     76   Handle<Context> context = context_builder.evaluation_context();
     77   Handle<JSObject> receiver(context->global_proxy());
     78   MaybeHandle<Object> maybe_result =
     79       Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
     80                throw_on_side_effect);
     81   if (!maybe_result.is_null()) context_builder.UpdateValues();
     82   return maybe_result;
     83 }
     84 
     85 
     86 // Compile and evaluate source for the given context.
     87 MaybeHandle<Object> DebugEvaluate::Evaluate(
     88     Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
     89     Handle<Context> context, Handle<Object> receiver, Handle<String> source,
     90     bool throw_on_side_effect) {
     91   Handle<JSFunction> eval_fun;
     92   ASSIGN_RETURN_ON_EXCEPTION(
     93       isolate, eval_fun,
     94       Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY,
     95                                     NO_PARSE_RESTRICTION, kNoSourcePosition,
     96                                     kNoSourcePosition, kNoSourcePosition),
     97       Object);
     98 
     99   Handle<Object> result;
    100   {
    101     NoSideEffectScope no_side_effect(isolate, throw_on_side_effect);
    102     ASSIGN_RETURN_ON_EXCEPTION(
    103         isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL),
    104         Object);
    105   }
    106 
    107   // Skip the global proxy as it has no properties and always delegates to the
    108   // real global object.
    109   if (result->IsJSGlobalProxy()) {
    110     PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result));
    111     // TODO(verwaest): This will crash when the global proxy is detached.
    112     result = PrototypeIterator::GetCurrent<JSObject>(iter);
    113   }
    114 
    115   return result;
    116 }
    117 
    118 
    119 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
    120                                               JavaScriptFrame* frame,
    121                                               int inlined_jsframe_index)
    122     : isolate_(isolate),
    123       frame_(frame),
    124       inlined_jsframe_index_(inlined_jsframe_index) {
    125   FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
    126   Handle<JSFunction> local_function = frame_inspector.GetFunction();
    127   Handle<Context> outer_context(local_function->context());
    128   evaluation_context_ = outer_context;
    129   outer_info_ = handle(local_function->shared());
    130   Factory* factory = isolate->factory();
    131 
    132   // To evaluate as if we were running eval at the point of the debug break,
    133   // we reconstruct the context chain as follows:
    134   //  - To make stack-allocated variables visible, we materialize them and
    135   //    use a debug-evaluate context to wrap both the materialized object and
    136   //    the original context.
    137   //  - We use the original context chain from the function context to the
    138   //    native context.
    139   //  - Between the function scope and the native context, we only resolve
    140   //    variable names that the current function already uses. Only for these
    141   //    names we can be sure that they will be correctly resolved. For the
    142   //    rest, we only resolve to with, script, and native contexts. We use a
    143   //    whitelist to implement that.
    144   // Context::Lookup has special handling for debug-evaluate contexts:
    145   //  - Look up in the materialized stack variables.
    146   //  - Look up in the original context.
    147   //  - Check the whitelist to find out whether to skip contexts during lookup.
    148   const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
    149   for (ScopeIterator it(isolate, &frame_inspector, option); !it.Done();
    150        it.Next()) {
    151     ScopeIterator::ScopeType scope_type = it.Type();
    152     if (scope_type == ScopeIterator::ScopeTypeLocal) {
    153       DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
    154       Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
    155       Handle<Context> local_context =
    156           it.HasContext() ? it.CurrentContext() : outer_context;
    157       Handle<StringSet> non_locals = it.GetNonLocals();
    158       MaterializeReceiver(materialized, local_context, local_function,
    159                           non_locals);
    160       frame_inspector.MaterializeStackLocals(materialized, local_function);
    161       MaterializeArgumentsObject(materialized, local_function);
    162       ContextChainElement context_chain_element;
    163       context_chain_element.scope_info = it.CurrentScopeInfo();
    164       context_chain_element.materialized_object = materialized;
    165       // Non-locals that are already being referenced by the current function
    166       // are guaranteed to be correctly resolved.
    167       context_chain_element.whitelist = non_locals;
    168       if (it.HasContext()) {
    169         context_chain_element.wrapped_context = it.CurrentContext();
    170       }
    171       context_chain_.Add(context_chain_element);
    172       evaluation_context_ = outer_context;
    173       break;
    174     } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
    175                scope_type == ScopeIterator::ScopeTypeWith) {
    176       ContextChainElement context_chain_element;
    177       Handle<Context> current_context = it.CurrentContext();
    178       if (!current_context->IsDebugEvaluateContext()) {
    179         context_chain_element.wrapped_context = current_context;
    180       }
    181       context_chain_.Add(context_chain_element);
    182     } else if (scope_type == ScopeIterator::ScopeTypeBlock ||
    183                scope_type == ScopeIterator::ScopeTypeEval) {
    184       Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
    185       frame_inspector.MaterializeStackLocals(materialized,
    186                                              it.CurrentScopeInfo());
    187       ContextChainElement context_chain_element;
    188       context_chain_element.scope_info = it.CurrentScopeInfo();
    189       context_chain_element.materialized_object = materialized;
    190       if (it.HasContext()) {
    191         context_chain_element.wrapped_context = it.CurrentContext();
    192       }
    193       context_chain_.Add(context_chain_element);
    194     } else {
    195       break;
    196     }
    197   }
    198 
    199   for (int i = context_chain_.length() - 1; i >= 0; i--) {
    200     Handle<ScopeInfo> scope_info(ScopeInfo::CreateForWithScope(
    201         isolate, evaluation_context_->IsNativeContext()
    202                      ? Handle<ScopeInfo>::null()
    203                      : Handle<ScopeInfo>(evaluation_context_->scope_info())));
    204     scope_info->SetIsDebugEvaluateScope();
    205     evaluation_context_ = factory->NewDebugEvaluateContext(
    206         evaluation_context_, scope_info, context_chain_[i].materialized_object,
    207         context_chain_[i].wrapped_context, context_chain_[i].whitelist);
    208   }
    209 }
    210 
    211 
    212 void DebugEvaluate::ContextBuilder::UpdateValues() {
    213   // TODO(yangguo): remove updating values.
    214   for (int i = 0; i < context_chain_.length(); i++) {
    215     ContextChainElement element = context_chain_[i];
    216     if (!element.materialized_object.is_null()) {
    217       // Write back potential changes to materialized stack locals to the stack.
    218       FrameInspector(frame_, inlined_jsframe_index_, isolate_)
    219           .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
    220                                                    element.scope_info);
    221     }
    222   }
    223 }
    224 
    225 
    226 void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
    227     Handle<JSObject> target, Handle<JSFunction> function) {
    228   // Do not materialize the arguments object for eval or top-level code.
    229   // Skip if "arguments" is already taken.
    230   if (function->shared()->is_toplevel()) return;
    231   Maybe<bool> maybe = JSReceiver::HasOwnProperty(
    232       target, isolate_->factory()->arguments_string());
    233   DCHECK(maybe.IsJust());
    234   if (maybe.FromJust()) return;
    235 
    236   // FunctionGetArguments can't throw an exception.
    237   Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
    238   Handle<String> arguments_str = isolate_->factory()->arguments_string();
    239   JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
    240                                            NONE)
    241       .Check();
    242 }
    243 
    244 void DebugEvaluate::ContextBuilder::MaterializeReceiver(
    245     Handle<JSObject> target, Handle<Context> local_context,
    246     Handle<JSFunction> local_function, Handle<StringSet> non_locals) {
    247   Handle<Object> recv = isolate_->factory()->undefined_value();
    248   Handle<String> name = isolate_->factory()->this_string();
    249   if (non_locals->Has(name)) {
    250     // 'this' is allocated in an outer context and is is already being
    251     // referenced by the current function, so it can be correctly resolved.
    252     return;
    253   } else if (local_function->shared()->scope_info()->HasReceiver() &&
    254              !frame_->receiver()->IsTheHole(isolate_)) {
    255     recv = handle(frame_->receiver(), isolate_);
    256   }
    257   JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check();
    258 }
    259 
    260 namespace {
    261 
    262 bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
    263   switch (id) {
    264     // Whitelist for intrinsics amd runtime functions.
    265     // Conversions.
    266     case Runtime::kToInteger:
    267     case Runtime::kInlineToInteger:
    268     case Runtime::kToObject:
    269     case Runtime::kInlineToObject:
    270     case Runtime::kToString:
    271     case Runtime::kInlineToString:
    272     case Runtime::kToLength:
    273     case Runtime::kInlineToLength:
    274     case Runtime::kToNumber:
    275     // Type checks.
    276     case Runtime::kIsJSReceiver:
    277     case Runtime::kInlineIsJSReceiver:
    278     case Runtime::kIsSmi:
    279     case Runtime::kInlineIsSmi:
    280     case Runtime::kIsArray:
    281     case Runtime::kInlineIsArray:
    282     case Runtime::kIsFunction:
    283     case Runtime::kIsDate:
    284     case Runtime::kIsJSProxy:
    285     case Runtime::kIsRegExp:
    286     case Runtime::kIsTypedArray:
    287     // Loads.
    288     case Runtime::kLoadLookupSlotForCall:
    289     // Arrays.
    290     case Runtime::kArraySpeciesConstructor:
    291     case Runtime::kNormalizeElements:
    292     case Runtime::kGetArrayKeys:
    293     case Runtime::kHasComplexElements:
    294     case Runtime::kEstimateNumberOfElements:
    295     // Errors.
    296     case Runtime::kReThrow:
    297     case Runtime::kThrowReferenceError:
    298     case Runtime::kThrowSymbolIteratorInvalid:
    299     case Runtime::kThrowIteratorResultNotAnObject:
    300     case Runtime::kNewTypeError:
    301     // Strings.
    302     case Runtime::kInlineStringCharCodeAt:
    303     case Runtime::kStringCharCodeAt:
    304     case Runtime::kStringIndexOf:
    305     case Runtime::kStringReplaceOneCharWithString:
    306     case Runtime::kSubString:
    307     case Runtime::kInlineSubString:
    308     case Runtime::kRegExpInternalReplace:
    309     // Literals.
    310     case Runtime::kCreateArrayLiteral:
    311     case Runtime::kCreateObjectLiteral:
    312     case Runtime::kCreateRegExpLiteral:
    313     // Misc.
    314     case Runtime::kForInPrepare:
    315     case Runtime::kInlineCall:
    316     case Runtime::kCall:
    317     case Runtime::kInlineMaxSmi:
    318     case Runtime::kMaxSmi:
    319       return true;
    320     default:
    321       if (FLAG_trace_side_effect_free_debug_evaluate) {
    322         PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n",
    323                Runtime::FunctionForId(id)->name);
    324       }
    325       return false;
    326   }
    327 }
    328 
    329 bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
    330   typedef interpreter::Bytecode Bytecode;
    331   typedef interpreter::Bytecodes Bytecodes;
    332   if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true;
    333   if (Bytecodes::IsCallOrConstruct(bytecode)) return true;
    334   if (Bytecodes::WritesBooleanToAccumulator(bytecode)) return true;
    335   if (Bytecodes::IsJumpIfToBoolean(bytecode)) return true;
    336   if (Bytecodes::IsPrefixScalingBytecode(bytecode)) return true;
    337   switch (bytecode) {
    338     // Whitelist for bytecodes.
    339     // Loads.
    340     case Bytecode::kLdaLookupSlot:
    341     case Bytecode::kLdaGlobal:
    342     case Bytecode::kLdaNamedProperty:
    343     case Bytecode::kLdaKeyedProperty:
    344     // Arithmetics.
    345     case Bytecode::kAdd:
    346     case Bytecode::kAddSmi:
    347     case Bytecode::kSub:
    348     case Bytecode::kSubSmi:
    349     case Bytecode::kMul:
    350     case Bytecode::kDiv:
    351     case Bytecode::kMod:
    352     case Bytecode::kBitwiseAnd:
    353     case Bytecode::kBitwiseAndSmi:
    354     case Bytecode::kBitwiseOr:
    355     case Bytecode::kBitwiseOrSmi:
    356     case Bytecode::kBitwiseXor:
    357     case Bytecode::kShiftLeft:
    358     case Bytecode::kShiftLeftSmi:
    359     case Bytecode::kShiftRight:
    360     case Bytecode::kShiftRightSmi:
    361     case Bytecode::kShiftRightLogical:
    362     case Bytecode::kInc:
    363     case Bytecode::kDec:
    364     case Bytecode::kLogicalNot:
    365     case Bytecode::kToBooleanLogicalNot:
    366     case Bytecode::kTypeOf:
    367     // Contexts.
    368     case Bytecode::kCreateBlockContext:
    369     case Bytecode::kCreateCatchContext:
    370     case Bytecode::kCreateFunctionContext:
    371     case Bytecode::kCreateEvalContext:
    372     case Bytecode::kCreateWithContext:
    373     // Literals.
    374     case Bytecode::kCreateArrayLiteral:
    375     case Bytecode::kCreateObjectLiteral:
    376     case Bytecode::kCreateRegExpLiteral:
    377     // Allocations.
    378     case Bytecode::kCreateClosure:
    379     case Bytecode::kCreateUnmappedArguments:
    380     // Conversions.
    381     case Bytecode::kToObject:
    382     case Bytecode::kToNumber:
    383     // Misc.
    384     case Bytecode::kForInPrepare:
    385     case Bytecode::kForInContinue:
    386     case Bytecode::kForInNext:
    387     case Bytecode::kForInStep:
    388     case Bytecode::kThrow:
    389     case Bytecode::kReThrow:
    390     case Bytecode::kIllegal:
    391     case Bytecode::kCallJSRuntime:
    392     case Bytecode::kStackCheck:
    393     case Bytecode::kReturn:
    394     case Bytecode::kSetPendingMessage:
    395       return true;
    396     default:
    397       if (FLAG_trace_side_effect_free_debug_evaluate) {
    398         PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
    399                Bytecodes::ToString(bytecode));
    400       }
    401       return false;
    402   }
    403 }
    404 
    405 bool BuiltinHasNoSideEffect(Builtins::Name id) {
    406   switch (id) {
    407     // Whitelist for builtins.
    408     // Array builtins.
    409     case Builtins::kArrayCode:
    410     case Builtins::kArrayIndexOf:
    411     case Builtins::kArrayPrototypeValues:
    412     case Builtins::kArrayIncludes:
    413     case Builtins::kArrayPrototypeEntries:
    414     case Builtins::kArrayPrototypeKeys:
    415     case Builtins::kArrayForEach:
    416     // Math builtins.
    417     case Builtins::kMathAbs:
    418     case Builtins::kMathAcos:
    419     case Builtins::kMathAcosh:
    420     case Builtins::kMathAsin:
    421     case Builtins::kMathAsinh:
    422     case Builtins::kMathAtan:
    423     case Builtins::kMathAtanh:
    424     case Builtins::kMathAtan2:
    425     case Builtins::kMathCeil:
    426     case Builtins::kMathCbrt:
    427     case Builtins::kMathExpm1:
    428     case Builtins::kMathClz32:
    429     case Builtins::kMathCos:
    430     case Builtins::kMathCosh:
    431     case Builtins::kMathExp:
    432     case Builtins::kMathFloor:
    433     case Builtins::kMathFround:
    434     case Builtins::kMathHypot:
    435     case Builtins::kMathImul:
    436     case Builtins::kMathLog:
    437     case Builtins::kMathLog1p:
    438     case Builtins::kMathLog2:
    439     case Builtins::kMathLog10:
    440     case Builtins::kMathMax:
    441     case Builtins::kMathMin:
    442     case Builtins::kMathPow:
    443     case Builtins::kMathRandom:
    444     case Builtins::kMathRound:
    445     case Builtins::kMathSign:
    446     case Builtins::kMathSin:
    447     case Builtins::kMathSinh:
    448     case Builtins::kMathSqrt:
    449     case Builtins::kMathTan:
    450     case Builtins::kMathTanh:
    451     case Builtins::kMathTrunc:
    452     // Number builtins.
    453     case Builtins::kNumberConstructor:
    454     case Builtins::kNumberIsFinite:
    455     case Builtins::kNumberIsInteger:
    456     case Builtins::kNumberIsNaN:
    457     case Builtins::kNumberIsSafeInteger:
    458     case Builtins::kNumberParseFloat:
    459     case Builtins::kNumberParseInt:
    460     case Builtins::kNumberPrototypeToExponential:
    461     case Builtins::kNumberPrototypeToFixed:
    462     case Builtins::kNumberPrototypeToPrecision:
    463     case Builtins::kNumberPrototypeToString:
    464     case Builtins::kNumberPrototypeValueOf:
    465     // String builtins. Strings are immutable.
    466     case Builtins::kStringFromCharCode:
    467     case Builtins::kStringFromCodePoint:
    468     case Builtins::kStringConstructor:
    469     case Builtins::kStringPrototypeCharAt:
    470     case Builtins::kStringPrototypeCharCodeAt:
    471     case Builtins::kStringPrototypeEndsWith:
    472     case Builtins::kStringPrototypeIncludes:
    473     case Builtins::kStringPrototypeIndexOf:
    474     case Builtins::kStringPrototypeLastIndexOf:
    475     case Builtins::kStringPrototypeStartsWith:
    476     case Builtins::kStringPrototypeSubstr:
    477     case Builtins::kStringPrototypeSubstring:
    478     case Builtins::kStringPrototypeToString:
    479     case Builtins::kStringPrototypeToLowerCase:
    480     case Builtins::kStringPrototypeToUpperCase:
    481     case Builtins::kStringPrototypeTrim:
    482     case Builtins::kStringPrototypeTrimLeft:
    483     case Builtins::kStringPrototypeTrimRight:
    484     case Builtins::kStringPrototypeValueOf:
    485     // JSON builtins.
    486     case Builtins::kJsonParse:
    487     case Builtins::kJsonStringify:
    488     // Error builtins.
    489     case Builtins::kMakeError:
    490     case Builtins::kMakeTypeError:
    491     case Builtins::kMakeSyntaxError:
    492     case Builtins::kMakeRangeError:
    493     case Builtins::kMakeURIError:
    494       return true;
    495     default:
    496       if (FLAG_trace_side_effect_free_debug_evaluate) {
    497         PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
    498                Builtins::name(id));
    499       }
    500       return false;
    501   }
    502 }
    503 
    504 static const Address accessors_with_no_side_effect[] = {
    505     // Whitelist for accessors.
    506     FUNCTION_ADDR(Accessors::StringLengthGetter),
    507     FUNCTION_ADDR(Accessors::ArrayLengthGetter)};
    508 
    509 }  // anonymous namespace
    510 
    511 // static
    512 bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) {
    513   if (FLAG_trace_side_effect_free_debug_evaluate) {
    514     PrintF("[debug-evaluate] Checking function %s for side effect.\n",
    515            info->DebugName()->ToCString().get());
    516   }
    517 
    518   DCHECK(info->is_compiled());
    519 
    520   if (info->HasBytecodeArray()) {
    521     // Check bytecodes against whitelist.
    522     Handle<BytecodeArray> bytecode_array(info->bytecode_array());
    523     if (FLAG_trace_side_effect_free_debug_evaluate) bytecode_array->Print();
    524     for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
    525          it.Advance()) {
    526       interpreter::Bytecode bytecode = it.current_bytecode();
    527 
    528       if (interpreter::Bytecodes::IsCallRuntime(bytecode)) {
    529         Runtime::FunctionId id =
    530             (bytecode == interpreter::Bytecode::kInvokeIntrinsic)
    531                 ? it.GetIntrinsicIdOperand(0)
    532                 : it.GetRuntimeIdOperand(0);
    533         if (IntrinsicHasNoSideEffect(id)) continue;
    534         return false;
    535       }
    536 
    537       if (BytecodeHasNoSideEffect(bytecode)) continue;
    538 
    539       // Did not match whitelist.
    540       return false;
    541     }
    542     return true;
    543   } else {
    544     // Check built-ins against whitelist.
    545     int builtin_index = info->code()->builtin_index();
    546     if (builtin_index >= 0 && builtin_index < Builtins::builtin_count &&
    547         BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) {
    548       return true;
    549     }
    550   }
    551 
    552   return false;
    553 }
    554 
    555 // static
    556 bool DebugEvaluate::CallbackHasNoSideEffect(Address function_addr) {
    557   for (size_t i = 0; i < arraysize(accessors_with_no_side_effect); i++) {
    558     if (function_addr == accessors_with_no_side_effect[i]) return true;
    559   }
    560 
    561   if (FLAG_trace_side_effect_free_debug_evaluate) {
    562     PrintF("[debug-evaluate] API Callback at %p may cause side effect.\n",
    563            reinterpret_cast<void*>(function_addr));
    564   }
    565   return false;
    566 }
    567 
    568 }  // namespace internal
    569 }  // namespace v8
    570