// Copyright 2017 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/wasm/wasm-run-utils.h" #include "src/api.h" #include "src/assembler-inl.h" #include "src/wasm/wasm-memory.h" #include "src/wasm/wasm-objects-inl.h" namespace v8 { namespace internal { namespace wasm { TestingModuleBuilder::TestingModuleBuilder( Zone* zone, WasmExecutionMode mode, compiler::RuntimeExceptionSupport exception_support) : test_module_ptr_(&test_module_), isolate_(CcTest::InitIsolateOnce()), global_offset(0), mem_start_(nullptr), mem_size_(0), interpreter_(nullptr), execution_mode_(mode), runtime_exception_support_(exception_support), lower_simd_(mode == kExecuteSimdLowered) { WasmJs::Install(isolate_, true); test_module_.globals_size = kMaxGlobalsSize; memset(globals_data_, 0, sizeof(globals_data_)); instance_object_ = InitInstanceObject(); if (mode == kExecuteInterpreter) { interpreter_ = WasmDebugInfo::SetupForTesting(instance_object_); } } byte* TestingModuleBuilder::AddMemory(uint32_t size) { CHECK(!test_module_.has_memory); CHECK_NULL(mem_start_); CHECK_EQ(0, mem_size_); DCHECK(!instance_object_->has_memory_object()); test_module_.has_memory = true; const bool enable_guard_regions = trap_handler::UseTrapHandler() && test_module_.is_wasm(); uint32_t alloc_size = enable_guard_regions ? RoundUp(size, base::OS::CommitPageSize()) : size; Handle new_buffer = wasm::NewArrayBuffer(isolate_, alloc_size, enable_guard_regions); CHECK(!new_buffer.is_null()); mem_start_ = reinterpret_cast(new_buffer->backing_store()); mem_size_ = size; CHECK(size == 0 || mem_start_); memset(mem_start_, 0, size); // Create the WasmMemoryObject. Handle memory_object = WasmMemoryObject::New( isolate_, new_buffer, (test_module_.maximum_pages != 0) ? test_module_.maximum_pages : -1); instance_object_->set_memory_object(*memory_object); WasmMemoryObject::AddInstance(isolate_, memory_object, instance_object_); // TODO(wasm): Delete the following two lines when test-run-wasm will use a // multiple of kPageSize as memory size. At the moment, the effect of these // two lines is used to shrink the memory for testing purposes. instance_object_->wasm_context()->get()->mem_start = mem_start_; instance_object_->wasm_context()->get()->mem_size = mem_size_; return mem_start_; } uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, Handle code, const char* name) { if (test_module_.functions.size() == 0) { // TODO(titzer): Reserving space here to avoid the underlying WasmFunction // structs from moving. test_module_.functions.reserve(kMaxFunctions); } uint32_t index = static_cast(test_module_.functions.size()); test_module_.functions.push_back( {sig, index, 0, {0, 0}, {0, 0}, false, false}); if (name) { Vector name_vec = Vector::cast(CStrVector(name)); test_module_.functions.back().name = { AddBytes(name_vec), static_cast(name_vec.length())}; } function_code_.push_back(code); if (interpreter_) { interpreter_->AddFunctionForTesting(&test_module_.functions.back()); } DCHECK_LT(index, kMaxFunctions); // limited for testing. return index; } uint32_t TestingModuleBuilder::AddJsFunction( FunctionSig* sig, const char* source, Handle js_imports_table) { Handle jsfunc = Handle::cast(v8::Utils::OpenHandle( *v8::Local::Cast(CompileRun(source)))); uint32_t index = AddFunction(sig, Handle::null(), nullptr); js_imports_table->set(0, *isolate_->native_context()); Handle code = compiler::CompileWasmToJSWrapper( isolate_, jsfunc, sig, index, test_module_.origin(), js_imports_table); function_code_[index] = code; return index; } Handle TestingModuleBuilder::WrapCode(uint32_t index) { // Wrap the code so it can be called as a JS function. Handle code = function_code_[index]; byte* context_address = test_module_.has_memory ? reinterpret_cast(instance_object_->wasm_context()) : nullptr; Handle ret_code = compiler::CompileJSToWasmWrapper( isolate_, &test_module_, code, index, context_address); Handle ret = WasmExportedFunction::New( isolate_, instance_object(), MaybeHandle(), static_cast(index), static_cast(test_module_.functions[index].sig->parameter_count()), ret_code); // Add weak reference to exported functions. Handle compiled_module( instance_object()->compiled_module(), isolate_); Handle old_arr = compiled_module->weak_exported_functions(); Handle new_arr = isolate_->factory()->NewFixedArray(old_arr->length() + 1); old_arr->CopyTo(0, *new_arr, 0, old_arr->length()); Handle weak_fn = isolate_->factory()->NewWeakCell(ret); new_arr->set(old_arr->length(), *weak_fn); compiled_module->set_weak_exported_functions(new_arr); return ret; } void TestingModuleBuilder::AddIndirectFunctionTable(uint16_t* function_indexes, uint32_t table_size) { test_module_.function_tables.emplace_back(); WasmIndirectFunctionTable& table = test_module_.function_tables.back(); table.initial_size = table_size; table.maximum_size = table_size; table.has_maximum_size = true; for (uint32_t i = 0; i < table_size; ++i) { table.values.push_back(function_indexes[i]); } function_tables_.push_back( isolate_->global_handles() ->Create(*isolate_->factory()->NewFixedArray(table_size)) .address()); signature_tables_.push_back( isolate_->global_handles() ->Create(*isolate_->factory()->NewFixedArray(table_size)) .address()); } void TestingModuleBuilder::PopulateIndirectFunctionTable() { if (interpret()) return; // Initialize the fixed arrays in instance->function_tables. for (uint32_t i = 0; i < function_tables_.size(); i++) { WasmIndirectFunctionTable& table = test_module_.function_tables[i]; Handle function_table( reinterpret_cast(function_tables_[i])); Handle signature_table( reinterpret_cast(signature_tables_[i])); int table_size = static_cast(table.values.size()); for (int j = 0; j < table_size; j++) { WasmFunction& function = test_module_.functions[table.values[j]]; signature_table->set( j, Smi::FromInt(test_module_.signature_map.Find(function.sig))); function_table->set(j, *function_code_[function.func_index]); } } } uint32_t TestingModuleBuilder::AddBytes(Vector bytes) { Handle old_bytes( instance_object_->compiled_module()->module_bytes(), isolate_); uint32_t old_size = static_cast(old_bytes->length()); // Avoid placing strings at offset 0, this might be interpreted as "not // set", e.g. for function names. uint32_t bytes_offset = old_size ? old_size : 1; ScopedVector new_bytes(bytes_offset + bytes.length()); memcpy(new_bytes.start(), old_bytes->GetChars(), old_size); memcpy(new_bytes.start() + bytes_offset, bytes.start(), bytes.length()); Handle new_bytes_str = Handle::cast( isolate_->factory()->NewStringFromOneByte(new_bytes).ToHandleChecked()); instance_object_->compiled_module()->shared()->set_module_bytes( *new_bytes_str); return bytes_offset; } compiler::ModuleEnv TestingModuleBuilder::CreateModuleEnv() { return {&test_module_, function_tables_, signature_tables_, function_code_, Handle::null()}; } const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) { byte size = WasmOpcodes::MemSize(WasmOpcodes::MachineTypeFor(type)); global_offset = (global_offset + size - 1) & ~(size - 1); // align test_module_.globals.push_back( {type, true, WasmInitExpr(), global_offset, false, false}); global_offset += size; // limit number of globals. CHECK_LT(global_offset, kMaxGlobalsSize); return &test_module_.globals.back(); } Handle TestingModuleBuilder::InitInstanceObject() { Handle empty_string = Handle::cast( isolate_->factory()->NewStringFromOneByte({}).ToHandleChecked()); // The lifetime of the wasm module is tied to this object's, and we cannot // rely on the mechanics of Managed. Handle module_wrapper = isolate_->factory()->NewForeign( reinterpret_cast
(&test_module_ptr_)); Handle