// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/debug/debug-evaluate.h"

#include "src/accessors.h"
#include "src/assembler-inl.h"
#include "src/compiler.h"
#include "src/contexts.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#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"
#include "src/objects/api-callbacks.h"
#include "src/snapshot/snapshot.h"

namespace v8 {
namespace internal {

MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
                                          Handle<String> source,
                                          bool throw_on_side_effect) {
  // Disable breaks in side-effect free mode.
  DisableBreak disable_break_scope(isolate->debug(), throw_on_side_effect);

  Handle<Context> context = isolate->native_context();
  ScriptOriginOptions origin_options(false, true);
  MaybeHandle<SharedFunctionInfo> maybe_function_info =
      Compiler::GetSharedFunctionInfoForScript(
          isolate, source,
          Compiler::ScriptDetails(isolate->factory()->empty_string()),
          origin_options, nullptr, nullptr, ScriptCompiler::kNoCompileOptions,
          ScriptCompiler::kNoCacheNoReason, NOT_NATIVES_CODE);

  Handle<SharedFunctionInfo> shared_info;
  if (!maybe_function_info.ToHandle(&shared_info)) return MaybeHandle<Object>();

  Handle<JSFunction> fun =
      isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info,
                                                            context);
  if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
  MaybeHandle<Object> result = Execution::Call(
      isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0,
      nullptr);
  if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
  return result;
}

MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
                                         StackFrame::Id frame_id,
                                         int inlined_jsframe_index,
                                         Handle<String> source,
                                         bool throw_on_side_effect) {
  // Handle the processing of break.
  DisableBreak disable_break_scope(isolate->debug());

  // Get the frame where the debugging is performed.
  StackTraceFrameIterator it(isolate, frame_id);
  if (!it.is_javascript()) return isolate->factory()->undefined_value();
  JavaScriptFrame* frame = it.javascript_frame();

  // This is not a lot different than DebugEvaluate::Global, except that
  // variables accessible by the function we are evaluating from are
  // materialized and included on top of the native context. Changes to
  // the materialized object are written back afterwards.
  // Note that the native context is taken from the original context chain,
  // which may not be the current native context of the isolate.
  ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
  if (isolate->has_pending_exception()) return MaybeHandle<Object>();

  Handle<Context> context = context_builder.evaluation_context();
  Handle<JSObject> receiver(context->global_proxy(), isolate);
  MaybeHandle<Object> maybe_result =
      Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
               throw_on_side_effect);
  if (!maybe_result.is_null()) context_builder.UpdateValues();
  return maybe_result;
}

MaybeHandle<Object> DebugEvaluate::WithTopmostArguments(Isolate* isolate,
                                                        Handle<String> source) {
  // Handle the processing of break.
  DisableBreak disable_break_scope(isolate->debug());
  Factory* factory = isolate->factory();
  JavaScriptFrameIterator it(isolate);

  // Get context and receiver.
  Handle<Context> native_context(
      Context::cast(it.frame()->context())->native_context(), isolate);

  // Materialize arguments as property on an extension object.
  Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
  Handle<String> arguments_str = factory->arguments_string();
  JSObject::SetOwnPropertyIgnoreAttributes(
      materialized, arguments_str,
      Accessors::FunctionGetArguments(it.frame(), 0), NONE)
      .Check();

  // Materialize receiver.
  Handle<String> this_str = factory->this_string();
  JSObject::SetOwnPropertyIgnoreAttributes(
      materialized, this_str, Handle<Object>(it.frame()->receiver(), isolate),
      NONE)
      .Check();

  // Use extension object in a debug-evaluate scope.
  Handle<ScopeInfo> scope_info =
      ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
  scope_info->SetIsDebugEvaluateScope();
  Handle<Context> evaluation_context =
      factory->NewDebugEvaluateContext(native_context, scope_info, materialized,
                                       Handle<Context>(), Handle<StringSet>());
  Handle<SharedFunctionInfo> outer_info(
      native_context->empty_function()->shared(), isolate);
  Handle<JSObject> receiver(native_context->global_proxy(), isolate);
  const bool throw_on_side_effect = false;
  MaybeHandle<Object> maybe_result =
      Evaluate(isolate, outer_info, evaluation_context, receiver, source,
               throw_on_side_effect);
  return maybe_result;
}

// Compile and evaluate source for the given context.
MaybeHandle<Object> DebugEvaluate::Evaluate(
    Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
    Handle<Context> context, Handle<Object> receiver, Handle<String> source,
    bool throw_on_side_effect) {
  Handle<JSFunction> eval_fun;
  ASSIGN_RETURN_ON_EXCEPTION(
      isolate, eval_fun,
      Compiler::GetFunctionFromEval(source, outer_info, context,
                                    LanguageMode::kSloppy, NO_PARSE_RESTRICTION,
                                    kNoSourcePosition, kNoSourcePosition,
                                    kNoSourcePosition),
      Object);

  Handle<Object> result;
  bool sucess = false;
  if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
  sucess = Execution::Call(isolate, eval_fun, receiver, 0, nullptr)
               .ToHandle(&result);
  if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
  if (!sucess) {
    DCHECK(isolate->has_pending_exception());
    return MaybeHandle<Object>();
  }

  // Skip the global proxy as it has no properties and always delegates to the
  // real global object.
  if (result->IsJSGlobalProxy()) {
    PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result));
    // TODO(verwaest): This will crash when the global proxy is detached.
    result = PrototypeIterator::GetCurrent<JSObject>(iter);
  }

  return result;
}

Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const {
  return handle(frame_inspector_.GetFunction()->shared(), isolate_);
}

DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
                                              JavaScriptFrame* frame,
                                              int inlined_jsframe_index)
    : isolate_(isolate),
      frame_inspector_(frame, inlined_jsframe_index, isolate),
      scope_iterator_(isolate, &frame_inspector_,
                      ScopeIterator::COLLECT_NON_LOCALS) {
  Handle<Context> outer_context(frame_inspector_.GetFunction()->context(),
                                isolate);
  evaluation_context_ = outer_context;
  Factory* factory = isolate->factory();

  if (scope_iterator_.Done()) return;

  // To evaluate as if we were running eval at the point of the debug break,
  // we reconstruct the context chain as follows:
  //  - To make stack-allocated variables visible, we materialize them and
  //    use a debug-evaluate context to wrap both the materialized object and
  //    the original context.
  //  - We use the original context chain from the function context to the
  //    native context.
  //  - Between the function scope and the native context, we only resolve
  //    variable names that the current function already uses. Only for these
  //    names we can be sure that they will be correctly resolved. For the
  //    rest, we only resolve to with, script, and native contexts. We use a
  //    whitelist to implement that.
  // Context::Lookup has special handling for debug-evaluate contexts:
  //  - Look up in the materialized stack variables.
  //  - Look up in the original context.
  //  - Check the whitelist to find out whether to skip contexts during lookup.
  for (; scope_iterator_.InInnerScope(); scope_iterator_.Next()) {
    ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
    if (scope_type == ScopeIterator::ScopeTypeScript) break;
    ContextChainElement context_chain_element;
    if (scope_type == ScopeIterator::ScopeTypeLocal ||
        scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK)) {
      context_chain_element.materialized_object =
          scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
    }
    if (scope_iterator_.HasContext()) {
      context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
    }
    if (scope_type == ScopeIterator::ScopeTypeLocal) {
      context_chain_element.whitelist = scope_iterator_.GetNonLocals();
    }
    context_chain_.push_back(context_chain_element);
  }

  Handle<ScopeInfo> scope_info =
      evaluation_context_->IsNativeContext()
          ? Handle<ScopeInfo>::null()
          : handle(evaluation_context_->scope_info(), isolate);
  for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
       rit++) {
    ContextChainElement element = *rit;
    scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
    scope_info->SetIsDebugEvaluateScope();
    evaluation_context_ = factory->NewDebugEvaluateContext(
        evaluation_context_, scope_info, element.materialized_object,
        element.wrapped_context, element.whitelist);
  }
}


void DebugEvaluate::ContextBuilder::UpdateValues() {
  scope_iterator_.Restart();
  for (ContextChainElement& element : context_chain_) {
    if (!element.materialized_object.is_null()) {
      Handle<FixedArray> keys =
          KeyAccumulator::GetKeys(element.materialized_object,
                                  KeyCollectionMode::kOwnOnly,
                                  ENUMERABLE_STRINGS)
              .ToHandleChecked();

      for (int i = 0; i < keys->length(); i++) {
        DCHECK(keys->get(i)->IsString());
        Handle<String> key(String::cast(keys->get(i)), isolate_);
        Handle<Object> value =
            JSReceiver::GetDataProperty(element.materialized_object, key);
        scope_iterator_.SetVariableValue(key, value);
      }
    }
    scope_iterator_.Next();
  }
}

namespace {

bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
// Use macro to include both inlined and non-inlined version of an intrinsic.
#define INTRINSIC_WHITELIST(V)                \
  /* Conversions */                           \
  V(NumberToString)                           \
  V(ToBigInt)                                 \
  V(ToInteger)                                \
  V(ToLength)                                 \
  V(ToNumber)                                 \
  V(ToObject)                                 \
  V(ToString)                                 \
  /* Type checks */                           \
  V(IsArray)                                  \
  V(IsDate)                                   \
  V(IsFunction)                               \
  V(IsJSProxy)                                \
  V(IsJSReceiver)                             \
  V(IsRegExp)                                 \
  V(IsSmi)                                    \
  V(IsTypedArray)                             \
  /* Loads */                                 \
  V(LoadLookupSlotForCall)                    \
  V(GetProperty)                              \
  /* Arrays */                                \
  V(ArraySpeciesConstructor)                  \
  V(EstimateNumberOfElements)                 \
  V(GetArrayKeys)                             \
  V(HasComplexElements)                       \
  V(HasFastPackedElements)                    \
  V(NewArray)                                 \
  V(NormalizeElements)                        \
  V(PrepareElementsForSort)                   \
  V(TrySliceSimpleNonFastElements)            \
  V(TypedArrayGetBuffer)                      \
  /* Errors */                                \
  V(NewTypeError)                             \
  V(ReThrow)                                  \
  V(ThrowCalledNonCallable)                   \
  V(ThrowInvalidStringLength)                 \
  V(ThrowIteratorResultNotAnObject)           \
  V(ThrowReferenceError)                      \
  V(ThrowSymbolIteratorInvalid)               \
  /* Strings */                               \
  V(RegExpInternalReplace)                    \
  V(StringIncludes)                           \
  V(StringIndexOf)                            \
  V(StringReplaceOneCharWithString)           \
  V(StringSubstring)                          \
  V(StringToNumber)                           \
  V(StringTrim)                               \
  /* BigInts */                               \
  V(BigIntEqualToBigInt)                      \
  V(BigIntToBoolean)                          \
  V(BigIntToNumber)                           \
  /* Literals */                              \
  V(CreateArrayLiteral)                       \
  V(CreateArrayLiteralWithoutAllocationSite)  \
  V(CreateObjectLiteral)                      \
  V(CreateObjectLiteralWithoutAllocationSite) \
  V(CreateRegExpLiteral)                      \
  /* Called from builtins */                  \
  V(AllocateInNewSpace)                       \
  V(AllocateInTargetSpace)                    \
  V(AllocateSeqOneByteString)                 \
  V(AllocateSeqTwoByteString)                 \
  V(ArrayIncludes_Slow)                       \
  V(ArrayIndexOf)                             \
  V(ArrayIsArray)                             \
  V(ClassOf)                                  \
  V(GenerateRandomNumbers)                    \
  V(GetFunctionName)                          \
  V(GetOwnPropertyDescriptor)                 \
  V(GlobalPrint)                              \
  V(HasProperty)                              \
  V(ObjectCreate)                             \
  V(ObjectEntries)                            \
  V(ObjectEntriesSkipFastPath)                \
  V(ObjectHasOwnProperty)                     \
  V(ObjectValues)                             \
  V(ObjectValuesSkipFastPath)                 \
  V(ObjectGetOwnPropertyNames)                \
  V(ObjectGetOwnPropertyNamesTryFast)         \
  V(RegExpInitializeAndCompile)               \
  V(StackGuard)                               \
  V(StringAdd)                                \
  V(StringCharCodeAt)                         \
  V(StringEqual)                              \
  V(StringIndexOfUnchecked)                   \
  V(StringParseFloat)                         \
  V(StringParseInt)                           \
  V(SymbolDescriptiveString)                  \
  V(ThrowRangeError)                          \
  V(ThrowTypeError)                           \
  V(ToName)                                   \
  V(TransitionElementsKind)                   \
  /* Misc. */                                 \
  V(Call)                                     \
  V(CompleteInobjectSlackTrackingForMap)      \
  V(HasInPrototypeChain)                      \
  V(MaxSmi)                                   \
  V(NewObject)                                \
  V(SmiLexicographicCompare)                  \
  V(StringMaxLength)                          \
  V(StringToArray)                            \
  /* Test */                                  \
  V(GetOptimizationStatus)                    \
  V(OptimizeFunctionOnNextCall)               \
  V(OptimizeOsr)                              \
  V(UnblockConcurrentRecompilation)

#define CASE(Name)       \
  case Runtime::k##Name: \
  case Runtime::kInline##Name:

  switch (id) {
    INTRINSIC_WHITELIST(CASE)
    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;
  }

#undef CASE
#undef INTRINSIC_WHITELIST
}

#ifdef DEBUG
bool BuiltinToIntrinsicHasNoSideEffect(Builtins::Name builtin_id,
                                       Runtime::FunctionId intrinsic_id) {
  // First check the intrinsic whitelist.
  if (IntrinsicHasNoSideEffect(intrinsic_id)) return true;

// Whitelist intrinsics called from specific builtins.
#define BUILTIN_INTRINSIC_WHITELIST(V, W)                                 \
  /* Arrays */                                                            \
  V(Builtins::kArrayFilter, W(CreateDataProperty))                        \
  V(Builtins::kArrayMap, W(CreateDataProperty))                           \
  V(Builtins::kArrayPrototypeSlice, W(CreateDataProperty) W(SetProperty)) \
  /* TypedArrays */                                                       \
  V(Builtins::kTypedArrayConstructor,                                     \
    W(TypedArrayCopyElements) W(ThrowInvalidTypedArrayAlignment))         \
  V(Builtins::kTypedArrayPrototypeFilter, W(TypedArrayCopyElements))      \
  V(Builtins::kTypedArrayPrototypeMap, W(SetProperty))

#define CASE(Builtin, ...) \
  case Builtin:            \
    return (__VA_ARGS__ false);

#define MATCH(Intrinsic)                   \
  intrinsic_id == Runtime::k##Intrinsic || \
      intrinsic_id == Runtime::kInline##Intrinsic ||

  switch (builtin_id) {
    BUILTIN_INTRINSIC_WHITELIST(CASE, MATCH)
    default:
      return false;
  }

#undef MATCH
#undef CASE
#undef BUILTIN_INTRINSIC_WHITELIST
}
#endif  // DEBUG

bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
  typedef interpreter::Bytecode Bytecode;
  typedef interpreter::Bytecodes Bytecodes;
  if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true;
  if (Bytecodes::IsCallOrConstruct(bytecode)) return true;
  if (Bytecodes::IsJumpIfToBoolean(bytecode)) return true;
  if (Bytecodes::IsPrefixScalingBytecode(bytecode)) return true;
  switch (bytecode) {
    // Whitelist for bytecodes.
    // Loads.
    case Bytecode::kLdaLookupSlot:
    case Bytecode::kLdaGlobal:
    case Bytecode::kLdaNamedProperty:
    case Bytecode::kLdaKeyedProperty:
    case Bytecode::kLdaGlobalInsideTypeof:
    case Bytecode::kLdaLookupSlotInsideTypeof:
    // Arithmetics.
    case Bytecode::kAdd:
    case Bytecode::kAddSmi:
    case Bytecode::kSub:
    case Bytecode::kSubSmi:
    case Bytecode::kMul:
    case Bytecode::kMulSmi:
    case Bytecode::kDiv:
    case Bytecode::kDivSmi:
    case Bytecode::kMod:
    case Bytecode::kModSmi:
    case Bytecode::kExp:
    case Bytecode::kExpSmi:
    case Bytecode::kNegate:
    case Bytecode::kBitwiseAnd:
    case Bytecode::kBitwiseAndSmi:
    case Bytecode::kBitwiseNot:
    case Bytecode::kBitwiseOr:
    case Bytecode::kBitwiseOrSmi:
    case Bytecode::kBitwiseXor:
    case Bytecode::kBitwiseXorSmi:
    case Bytecode::kShiftLeft:
    case Bytecode::kShiftLeftSmi:
    case Bytecode::kShiftRight:
    case Bytecode::kShiftRightSmi:
    case Bytecode::kShiftRightLogical:
    case Bytecode::kShiftRightLogicalSmi:
    case Bytecode::kInc:
    case Bytecode::kDec:
    case Bytecode::kLogicalNot:
    case Bytecode::kToBooleanLogicalNot:
    case Bytecode::kTypeOf:
    // Contexts.
    case Bytecode::kCreateBlockContext:
    case Bytecode::kCreateCatchContext:
    case Bytecode::kCreateFunctionContext:
    case Bytecode::kCreateEvalContext:
    case Bytecode::kCreateWithContext:
    // Literals.
    case Bytecode::kCreateArrayLiteral:
    case Bytecode::kCreateEmptyArrayLiteral:
    case Bytecode::kCreateObjectLiteral:
    case Bytecode::kCreateEmptyObjectLiteral:
    case Bytecode::kCreateRegExpLiteral:
    // Allocations.
    case Bytecode::kCreateClosure:
    case Bytecode::kCreateUnmappedArguments:
    case Bytecode::kCreateRestParameter:
    // Comparisons.
    case Bytecode::kTestEqual:
    case Bytecode::kTestEqualStrict:
    case Bytecode::kTestLessThan:
    case Bytecode::kTestLessThanOrEqual:
    case Bytecode::kTestGreaterThan:
    case Bytecode::kTestGreaterThanOrEqual:
    case Bytecode::kTestInstanceOf:
    case Bytecode::kTestIn:
    case Bytecode::kTestReferenceEqual:
    case Bytecode::kTestUndetectable:
    case Bytecode::kTestTypeOf:
    case Bytecode::kTestUndefined:
    case Bytecode::kTestNull:
    // Conversions.
    case Bytecode::kToObject:
    case Bytecode::kToName:
    case Bytecode::kToNumber:
    case Bytecode::kToNumeric:
    case Bytecode::kToString:
    // Misc.
    case Bytecode::kForInEnumerate:
    case Bytecode::kForInPrepare:
    case Bytecode::kForInContinue:
    case Bytecode::kForInNext:
    case Bytecode::kForInStep:
    case Bytecode::kThrow:
    case Bytecode::kReThrow:
    case Bytecode::kThrowReferenceErrorIfHole:
    case Bytecode::kThrowSuperNotCalledIfHole:
    case Bytecode::kThrowSuperAlreadyCalledIfNotHole:
    case Bytecode::kIllegal:
    case Bytecode::kCallJSRuntime:
    case Bytecode::kStackCheck:
    case Bytecode::kReturn:
    case Bytecode::kSetPendingMessage:
      return true;
    default:
      return false;
  }
}

DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
  switch (id) {
    // Whitelist for builtins.
    // Object builtins.
    case Builtins::kObjectConstructor:
    case Builtins::kObjectCreate:
    case Builtins::kObjectEntries:
    case Builtins::kObjectGetOwnPropertyDescriptor:
    case Builtins::kObjectGetOwnPropertyDescriptors:
    case Builtins::kObjectGetOwnPropertyNames:
    case Builtins::kObjectGetOwnPropertySymbols:
    case Builtins::kObjectGetPrototypeOf:
    case Builtins::kObjectIs:
    case Builtins::kObjectIsExtensible:
    case Builtins::kObjectIsFrozen:
    case Builtins::kObjectIsSealed:
    case Builtins::kObjectPrototypeValueOf:
    case Builtins::kObjectValues:
    case Builtins::kObjectPrototypeHasOwnProperty:
    case Builtins::kObjectPrototypeIsPrototypeOf:
    case Builtins::kObjectPrototypePropertyIsEnumerable:
    case Builtins::kObjectPrototypeToString:
    // Array builtins.
    case Builtins::kArrayIsArray:
    case Builtins::kArrayConstructor:
    case Builtins::kArrayIndexOf:
    case Builtins::kArrayPrototypeValues:
    case Builtins::kArrayIncludes:
    case Builtins::kArrayPrototypeEntries:
    case Builtins::kArrayPrototypeFill:
    case Builtins::kArrayPrototypeFind:
    case Builtins::kArrayPrototypeFindIndex:
    case Builtins::kArrayPrototypeFlat:
    case Builtins::kArrayPrototypeFlatMap:
    case Builtins::kArrayPrototypeKeys:
    case Builtins::kArrayPrototypeSlice:
    case Builtins::kArrayPrototypeSort:
    case Builtins::kArrayForEach:
    case Builtins::kArrayEvery:
    case Builtins::kArraySome:
    case Builtins::kArrayConcat:
    case Builtins::kArrayFilter:
    case Builtins::kArrayMap:
    case Builtins::kArrayReduce:
    case Builtins::kArrayReduceRight:
    // Trace builtins.
    case Builtins::kIsTraceCategoryEnabled:
    case Builtins::kTrace:
    // TypedArray builtins.
    case Builtins::kTypedArrayConstructor:
    case Builtins::kTypedArrayPrototypeBuffer:
    case Builtins::kTypedArrayPrototypeByteLength:
    case Builtins::kTypedArrayPrototypeByteOffset:
    case Builtins::kTypedArrayPrototypeLength:
    case Builtins::kTypedArrayPrototypeEntries:
    case Builtins::kTypedArrayPrototypeKeys:
    case Builtins::kTypedArrayPrototypeValues:
    case Builtins::kTypedArrayPrototypeFind:
    case Builtins::kTypedArrayPrototypeFindIndex:
    case Builtins::kTypedArrayPrototypeIncludes:
    case Builtins::kTypedArrayPrototypeIndexOf:
    case Builtins::kTypedArrayPrototypeLastIndexOf:
    case Builtins::kTypedArrayPrototypeSlice:
    case Builtins::kTypedArrayPrototypeSubArray:
    case Builtins::kTypedArrayPrototypeEvery:
    case Builtins::kTypedArrayPrototypeSome:
    case Builtins::kTypedArrayPrototypeFilter:
    case Builtins::kTypedArrayPrototypeMap:
    case Builtins::kTypedArrayPrototypeReduce:
    case Builtins::kTypedArrayPrototypeReduceRight:
    case Builtins::kTypedArrayPrototypeForEach:
    // ArrayBuffer builtins.
    case Builtins::kArrayBufferConstructor:
    case Builtins::kArrayBufferPrototypeGetByteLength:
    case Builtins::kArrayBufferIsView:
    case Builtins::kArrayBufferPrototypeSlice:
    case Builtins::kReturnReceiver:
    // DataView builtins.
    case Builtins::kDataViewConstructor:
    case Builtins::kDataViewPrototypeGetBuffer:
    case Builtins::kDataViewPrototypeGetByteLength:
    case Builtins::kDataViewPrototypeGetByteOffset:
    case Builtins::kDataViewPrototypeGetInt8:
    case Builtins::kDataViewPrototypeGetUint8:
    case Builtins::kDataViewPrototypeGetInt16:
    case Builtins::kDataViewPrototypeGetUint16:
    case Builtins::kDataViewPrototypeGetInt32:
    case Builtins::kDataViewPrototypeGetUint32:
    case Builtins::kDataViewPrototypeGetFloat32:
    case Builtins::kDataViewPrototypeGetFloat64:
    case Builtins::kDataViewPrototypeGetBigInt64:
    case Builtins::kDataViewPrototypeGetBigUint64:
    // Boolean bulitins.
    case Builtins::kBooleanConstructor:
    case Builtins::kBooleanPrototypeToString:
    case Builtins::kBooleanPrototypeValueOf:
    // Date builtins.
    case Builtins::kDateConstructor:
    case Builtins::kDateNow:
    case Builtins::kDateParse:
    case Builtins::kDatePrototypeGetDate:
    case Builtins::kDatePrototypeGetDay:
    case Builtins::kDatePrototypeGetFullYear:
    case Builtins::kDatePrototypeGetHours:
    case Builtins::kDatePrototypeGetMilliseconds:
    case Builtins::kDatePrototypeGetMinutes:
    case Builtins::kDatePrototypeGetMonth:
    case Builtins::kDatePrototypeGetSeconds:
    case Builtins::kDatePrototypeGetTime:
    case Builtins::kDatePrototypeGetTimezoneOffset:
    case Builtins::kDatePrototypeGetUTCDate:
    case Builtins::kDatePrototypeGetUTCDay:
    case Builtins::kDatePrototypeGetUTCFullYear:
    case Builtins::kDatePrototypeGetUTCHours:
    case Builtins::kDatePrototypeGetUTCMilliseconds:
    case Builtins::kDatePrototypeGetUTCMinutes:
    case Builtins::kDatePrototypeGetUTCMonth:
    case Builtins::kDatePrototypeGetUTCSeconds:
    case Builtins::kDatePrototypeGetYear:
    case Builtins::kDatePrototypeToDateString:
    case Builtins::kDatePrototypeToISOString:
    case Builtins::kDatePrototypeToUTCString:
    case Builtins::kDatePrototypeToString:
    case Builtins::kDatePrototypeToTimeString:
    case Builtins::kDatePrototypeToJson:
    case Builtins::kDatePrototypeToPrimitive:
    case Builtins::kDatePrototypeValueOf:
    // Map builtins.
    case Builtins::kMapConstructor:
    case Builtins::kMapPrototypeForEach:
    case Builtins::kMapPrototypeGet:
    case Builtins::kMapPrototypeHas:
    case Builtins::kMapPrototypeEntries:
    case Builtins::kMapPrototypeGetSize:
    case Builtins::kMapPrototypeKeys:
    case Builtins::kMapPrototypeValues:
    // WeakMap builtins.
    case Builtins::kWeakMapConstructor:
    case Builtins::kWeakMapGet:
    case Builtins::kWeakMapHas:
    // Math builtins.
    case Builtins::kMathAbs:
    case Builtins::kMathAcos:
    case Builtins::kMathAcosh:
    case Builtins::kMathAsin:
    case Builtins::kMathAsinh:
    case Builtins::kMathAtan:
    case Builtins::kMathAtanh:
    case Builtins::kMathAtan2:
    case Builtins::kMathCeil:
    case Builtins::kMathCbrt:
    case Builtins::kMathExpm1:
    case Builtins::kMathClz32:
    case Builtins::kMathCos:
    case Builtins::kMathCosh:
    case Builtins::kMathExp:
    case Builtins::kMathFloor:
    case Builtins::kMathFround:
    case Builtins::kMathHypot:
    case Builtins::kMathImul:
    case Builtins::kMathLog:
    case Builtins::kMathLog1p:
    case Builtins::kMathLog2:
    case Builtins::kMathLog10:
    case Builtins::kMathMax:
    case Builtins::kMathMin:
    case Builtins::kMathPow:
    case Builtins::kMathRandom:
    case Builtins::kMathRound:
    case Builtins::kMathSign:
    case Builtins::kMathSin:
    case Builtins::kMathSinh:
    case Builtins::kMathSqrt:
    case Builtins::kMathTan:
    case Builtins::kMathTanh:
    case Builtins::kMathTrunc:
    // Number builtins.
    case Builtins::kNumberConstructor:
    case Builtins::kNumberIsFinite:
    case Builtins::kNumberIsInteger:
    case Builtins::kNumberIsNaN:
    case Builtins::kNumberIsSafeInteger:
    case Builtins::kNumberParseFloat:
    case Builtins::kNumberParseInt:
    case Builtins::kNumberPrototypeToExponential:
    case Builtins::kNumberPrototypeToFixed:
    case Builtins::kNumberPrototypeToPrecision:
    case Builtins::kNumberPrototypeToString:
    case Builtins::kNumberPrototypeValueOf:
    // BigInt builtins.
    case Builtins::kBigIntConstructor:
    case Builtins::kBigIntAsIntN:
    case Builtins::kBigIntAsUintN:
    case Builtins::kBigIntPrototypeToString:
    case Builtins::kBigIntPrototypeValueOf:
    // Set builtins.
    case Builtins::kSetConstructor:
    case Builtins::kSetPrototypeEntries:
    case Builtins::kSetPrototypeForEach:
    case Builtins::kSetPrototypeGetSize:
    case Builtins::kSetPrototypeHas:
    case Builtins::kSetPrototypeValues:
    // WeakSet builtins.
    case Builtins::kWeakSetConstructor:
    case Builtins::kWeakSetHas:
    // String builtins. Strings are immutable.
    case Builtins::kStringFromCharCode:
    case Builtins::kStringFromCodePoint:
    case Builtins::kStringConstructor:
    case Builtins::kStringPrototypeAnchor:
    case Builtins::kStringPrototypeBig:
    case Builtins::kStringPrototypeBlink:
    case Builtins::kStringPrototypeBold:
    case Builtins::kStringPrototypeCharAt:
    case Builtins::kStringPrototypeCharCodeAt:
    case Builtins::kStringPrototypeCodePointAt:
    case Builtins::kStringPrototypeConcat:
    case Builtins::kStringPrototypeEndsWith:
    case Builtins::kStringPrototypeFixed:
    case Builtins::kStringPrototypeFontcolor:
    case Builtins::kStringPrototypeFontsize:
    case Builtins::kStringPrototypeIncludes:
    case Builtins::kStringPrototypeIndexOf:
    case Builtins::kStringPrototypeItalics:
    case Builtins::kStringPrototypeLastIndexOf:
    case Builtins::kStringPrototypeLink:
    case Builtins::kStringPrototypePadEnd:
    case Builtins::kStringPrototypePadStart:
    case Builtins::kStringPrototypeRepeat:
    case Builtins::kStringPrototypeSlice:
    case Builtins::kStringPrototypeSmall:
    case Builtins::kStringPrototypeStartsWith:
    case Builtins::kStringPrototypeStrike:
    case Builtins::kStringPrototypeSub:
    case Builtins::kStringPrototypeSubstr:
    case Builtins::kStringPrototypeSubstring:
    case Builtins::kStringPrototypeSup:
    case Builtins::kStringPrototypeToString:
#ifndef V8_INTL_SUPPORT
    case Builtins::kStringPrototypeToLowerCase:
    case Builtins::kStringPrototypeToUpperCase:
#endif
    case Builtins::kStringPrototypeTrim:
    case Builtins::kStringPrototypeTrimEnd:
    case Builtins::kStringPrototypeTrimStart:
    case Builtins::kStringPrototypeValueOf:
    case Builtins::kStringToNumber:
    case Builtins::kStringSubstring:
    // Symbol builtins.
    case Builtins::kSymbolConstructor:
    case Builtins::kSymbolKeyFor:
    case Builtins::kSymbolPrototypeToString:
    case Builtins::kSymbolPrototypeValueOf:
    case Builtins::kSymbolPrototypeToPrimitive:
    // JSON builtins.
    case Builtins::kJsonParse:
    case Builtins::kJsonStringify:
    // Global function builtins.
    case Builtins::kGlobalDecodeURI:
    case Builtins::kGlobalDecodeURIComponent:
    case Builtins::kGlobalEncodeURI:
    case Builtins::kGlobalEncodeURIComponent:
    case Builtins::kGlobalEscape:
    case Builtins::kGlobalUnescape:
    case Builtins::kGlobalIsFinite:
    case Builtins::kGlobalIsNaN:
    // Function builtins.
    case Builtins::kFunctionPrototypeToString:
    case Builtins::kFunctionPrototypeBind:
    case Builtins::kFastFunctionPrototypeBind:
    case Builtins::kFunctionPrototypeCall:
    case Builtins::kFunctionPrototypeApply:
    // Error builtins.
    case Builtins::kErrorConstructor:
    case Builtins::kMakeError:
    case Builtins::kMakeTypeError:
    case Builtins::kMakeSyntaxError:
    case Builtins::kMakeRangeError:
    case Builtins::kMakeURIError:
    // RegExp builtins.
    case Builtins::kRegExpConstructor:
      return DebugInfo::kHasNoSideEffect;
    // Set builtins.
    case Builtins::kSetIteratorPrototypeNext:
    case Builtins::kSetPrototypeAdd:
    case Builtins::kSetPrototypeClear:
    case Builtins::kSetPrototypeDelete:
    // Array builtins.
    case Builtins::kArrayIteratorPrototypeNext:
    case Builtins::kArrayPrototypePop:
    case Builtins::kArrayPrototypePush:
    case Builtins::kArrayPrototypeReverse:
    case Builtins::kArrayPrototypeShift:
    case Builtins::kArraySplice:
    case Builtins::kArrayUnshift:
    // Map builtins.
    case Builtins::kMapIteratorPrototypeNext:
    case Builtins::kMapPrototypeClear:
    case Builtins::kMapPrototypeDelete:
    case Builtins::kMapPrototypeSet:
    // RegExp builtins.
    case Builtins::kRegExpPrototypeTest:
    case Builtins::kRegExpPrototypeExec:
    case Builtins::kRegExpPrototypeSplit:
    case Builtins::kRegExpPrototypeFlagsGetter:
    case Builtins::kRegExpPrototypeGlobalGetter:
    case Builtins::kRegExpPrototypeIgnoreCaseGetter:
    case Builtins::kRegExpPrototypeMultilineGetter:
    case Builtins::kRegExpPrototypeDotAllGetter:
    case Builtins::kRegExpPrototypeUnicodeGetter:
    case Builtins::kRegExpPrototypeStickyGetter:
      return DebugInfo::kRequiresRuntimeChecks;
    default:
      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
               Builtins::name(id));
      }
      return DebugInfo::kHasSideEffects;
  }
}

bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode) {
  typedef interpreter::Bytecode Bytecode;
  switch (bytecode) {
    case Bytecode::kStaNamedProperty:
    case Bytecode::kStaNamedOwnProperty:
    case Bytecode::kStaKeyedProperty:
    case Bytecode::kStaInArrayLiteral:
    case Bytecode::kStaDataPropertyInLiteral:
    case Bytecode::kStaCurrentContextSlot:
      return true;
    default:
      return false;
  }
}

}  // anonymous namespace

// static
DebugInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
    Isolate* isolate, 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->GetBytecodeArray(), isolate);
    if (FLAG_trace_side_effect_free_debug_evaluate) {
      bytecode_array->Print();
    }
    bool requires_runtime_checks = false;
    for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
         it.Advance()) {
      interpreter::Bytecode bytecode = it.current_bytecode();

      if (interpreter::Bytecodes::IsCallRuntime(bytecode)) {
        Runtime::FunctionId id =
            (bytecode == interpreter::Bytecode::kInvokeIntrinsic)
                ? it.GetIntrinsicIdOperand(0)
                : it.GetRuntimeIdOperand(0);
        if (IntrinsicHasNoSideEffect(id)) continue;
        return DebugInfo::kHasSideEffects;
      }

      if (BytecodeHasNoSideEffect(bytecode)) continue;
      if (BytecodeRequiresRuntimeCheck(bytecode)) {
        requires_runtime_checks = true;
        continue;
      }

      if (FLAG_trace_side_effect_free_debug_evaluate) {
        PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
               interpreter::Bytecodes::ToString(bytecode));
      }

      // Did not match whitelist.
      return DebugInfo::kHasSideEffects;
    }
    return requires_runtime_checks ? DebugInfo::kRequiresRuntimeChecks
                                   : DebugInfo::kHasNoSideEffect;
  } else if (info->IsApiFunction()) {
    if (info->GetCode()->is_builtin()) {
      return info->GetCode()->builtin_index() == Builtins::kHandleApiCall
                 ? DebugInfo::kHasNoSideEffect
                 : DebugInfo::kHasSideEffects;
    }
  } else {
    // Check built-ins against whitelist.
    int builtin_index =
        info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId;
    DCHECK_NE(Builtins::kDeserializeLazy, builtin_index);
    if (!Builtins::IsBuiltinId(builtin_index))
      return DebugInfo::kHasSideEffects;
    DebugInfo::SideEffectState state =
        BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index));
#ifdef DEBUG
    if (state == DebugInfo::kHasNoSideEffect) {
      Code* code = isolate->builtins()->builtin(builtin_index);
      if (code->builtin_index() == Builtins::kDeserializeLazy) {
        // Target builtin is not yet deserialized. Deserialize it now.

        DCHECK(Builtins::IsLazy(builtin_index));
        DCHECK_EQ(Builtins::TFJ, Builtins::KindOf(builtin_index));

        code = Snapshot::DeserializeBuiltin(isolate, builtin_index);
        DCHECK_NE(Builtins::kDeserializeLazy, code->builtin_index());
      }
      // TODO(yangguo): Check builtin-to-builtin calls too.
      int mode = RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE);
      bool failed = false;
      for (RelocIterator it(code, mode); !it.done(); it.next()) {
        RelocInfo* rinfo = it.rinfo();
        Address address = rinfo->target_external_reference();
        const Runtime::Function* function = Runtime::FunctionForEntry(address);
        if (function == nullptr) continue;
        if (!BuiltinToIntrinsicHasNoSideEffect(
                static_cast<Builtins::Name>(builtin_index),
                function->function_id)) {
          PrintF("Whitelisted builtin %s calls non-whitelisted intrinsic %s\n",
                 Builtins::name(builtin_index), function->name);
          failed = true;
        }
        DCHECK(!failed);
      }
    }
#endif  // DEBUG
    return state;
  }

  return DebugInfo::kHasSideEffects;
}

// static
bool DebugEvaluate::CallbackHasNoSideEffect(Object* callback_info) {
  DisallowHeapAllocation no_gc;
  if (callback_info->IsAccessorInfo()) {
    // List of whitelisted internal accessors can be found in accessors.h.
    AccessorInfo* info = AccessorInfo::cast(callback_info);
    if (info->has_no_side_effect()) return true;
    if (FLAG_trace_side_effect_free_debug_evaluate) {
      PrintF("[debug-evaluate] API Callback '");
      info->name()->ShortPrint();
      PrintF("' may cause side effect.\n");
    }
  } else if (callback_info->IsInterceptorInfo()) {
    InterceptorInfo* info = InterceptorInfo::cast(callback_info);
    if (info->has_no_side_effect()) return true;
    if (FLAG_trace_side_effect_free_debug_evaluate) {
      PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
    }
  } else if (callback_info->IsCallHandlerInfo()) {
    CallHandlerInfo* info = CallHandlerInfo::cast(callback_info);
    if (info->IsSideEffectFreeCallHandlerInfo()) return true;
    if (FLAG_trace_side_effect_free_debug_evaluate) {
      PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
    }
  }
  return false;
}

// static
void DebugEvaluate::ApplySideEffectChecks(
    Handle<BytecodeArray> bytecode_array) {
  for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
       it.Advance()) {
    interpreter::Bytecode bytecode = it.current_bytecode();
    if (BytecodeRequiresRuntimeCheck(bytecode)) {
      interpreter::Bytecode debugbreak =
          interpreter::Bytecodes::GetDebugBreak(bytecode);
      bytecode_array->set(it.current_offset(),
                          interpreter::Bytecodes::ToByte(debugbreak));
    }
  }
}

}  // namespace internal
}  // namespace v8
