Index: test/cctest/interpreter/bytecode-expectations.cc |
diff --git a/test/cctest/interpreter/bytecode-expectations.cc b/test/cctest/interpreter/bytecode-expectations.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0fcff5761b0b6d983887b23ed8a9dd994d0372da |
--- /dev/null |
+++ b/test/cctest/interpreter/bytecode-expectations.cc |
@@ -0,0 +1,276 @@ |
+// Copyright 2016 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 "test/cctest/interpreter/bytecode-expectations.h" |
+ |
+#include <iostream> |
+ |
+#include "include/libplatform/libplatform.h" |
+#include "include/v8.h" |
+ |
+#include "src/base/logging.h" |
+#include "src/base/smart-pointers.h" |
+#include "src/compiler.h" |
+ |
+#include "src/interpreter/bytecode-array-iterator.h" |
+#include "src/interpreter/bytecode-generator.h" |
+#include "src/interpreter/bytecodes.h" |
+#include "src/interpreter/interpreter.h" |
+ |
+using namespace v8::internal::interpreter; |
+ |
+namespace { |
+ |
+i::Isolate* GetInternalIsolate(v8::Isolate* isolate) { |
rmcilroy
2016/02/12 15:29:05
Move this to generate-bytecode-expectations.cc and
Stefano Sanfilippo
2016/02/12 18:29:40
Turns out we need the external Isolate as well, fo
rmcilroy
2016/02/15 11:15:19
What you've done here is fine (modulo the rename o
|
+ return reinterpret_cast<i::Isolate*>(isolate); |
+} |
+ |
+v8::Local<v8::String> V8StringFromUTF8(v8::Isolate* isolate, const char* data) { |
+ return v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kNormal) |
+ .ToLocalChecked(); |
+} |
+ |
+std::string WrapCodeInFunction(const char* function_name, |
+ const std::string& function_body) { |
+ std::ostringstream program_stream; |
+ program_stream << "function " << function_name << "() {" << function_body |
+ << "}\n" |
+ << function_name << "();"; |
+ |
+ return program_stream.str(); |
+} |
+ |
+v8::Local<v8::Value> CompileAndRun(v8::Isolate* isolate, const char* program) { |
+ v8::Local<v8::String> source = V8StringFromUTF8(isolate, program); |
+ v8::Local<v8::Script> script = |
+ v8::Script::Compile(isolate->GetCurrentContext(), source) |
+ .ToLocalChecked(); |
+ |
+ v8::Local<v8::Value> result; |
+ CHECK(script->Run(isolate->GetCurrentContext()).ToLocal(&result)); |
+ |
+ return result; |
+} |
+ |
+i::Handle<v8::internal::BytecodeArray> GetBytecodeArrayForGlobal( |
+ v8::Isolate* isolate, const char* global_name) { |
+ const v8::Local<v8::Context>& context = isolate->GetCurrentContext(); |
+ v8::Local<v8::String> v8_global_name = V8StringFromUTF8(isolate, global_name); |
+ v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast( |
+ context->Global()->Get(context, v8_global_name).ToLocalChecked()); |
+ i::Handle<i::JSFunction> js_function = |
+ i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function)); |
+ |
+ i::Handle<i::BytecodeArray> bytecodes = i::handle( |
+ js_function->shared()->bytecode_array(), GetInternalIsolate(isolate)); |
+ |
+ return bytecodes; |
+} |
+ |
+void PrintEscapedString(std::ostream& stream, const std::string& string) { |
+ for (char c : string) { |
+ switch (c) { |
+ case '"': |
+ stream << "\\\""; |
+ break; |
+ case '\\': |
+ stream << "\\\\"; |
+ break; |
+ default: |
+ stream << c; |
+ break; |
+ } |
+ } |
+} |
+ |
+void PrintBytecodeOperand(std::ostream& stream, |
rmcilroy
2016/02/12 15:29:05
Functions from here onwards should be private func
Stefano Sanfilippo
2016/02/12 18:29:40
Done.
|
+ const BytecodeArrayIterator& bytecode_iter, |
+ const Bytecode& bytecode, int op_index) { |
+ OperandType op_type = Bytecodes::GetOperandType(bytecode, op_index); |
+ OperandSize op_size = Bytecodes::GetOperandSize(bytecode, op_index); |
+ |
+ const char* size_tag; |
+ switch (op_size) { |
+ case OperandSize::kByte: |
+ size_tag = "8"; |
+ break; |
+ case OperandSize::kShort: |
+ size_tag = "16"; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ return; |
+ } |
+ |
+ if (Bytecodes::IsRegisterOperandType(op_type)) { |
+ Register register_value = bytecode_iter.GetRegisterOperand(op_index); |
+ stream << 'R'; |
+ if (op_size != OperandSize::kByte) stream << size_tag; |
+ stream << '(' << register_value.index() << ')'; |
+ } else { |
+ stream << 'U' << size_tag << '('; |
+ |
+ if (Bytecodes::IsImmediateOperandType(op_type)) { |
+ // We need a cast, otherwise the result is printed as char. |
+ stream << static_cast<int>(bytecode_iter.GetImmediateOperand(op_index)); |
+ } else if (Bytecodes::IsRegisterCountOperandType(op_type)) { |
+ stream << bytecode_iter.GetRegisterCountOperand(op_index); |
+ } else if (Bytecodes::IsIndexOperandType(op_type)) { |
+ stream << bytecode_iter.GetIndexOperand(op_index); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ |
+ stream << ')'; |
+ } |
+} |
+ |
+void PrintBytecode(std::ostream& stream, |
+ const BytecodeArrayIterator& bytecode_iter) { |
+ Bytecode bytecode = bytecode_iter.current_bytecode(); |
+ |
+ stream << "B(" << Bytecodes::ToString(bytecode) << ')'; |
+ |
+ int operands_count = Bytecodes::NumberOfOperands(bytecode); |
+ for (int op_index = 0; op_index < operands_count; ++op_index) { |
+ stream << ", "; |
+ PrintBytecodeOperand(stream, bytecode_iter, bytecode, op_index); |
+ } |
+} |
+ |
+void PrintV8String(std::ostream& stream, i::String* string) { |
+ stream << '"'; |
+ for (int i = 0, length = string->length(); i < length; ++i) { |
+ stream << i::AsEscapedUC16ForJSON(string->Get(i)); |
+ } |
+ stream << '"'; |
+} |
+ |
+void PrintConstant(std::ostream& stream, |
+ ConstantPoolType expected_constant_type, |
+ i::Handle<i::Object> constant) { |
+ switch (expected_constant_type) { |
+ case kConstantPoolTypeString: |
+ CHECK(constant->IsString()); |
+ PrintV8String(stream, i::String::cast(*constant)); |
+ break; |
+ case kConstantPoolTypeInteger: |
+ if (constant->IsSmi()) { |
+ i::Smi::cast(*constant)->SmiPrint(stream); |
+ } else if (constant->IsHeapNumber()) { |
+ i::HeapNumber::cast(*constant)->HeapNumberPrint(stream); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ break; |
+ case kConstantPoolTypeDouble: |
+ i::HeapNumber::cast(*constant)->HeapNumberPrint(stream); |
+ break; |
+ case kConstantPoolTypeMixed: |
+ if (constant->IsSmi()) { |
+ stream << "kInstanceTypeDontCare"; |
+ } else { |
+ stream << "InstanceType::" |
+ << i::HeapObject::cast(*constant)->map()->instance_type(); |
+ } |
+ break; |
+ case kConstantPoolTypeUnknown: |
+ default: |
+ UNREACHABLE(); |
+ return; |
+ } |
+} |
+ |
+void PrintFrameSize(std::ostream& stream, |
+ i::Handle<i::BytecodeArray> bytecode_array) { |
+ const int kPointerSize = sizeof(void*); |
+ int frame_size = bytecode_array->frame_size(); |
+ |
+ DCHECK_EQ(frame_size % kPointerSize, 0); |
+ stream << "frame size: " << frame_size / kPointerSize; |
+ if (frame_size > 0) stream << " # in multiples of sizeof(void*)"; |
+ stream << "\nparameter count: " << bytecode_array->parameter_count() << '\n'; |
+} |
+ |
+void PrintBytecodeSequence(std::ostream& stream, |
+ i::Handle<i::BytecodeArray> bytecode_array) { |
+ stream << "bytecodes: [\n"; |
+ BytecodeArrayIterator bytecode_iter{bytecode_array}; |
+ for (; !bytecode_iter.done(); bytecode_iter.Advance()) { |
+ stream << " "; |
+ PrintBytecode(stream, bytecode_iter); |
+ stream << ",\n"; |
+ } |
+ stream << "]\n"; |
+} |
+ |
+void PrintConstantPool(std::ostream& stream, i::FixedArray* constant_pool, |
+ ConstantPoolType const_pool_type, v8::Isolate* isolate) { |
+ stream << "constant pool: [\n"; |
+ int num_constants = constant_pool->length(); |
+ if (num_constants > 0) { |
+ for (int i = 0; i < num_constants; ++i) { |
+ stream << " "; |
+ PrintConstant( |
+ stream, const_pool_type, |
+ i::FixedArray::get(constant_pool, i, GetInternalIsolate(isolate))); |
+ stream << ",\n"; |
+ } |
+ } |
+ stream << "]\n"; |
+} |
+ |
+void PrintCodeSnippet(std::ostream& stream, const std::string& body) { |
+ stream << "snippet: \"\n"; |
+ std::stringstream body_stream{body}; |
+ std::string body_line; |
+ while (std::getline(body_stream, body_line)) { |
+ stream << " "; |
+ PrintEscapedString(stream, body_line); |
+ stream << '\n'; |
+ } |
+ stream << "\"\n"; |
+} |
+ |
+void PrintBytecodeArray(std::ostream& stream, |
+ i::Handle<i::BytecodeArray> bytecode_array, |
+ const std::string& body, v8::Isolate* isolate, |
+ ConstantPoolType constant_pool_type) { |
+ stream << "---\n"; |
+ PrintCodeSnippet(stream, body); |
+ PrintFrameSize(stream, bytecode_array); |
+ PrintBytecodeSequence(stream, bytecode_array); |
+ PrintConstantPool(stream, bytecode_array->constant_pool(), constant_pool_type, |
+ isolate); |
+ |
+ // TODO(ssanfilippo) print handlers. |
+ i::HandlerTable* handlers = |
+ i::HandlerTable::cast(bytecode_array->handler_table()); |
+ CHECK_EQ(handlers->NumberOfRangeEntries(), 0); |
+} |
+ |
+} // namespace |
+ |
+namespace v8 { |
+namespace internal { |
+namespace interpreter { |
+ |
+void ExpectationPrinter::PrintExpectation(std::ostream& stream, |
+ const std::string& snippet) { |
+ const char* wrapper_function_name = "__genbckexp_wrapper__"; |
+ |
+ std::string source_code = WrapCodeInFunction(wrapper_function_name, snippet); |
+ CompileAndRun(isolate_, source_code.c_str()); |
+ |
+ i::Handle<i::BytecodeArray> bytecode_array = |
+ GetBytecodeArrayForGlobal(isolate_, wrapper_function_name); |
+ |
+ PrintBytecodeArray(stream, bytecode_array, snippet, isolate_, |
+ const_pool_type_); |
+ stream << '\n'; |
+} |
+ |
+} // namespace interpreter |
+} // namespace internal |
+} // namespace v8 |