// 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. #ifndef V8_TEST_UNITTESTS_INTERPRETER_INTERPRETER_TESTER_H_ #define V8_TEST_UNITTESTS_INTERPRETER_INTERPRETER_TESTER_H_ #include "include/v8-function.h" #include "src/api/api.h" #include "src/execution/execution.h" #include "src/handles/handles.h" #include "src/init/v8.h" #include "src/interpreter/bytecode-array-builder.h" #include "src/interpreter/interpreter.h" #include "src/objects/feedback-cell.h" #include "test/unittests/test-utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace v8 { namespace internal { namespace interpreter { template static MaybeHandle CallInterpreter(Isolate* isolate, Handle function, Handle receiver, A... args) { // Pad the array with an empty handle to ensure that argv size is at least 1. // It avoids MSVC error C2466. Handle argv[] = {args..., Handle()}; return Execution::Call(isolate, function, receiver, sizeof...(args), argv); } template class InterpreterCallable { public: virtual ~InterpreterCallable() = default; FeedbackVector vector() const { return function_->feedback_vector(); } protected: InterpreterCallable(Isolate* isolate, Handle function) : isolate_(isolate), function_(function) {} Isolate* isolate_; Handle function_; }; template class InterpreterCallableUndefinedReceiver : public InterpreterCallable { public: InterpreterCallableUndefinedReceiver(Isolate* isolate, Handle function) : InterpreterCallable(isolate, function) {} MaybeHandle operator()(A... args) { return CallInterpreter(this->isolate_, this->function_, this->isolate_->factory()->undefined_value(), args...); } }; template class InterpreterCallableWithReceiver : public InterpreterCallable { public: InterpreterCallableWithReceiver(Isolate* isolate, Handle function) : InterpreterCallable(isolate, function) {} MaybeHandle operator()(Handle receiver, A... args) { return CallInterpreter(this->isolate_, this->function_, receiver, args...); } }; static inline v8::Local CompileRun(const char* source) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Local context = isolate->GetCurrentContext(); v8::Local script = v8::Script::Compile( context, v8::String::NewFromUtf8(isolate, source).ToLocalChecked()) .ToLocalChecked(); return script->Run(context).ToLocalChecked(); } class InterpreterTester { public: InterpreterTester(Isolate* isolate, const char* source, MaybeHandle bytecode, MaybeHandle feedback_metadata, const char* filter); InterpreterTester(Isolate* isolate, Handle bytecode, MaybeHandle feedback_metadata = MaybeHandle(), const char* filter = kFunctionName); InterpreterTester(Isolate* isolate, const char* source, const char* filter = kFunctionName); virtual ~InterpreterTester(); InterpreterTester(const InterpreterTester&) = delete; InterpreterTester& operator=(const InterpreterTester&) = delete; template InterpreterCallableUndefinedReceiver GetCallable() { return InterpreterCallableUndefinedReceiver( isolate_, GetBytecodeFunction()); } template InterpreterCallableWithReceiver GetCallableWithReceiver() { return InterpreterCallableWithReceiver(isolate_, GetBytecodeFunction()); } Local CheckThrowsReturnMessage(); static Handle NewObject(const char* script); static Handle GetName(Isolate* isolate, const char* name); static std::string SourceForBody(const char* body); static std::string function_name(); static const char kFunctionName[]; // Expose raw RegisterList construction to tests. static RegisterList NewRegisterList(int first_reg_index, int register_count) { return RegisterList(first_reg_index, register_count); } inline bool HasFeedbackMetadata() { return !feedback_metadata_.is_null(); } private: Isolate* isolate_; const char* source_; MaybeHandle bytecode_; MaybeHandle feedback_metadata_; template Handle GetBytecodeFunction() { Handle function; IsCompiledScope is_compiled_scope; v8::Isolate* isolate = reinterpret_cast(isolate_); if (source_) { CompileRun(source_); v8::Local context = isolate->GetCurrentContext(); Local api_function = Local::Cast( context->Global() ->Get(context, v8::String::NewFromUtf8(isolate, kFunctionName) .ToLocalChecked()) .ToLocalChecked()); function = Handle::cast(v8::Utils::OpenHandle(*api_function)); is_compiled_scope = function->shared().is_compiled_scope(isolate_); } else { int arg_count = sizeof...(A); std::string source("(function " + function_name() + "("); for (int i = 0; i < arg_count; i++) { source += i == 0 ? "a" : ", a"; } source += "){})"; function = Handle::cast(v8::Utils::OpenHandle( *v8::Local::Cast(CompileRun(source.c_str())))); function->set_code(*BUILTIN_CODE(isolate_, InterpreterEntryTrampoline)); is_compiled_scope = function->shared().is_compiled_scope(isolate_); } if (!bytecode_.is_null()) { function->shared().set_function_data(*bytecode_.ToHandleChecked(), kReleaseStore); is_compiled_scope = function->shared().is_compiled_scope(isolate_); } if (HasFeedbackMetadata()) { function->set_raw_feedback_cell(isolate_->heap()->many_closures_cell()); // Set the raw feedback metadata to circumvent checks that we are not // overwriting existing metadata. function->shared().set_raw_outer_scope_info_or_feedback_metadata( *feedback_metadata_.ToHandleChecked()); JSFunction::EnsureFeedbackVector(isolate_, function, &is_compiled_scope); } return function; } }; } // namespace interpreter } // namespace internal } // namespace v8 #endif // V8_TEST_UNITTESTS_INTERPRETER_INTERPRETER_TESTER_H_