// 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, LowerSimd lower_simd) : 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_(lower_simd) { 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::IsTrapHandlerEnabled() && test_module_.is_wasm(); uint32_t alloc_size = enable_guard_regions ? RoundUp(size, 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()->SetRawMemory(mem_start_, mem_size_); return mem_start_; } uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, 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()); if (FLAG_wasm_jit_to_native) { native_module_->ResizeCodeTableForTest(index); } 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(Handle::null()); 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, nullptr); js_imports_table->set(0, *isolate_->native_context()); // TODO(6792): No longer needed once WebAssembly code is off heap. CodeSpaceMemoryModificationScope modification_scope(isolate_->heap()); Handle code = compiler::CompileWasmToJSWrapper( isolate_, jsfunc, sig, index, test_module_.origin(), trap_handler::IsTrapHandlerEnabled(), js_imports_table); if (FLAG_wasm_jit_to_native) { native_module_->ResizeCodeTableForTest(index); native_module_->AddCodeCopy(code, wasm::WasmCode::kWasmToJsWrapper, index); } else { function_code_[index] = code; } return index; } Handle TestingModuleBuilder::WrapCode(uint32_t index) { // Wrap the code so it can be called as a JS function. Link(); WasmCodeWrapper code = FLAG_wasm_jit_to_native ? WasmCodeWrapper(native_module_->GetCode(index)) : WasmCodeWrapper(function_code_[index]); byte* context_address = test_module_.has_memory ? reinterpret_cast(instance_object_->wasm_context()->get()) : nullptr; Handle ret_code = compiler::CompileJSToWasmWrapper( isolate_, &test_module_, code, index, context_address, trap_handler::IsTrapHandlerEnabled()); 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(), isolate_); 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( const 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]); } FixedArray* func_table = *isolate_->factory()->NewFixedArray( table_size * compiler::kFunctionTableEntrySize); function_tables_.push_back( isolate_->global_handles()->Create(func_table).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])); int table_size = static_cast(table.values.size()); for (int j = 0; j < table_size; j++) { WasmFunction& function = test_module_.functions[table.values[j]]; function_table->set( compiler::FunctionTableSigOffset(j), Smi::FromInt(test_module_.signature_map.Find(function.sig))); if (FLAG_wasm_jit_to_native) { Handle foreign_holder = isolate_->factory()->NewForeign( native_module_->GetCode(function.func_index) ->instructions() .start(), TENURED); function_table->set(compiler::FunctionTableCodeOffset(j), *foreign_holder); } else { function_table->set(compiler::FunctionTableCodeOffset(j), *function_code_[function.func_index]); } } } } uint32_t TestingModuleBuilder::AddBytes(Vector bytes) { Handle shared( instance_object_->compiled_module()->shared(), isolate_); Handle old_bytes(shared->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()); shared->set_module_bytes(*new_bytes_str); return bytes_offset; } compiler::ModuleEnv TestingModuleBuilder::CreateModuleEnv() { return {&test_module_, function_tables_, function_code_, Handle::null(), trap_handler::IsTrapHandlerEnabled()}; } 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