|  | // 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/builtins/accessors.h" | 
|  | #include "src/codegen/assembler-inl.h" | 
|  | #include "src/codegen/compiler.h" | 
|  | #include "src/common/globals.h" | 
|  | #include "src/debug/debug-frames.h" | 
|  | #include "src/debug/debug-scopes.h" | 
|  | #include "src/debug/debug.h" | 
|  | #include "src/execution/frames-inl.h" | 
|  | #include "src/execution/isolate-inl.h" | 
|  | #include "src/interpreter/bytecode-array-iterator.h" | 
|  | #include "src/interpreter/bytecodes.h" | 
|  | #include "src/objects/contexts.h" | 
|  | #include "src/snapshot/snapshot.h" | 
|  | #include "src/wasm/wasm-debug.h" | 
|  | #include "src/wasm/wasm-js.h" | 
|  |  | 
|  | namespace v8 { | 
|  | namespace internal { | 
|  |  | 
|  | namespace { | 
|  | static MaybeHandle<SharedFunctionInfo> GetFunctionInfo(Isolate* isolate, | 
|  | Handle<String> source, | 
|  | REPLMode repl_mode) { | 
|  | Compiler::ScriptDetails script_details(isolate->factory()->empty_string()); | 
|  | script_details.repl_mode = repl_mode; | 
|  | ScriptOriginOptions origin_options(false, true); | 
|  | return Compiler::GetSharedFunctionInfoForScript( | 
|  | isolate, source, script_details, origin_options, nullptr, nullptr, | 
|  | ScriptCompiler::kNoCompileOptions, ScriptCompiler::kNoCacheNoReason, | 
|  | NOT_NATIVES_CODE); | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, | 
|  | Handle<String> source, | 
|  | debug::EvaluateGlobalMode mode, | 
|  | REPLMode repl_mode) { | 
|  | // Disable breaks in side-effect free mode. | 
|  | DisableBreak disable_break_scope( | 
|  | isolate->debug(), | 
|  | mode == debug::EvaluateGlobalMode::kDisableBreaks || | 
|  | mode == | 
|  | debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect); | 
|  |  | 
|  | Handle<SharedFunctionInfo> shared_info; | 
|  | if (!GetFunctionInfo(isolate, source, repl_mode).ToHandle(&shared_info)) { | 
|  | return MaybeHandle<Object>(); | 
|  | } | 
|  |  | 
|  | Handle<Context> context = isolate->native_context(); | 
|  | Handle<JSFunction> fun = | 
|  | isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info, | 
|  | context); | 
|  | if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) { | 
|  | isolate->debug()->StartSideEffectCheckMode(); | 
|  | } | 
|  | MaybeHandle<Object> result = Execution::Call( | 
|  | isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0, | 
|  | nullptr); | 
|  | if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) { | 
|  | isolate->debug()->StopSideEffectCheckMode(); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, | 
|  | StackFrameId 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; | 
|  | } | 
|  |  | 
|  | V8_EXPORT MaybeHandle<Object> DebugEvaluate::WebAssembly( | 
|  | Handle<WasmInstanceObject> instance, StackFrameId frame_id, | 
|  | Handle<String> source, bool throw_on_side_effect) { | 
|  | Isolate* isolate = instance->GetIsolate(); | 
|  |  | 
|  | StackTraceFrameIterator it(isolate, frame_id); | 
|  | if (!it.is_wasm()) return isolate->factory()->undefined_value(); | 
|  | WasmFrame* frame = WasmFrame::cast(it.frame()); | 
|  |  | 
|  | Handle<JSProxy> context_extension = WasmJs::GetJSDebugProxy(frame); | 
|  |  | 
|  | DisableBreak disable_break_scope(isolate->debug(), /*disable=*/true); | 
|  |  | 
|  | Handle<SharedFunctionInfo> shared_info; | 
|  | if (!GetFunctionInfo(isolate, source, REPLMode::kNo).ToHandle(&shared_info)) { | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | Handle<ScopeInfo> scope_info = | 
|  | ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null()); | 
|  | Handle<Context> context = isolate->factory()->NewWithContext( | 
|  | isolate->native_context(), scope_info, context_extension); | 
|  |  | 
|  | Handle<Object> result; | 
|  | if (!DebugEvaluate::Evaluate(isolate, shared_info, context, context_extension, | 
|  | source, throw_on_side_effect) | 
|  | .ToHandle(&result)) { | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | return 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<Object> this_value(it.frame()->receiver(), isolate); | 
|  | DCHECK_EQ(it.frame()->IsConstructor(), this_value->IsTheHole(isolate)); | 
|  | if (!this_value->IsTheHole(isolate)) { | 
|  | Handle<String> this_str = factory->this_string(); | 
|  | JSObject::SetOwnPropertyIgnoreAttributes(materialized, this_str, this_value, | 
|  | 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 success = false; | 
|  | if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode(); | 
|  | success = Execution::Call(isolate, eval_fun, receiver, 0, nullptr) | 
|  | .ToHandle(&result); | 
|  | if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode(); | 
|  | if (!success) DCHECK(isolate->has_pending_exception()); | 
|  | return success ? result : MaybeHandle<Object>(); | 
|  | } | 
|  |  | 
|  | 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::ReparseStrategy::kScript) { | 
|  | 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 also wrap all contexts on the chain between the original context | 
|  | //    and the function context. | 
|  | //  - Between the function scope and the native context, we only resolve | 
|  | //    variable names that are guaranteed to not be shadowed by stack-allocated | 
|  | //    variables. Contexts between the function context and the original | 
|  | //    context have a blocklist attached to implement that. | 
|  | // Context::Lookup has special handling for debug-evaluate contexts: | 
|  | //  - Look up in the materialized stack variables. | 
|  | //  - Check the blocklist to find out whether to abort further lookup. | 
|  | //  - Look up in the original context. | 
|  | for (; !scope_iterator_.Done(); scope_iterator_.Next()) { | 
|  | ScopeIterator::ScopeType scope_type = scope_iterator_.Type(); | 
|  | if (scope_type == ScopeIterator::ScopeTypeScript) break; | 
|  | ContextChainElement context_chain_element; | 
|  | if (scope_iterator_.InInnerScope() && | 
|  | (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_iterator_.InInnerScope()) { | 
|  | context_chain_element.blocklist = scope_iterator_.GetLocals(); | 
|  | } | 
|  | 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.blocklist); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 only the non-inlined version of an intrinsic. | 
|  | #define INTRINSIC_ALLOWLIST(V)                \ | 
|  | /* Conversions */                           \ | 
|  | V(NumberToStringSlow)                       \ | 
|  | V(ToBigInt)                                 \ | 
|  | V(ToLength)                                 \ | 
|  | V(ToNumber)                                 \ | 
|  | V(ToObject)                                 \ | 
|  | V(ToString)                                 \ | 
|  | /* Type checks */                           \ | 
|  | V(IsArray)                                  \ | 
|  | V(IsFunction)                               \ | 
|  | V(IsJSProxy)                                \ | 
|  | V(IsJSReceiver)                             \ | 
|  | V(IsRegExp)                                 \ | 
|  | V(IsSmi)                                    \ | 
|  | /* Loads */                                 \ | 
|  | V(LoadLookupSlotForCall)                    \ | 
|  | V(GetProperty)                              \ | 
|  | /* Arrays */                                \ | 
|  | V(ArraySpeciesConstructor)                  \ | 
|  | V(HasFastPackedElements)                    \ | 
|  | V(NewArray)                                 \ | 
|  | V(NormalizeElements)                        \ | 
|  | V(TypedArrayGetBuffer)                      \ | 
|  | /* Errors */                                \ | 
|  | V(NewTypeError)                             \ | 
|  | V(ReThrow)                                  \ | 
|  | V(ThrowCalledNonCallable)                   \ | 
|  | V(ThrowInvalidStringLength)                 \ | 
|  | V(ThrowIteratorError)                       \ | 
|  | V(ThrowIteratorResultNotAnObject)           \ | 
|  | V(ThrowPatternAssignmentNonCoercible)       \ | 
|  | V(ThrowReferenceError)                      \ | 
|  | V(ThrowSymbolIteratorInvalid)               \ | 
|  | /* Strings */                               \ | 
|  | 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(AllocateInYoungGeneration)                \ | 
|  | V(AllocateInOldGeneration)                  \ | 
|  | V(AllocateSeqOneByteString)                 \ | 
|  | V(AllocateSeqTwoByteString)                 \ | 
|  | V(ArrayIncludes_Slow)                       \ | 
|  | V(ArrayIndexOf)                             \ | 
|  | V(ArrayIsArray)                             \ | 
|  | V(GetFunctionName)                          \ | 
|  | V(GetOwnPropertyDescriptor)                 \ | 
|  | V(GlobalPrint)                              \ | 
|  | V(HasProperty)                              \ | 
|  | V(ObjectCreate)                             \ | 
|  | V(ObjectEntries)                            \ | 
|  | V(ObjectEntriesSkipFastPath)                \ | 
|  | V(ObjectHasOwnProperty)                     \ | 
|  | V(ObjectKeys)                               \ | 
|  | V(ObjectValues)                             \ | 
|  | V(ObjectValuesSkipFastPath)                 \ | 
|  | V(ObjectGetOwnPropertyNames)                \ | 
|  | V(ObjectGetOwnPropertyNamesTryFast)         \ | 
|  | V(ObjectIsExtensible)                       \ | 
|  | 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(IncrementUseCounter)                      \ | 
|  | V(MaxSmi)                                   \ | 
|  | V(NewObject)                                \ | 
|  | V(StringMaxLength)                          \ | 
|  | V(StringToArray)                            \ | 
|  | V(AsyncFunctionEnter)                       \ | 
|  | V(AsyncFunctionReject)                      \ | 
|  | V(AsyncFunctionResolve)                     \ | 
|  | /* Test */                                  \ | 
|  | V(GetOptimizationStatus)                    \ | 
|  | V(OptimizeFunctionOnNextCall)               \ | 
|  | V(OptimizeOsr)                              \ | 
|  | V(UnblockConcurrentRecompilation) | 
|  |  | 
|  | // Intrinsics with inline versions have to be allowlisted here a second time. | 
|  | #define INLINE_INTRINSIC_ALLOWLIST(V) \ | 
|  | V(Call)                             \ | 
|  | V(IsJSReceiver)                     \ | 
|  | V(AsyncFunctionEnter)               \ | 
|  | V(AsyncFunctionReject)              \ | 
|  | V(AsyncFunctionResolve) | 
|  |  | 
|  | #define CASE(Name) case Runtime::k##Name: | 
|  | #define INLINE_CASE(Name) case Runtime::kInline##Name: | 
|  | switch (id) { | 
|  | INTRINSIC_ALLOWLIST(CASE) | 
|  | INLINE_INTRINSIC_ALLOWLIST(INLINE_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 INLINE_CASE | 
|  | #undef INTRINSIC_ALLOWLIST | 
|  | #undef INLINE_INTRINSIC_ALLOWLIST | 
|  | } | 
|  |  | 
|  | bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { | 
|  | using interpreter::Bytecode; | 
|  | using interpreter::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) { | 
|  | // Allowlist for bytecodes. | 
|  | // Loads. | 
|  | case Bytecode::kLdaLookupSlot: | 
|  | case Bytecode::kLdaGlobal: | 
|  | case Bytecode::kLdaNamedProperty: | 
|  | case Bytecode::kLdaNamedPropertyNoFeedback: | 
|  | case Bytecode::kLdaKeyedProperty: | 
|  | case Bytecode::kLdaGlobalInsideTypeof: | 
|  | case Bytecode::kLdaLookupSlotInsideTypeof: | 
|  | case Bytecode::kGetIterator: | 
|  | // 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::kCreateArrayFromIterable: | 
|  | 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::kIncBlockCounter:  // Coverage counters. | 
|  | case Bytecode::kForInEnumerate: | 
|  | case Bytecode::kForInPrepare: | 
|  | case Bytecode::kForInContinue: | 
|  | case Bytecode::kForInNext: | 
|  | case Bytecode::kForInStep: | 
|  | case Bytecode::kJumpLoop: | 
|  | case Bytecode::kThrow: | 
|  | case Bytecode::kReThrow: | 
|  | case Bytecode::kThrowReferenceErrorIfHole: | 
|  | case Bytecode::kThrowSuperNotCalledIfHole: | 
|  | case Bytecode::kThrowSuperAlreadyCalledIfNotHole: | 
|  | case Bytecode::kIllegal: | 
|  | case Bytecode::kCallJSRuntime: | 
|  | case Bytecode::kReturn: | 
|  | case Bytecode::kSetPendingMessage: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) { | 
|  | switch (id) { | 
|  | // Allowlist 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::kObjectKeys: | 
|  | case Builtins::kObjectPrototypeValueOf: | 
|  | case Builtins::kObjectValues: | 
|  | case Builtins::kObjectPrototypeHasOwnProperty: | 
|  | case Builtins::kObjectPrototypeIsPrototypeOf: | 
|  | case Builtins::kObjectPrototypePropertyIsEnumerable: | 
|  | case Builtins::kObjectPrototypeToString: | 
|  | case Builtins::kObjectPrototypeToLocaleString: | 
|  | // 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::kArrayPrototypeJoin: | 
|  | case Builtins::kArrayPrototypeKeys: | 
|  | case Builtins::kArrayPrototypeLastIndexOf: | 
|  | case Builtins::kArrayPrototypeSlice: | 
|  | case Builtins::kArrayPrototypeToLocaleString: | 
|  | case Builtins::kArrayPrototypeToString: | 
|  | 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::kTypedArrayPrototypeJoin: | 
|  | case Builtins::kTypedArrayPrototypeIndexOf: | 
|  | case Builtins::kTypedArrayPrototypeLastIndexOf: | 
|  | case Builtins::kTypedArrayPrototypeSlice: | 
|  | case Builtins::kTypedArrayPrototypeSubArray: | 
|  | case Builtins::kTypedArrayPrototypeEvery: | 
|  | case Builtins::kTypedArrayPrototypeSome: | 
|  | case Builtins::kTypedArrayPrototypeToLocaleString: | 
|  | 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: | 
|  | #ifdef V8_INTL_SUPPORT | 
|  | case Builtins::kDatePrototypeToLocaleString: | 
|  | case Builtins::kDatePrototypeToLocaleDateString: | 
|  | case Builtins::kDatePrototypeToLocaleTimeString: | 
|  | #endif | 
|  | 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::kWeakMapPrototypeHas: | 
|  | // 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::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::kNumberPrototypeToLocaleString: | 
|  | 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::kWeakSetPrototypeHas: | 
|  | // 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::kStringPrototypeMatchAll: | 
|  | 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: | 
|  | // RegExp builtins. | 
|  | case Builtins::kRegExpConstructor: | 
|  | // Internal. | 
|  | case Builtins::kStrictPoisonPillThrower: | 
|  | case Builtins::kAllocateInYoungGeneration: | 
|  | case Builtins::kAllocateInOldGeneration: | 
|  | case Builtins::kAllocateRegularInYoungGeneration: | 
|  | case Builtins::kAllocateRegularInOldGeneration: | 
|  | 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::kArrayPrototypeUnshift: | 
|  | case Builtins::kArrayPrototypeSort: | 
|  | case Builtins::kArrayPrototypeSplice: | 
|  | 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::kRegExpPrototypeMatchAll: | 
|  | 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) { | 
|  | using interpreter::Bytecode; | 
|  | switch (bytecode) { | 
|  | case Bytecode::kStaNamedProperty: | 
|  | case Bytecode::kStaNamedPropertyNoFeedback: | 
|  | 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()); | 
|  | DCHECK(!info->needs_script_context()); | 
|  | if (info->HasBytecodeArray()) { | 
|  | // Check bytecodes against allowlist. | 
|  | 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 allowlist. | 
|  | 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 allowlist. | 
|  | int builtin_index = | 
|  | info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId; | 
|  | if (!Builtins::IsBuiltinId(builtin_index)) | 
|  | return DebugInfo::kHasSideEffects; | 
|  | DebugInfo::SideEffectState state = | 
|  | BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index)); | 
|  | return state; | 
|  | } | 
|  |  | 
|  | return DebugInfo::kHasSideEffects; | 
|  | } | 
|  |  | 
|  | #ifdef DEBUG | 
|  | static bool TransitivelyCalledBuiltinHasNoSideEffect(Builtins::Name caller, | 
|  | Builtins::Name callee) { | 
|  | switch (callee) { | 
|  | // Transitively called Builtins: | 
|  | case Builtins::kAbort: | 
|  | case Builtins::kAbortCSAAssert: | 
|  | case Builtins::kAdaptorWithBuiltinExitFrame: | 
|  | case Builtins::kArrayConstructorImpl: | 
|  | case Builtins::kArrayEveryLoopContinuation: | 
|  | case Builtins::kArrayFilterLoopContinuation: | 
|  | case Builtins::kArrayFindIndexLoopContinuation: | 
|  | case Builtins::kArrayFindLoopContinuation: | 
|  | case Builtins::kArrayForEachLoopContinuation: | 
|  | case Builtins::kArrayIncludesHoleyDoubles: | 
|  | case Builtins::kArrayIncludesPackedDoubles: | 
|  | case Builtins::kArrayIncludesSmiOrObject: | 
|  | case Builtins::kArrayIndexOfHoleyDoubles: | 
|  | case Builtins::kArrayIndexOfPackedDoubles: | 
|  | case Builtins::kArrayIndexOfSmiOrObject: | 
|  | case Builtins::kArrayMapLoopContinuation: | 
|  | case Builtins::kArrayReduceLoopContinuation: | 
|  | case Builtins::kArrayReduceRightLoopContinuation: | 
|  | case Builtins::kArraySomeLoopContinuation: | 
|  | case Builtins::kArrayTimSort: | 
|  | case Builtins::kCall_ReceiverIsAny: | 
|  | case Builtins::kCall_ReceiverIsNotNullOrUndefined: | 
|  | case Builtins::kCall_ReceiverIsNullOrUndefined: | 
|  | case Builtins::kCallWithArrayLike: | 
|  | case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: | 
|  | case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit: | 
|  | case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit: | 
|  | case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_NoBuiltinExit: | 
|  | case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_BuiltinExit: | 
|  | case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: | 
|  | case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_BuiltinExit: | 
|  | case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit: | 
|  | case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_NoBuiltinExit: | 
|  | case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_BuiltinExit: | 
|  | case Builtins::kCloneFastJSArray: | 
|  | case Builtins::kConstruct: | 
|  | case Builtins::kConvertToLocaleString: | 
|  | case Builtins::kCreateTypedArray: | 
|  | case Builtins::kDirectCEntry: | 
|  | case Builtins::kDoubleToI: | 
|  | case Builtins::kExtractFastJSArray: | 
|  | case Builtins::kFastNewObject: | 
|  | case Builtins::kFindOrderedHashMapEntry: | 
|  | case Builtins::kFlatMapIntoArray: | 
|  | case Builtins::kFlattenIntoArray: | 
|  | case Builtins::kGetProperty: | 
|  | case Builtins::kHasProperty: | 
|  | case Builtins::kCreateHTML: | 
|  | case Builtins::kNonNumberToNumber: | 
|  | case Builtins::kNonPrimitiveToPrimitive_Number: | 
|  | case Builtins::kNumberToString: | 
|  | case Builtins::kObjectToString: | 
|  | case Builtins::kOrderedHashTableHealIndex: | 
|  | case Builtins::kOrdinaryToPrimitive_Number: | 
|  | case Builtins::kOrdinaryToPrimitive_String: | 
|  | case Builtins::kParseInt: | 
|  | case Builtins::kProxyHasProperty: | 
|  | case Builtins::kProxyIsExtensible: | 
|  | case Builtins::kProxyGetPrototypeOf: | 
|  | case Builtins::kRecordWrite: | 
|  | case Builtins::kStringAdd_CheckNone: | 
|  | case Builtins::kStringEqual: | 
|  | case Builtins::kStringIndexOf: | 
|  | case Builtins::kStringRepeat: | 
|  | case Builtins::kToInteger: | 
|  | case Builtins::kToLength: | 
|  | case Builtins::kToName: | 
|  | case Builtins::kToObject: | 
|  | case Builtins::kToString: | 
|  | case Builtins::kWeakMapLookupHashIndex: | 
|  | return true; | 
|  | case Builtins::kJoinStackPop: | 
|  | case Builtins::kJoinStackPush: | 
|  | switch (caller) { | 
|  | case Builtins::kArrayPrototypeJoin: | 
|  | case Builtins::kArrayPrototypeToLocaleString: | 
|  | case Builtins::kTypedArrayPrototypeJoin: | 
|  | case Builtins::kTypedArrayPrototypeToLocaleString: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | case Builtins::kFastCreateDataProperty: | 
|  | switch (caller) { | 
|  | case Builtins::kArrayPrototypeSlice: | 
|  | case Builtins::kArrayFilter: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | case Builtins::kSetProperty: | 
|  | switch (caller) { | 
|  | case Builtins::kArrayPrototypeSlice: | 
|  | case Builtins::kTypedArrayPrototypeMap: | 
|  | case Builtins::kStringPrototypeMatchAll: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | void DebugEvaluate::VerifyTransitiveBuiltins(Isolate* isolate) { | 
|  | // TODO(yangguo): also check runtime calls. | 
|  | bool failed = false; | 
|  | bool sanity_check = false; | 
|  | for (int i = 0; i < Builtins::builtin_count; i++) { | 
|  | Builtins::Name caller = static_cast<Builtins::Name>(i); | 
|  | DebugInfo::SideEffectState state = BuiltinGetSideEffectState(caller); | 
|  | if (state != DebugInfo::kHasNoSideEffect) continue; | 
|  | Code code = isolate->builtins()->builtin(caller); | 
|  | int mode = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | | 
|  | RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET); | 
|  |  | 
|  | for (RelocIterator it(code, mode); !it.done(); it.next()) { | 
|  | RelocInfo* rinfo = it.rinfo(); | 
|  | DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode())); | 
|  | Code callee_code = isolate->heap()->GcSafeFindCodeForInnerPointer( | 
|  | rinfo->target_address()); | 
|  | if (!callee_code.is_builtin()) continue; | 
|  | Builtins::Name callee = | 
|  | static_cast<Builtins::Name>(callee_code.builtin_index()); | 
|  | if (BuiltinGetSideEffectState(callee) == DebugInfo::kHasNoSideEffect) { | 
|  | continue; | 
|  | } | 
|  | if (TransitivelyCalledBuiltinHasNoSideEffect(caller, callee)) { | 
|  | sanity_check = true; | 
|  | continue; | 
|  | } | 
|  | PrintF("Allowlisted builtin %s calls non-allowlisted builtin %s\n", | 
|  | Builtins::name(caller), Builtins::name(callee)); | 
|  | failed = true; | 
|  | } | 
|  | } | 
|  | CHECK(!failed); | 
|  | #if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \ | 
|  | defined(V8_TARGET_ARCH_MIPS64) | 
|  | // Isolate-independent builtin calls and jumps do not emit reloc infos | 
|  | // on PPC. We try to avoid using PC relative code due to performance | 
|  | // issue with especially older hardwares. | 
|  | // MIPS64 doesn't have PC relative code currently. | 
|  | // TODO(mips): Add PC relative code to MIPS64. | 
|  | USE(sanity_check); | 
|  | #else | 
|  | CHECK(sanity_check); | 
|  | #endif | 
|  | } | 
|  | #endif  // DEBUG | 
|  |  | 
|  | // 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)) it.ApplyDebugBreak(); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace v8 |