wasm-module-runner.cc 10.4 KB
Newer Older
1 2 3 4
// 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.

5
#include "test/common/wasm/wasm-module-runner.h"
6 7 8

#include "src/handles.h"
#include "src/isolate.h"
9
#include "src/objects-inl.h"
10
#include "src/objects/heap-number-inl.h"
11 12
#include "src/property-descriptor.h"
#include "src/wasm/module-decoder.h"
13
#include "src/wasm/wasm-engine.h"
14
#include "src/wasm/wasm-interpreter.h"
15
#include "src/wasm/wasm-js.h"
16
#include "src/wasm/wasm-module.h"
17
#include "src/wasm/wasm-objects.h"
18 19 20 21 22 23 24
#include "src/wasm/wasm-result.h"

namespace v8 {
namespace internal {
namespace wasm {
namespace testing {

25
uint32_t GetInitialMemSize(const WasmModule* module) {
26
  return kWasmPageSize * module->initial_pages;
27 28
}

29 30 31
MaybeHandle<WasmModuleObject> CompileForTesting(Isolate* isolate,
                                                ErrorThrower* thrower,
                                                const ModuleWireBytes& bytes) {
32 33 34
  auto enabled_features = WasmFeaturesFromIsolate(isolate);
  MaybeHandle<WasmModuleObject> module = isolate->wasm_engine()->SyncCompile(
      isolate, enabled_features, thrower, bytes);
35
  DCHECK_EQ(thrower->error(), module.is_null());
36 37
  return module;
}
38

39 40 41 42 43
MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
    Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
  MaybeHandle<WasmModuleObject> module =
      CompileForTesting(isolate, thrower, bytes);
  if (module.is_null()) return {};
44 45 46 47
  return isolate->wasm_engine()->SyncInstantiate(
      isolate, thrower, module.ToHandleChecked(), {}, {});
}

48
std::shared_ptr<WasmModule> DecodeWasmModuleForTesting(
49 50
    Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
    const byte* module_end, ModuleOrigin origin, bool verify_functions) {
51 52
  // Decode the module, but don't verify function bodies, since we'll
  // be compiling them anyway.
53 54 55
  auto enabled_features = WasmFeaturesFromIsolate(isolate);
  ModuleResult decoding_result = DecodeWasmModule(
      enabled_features, module_start, module_end, verify_functions, origin,
56
      isolate->counters(), isolate->wasm_engine()->allocator());
57 58 59

  if (decoding_result.failed()) {
    // Module verification failed. throw.
60
    thrower->CompileError("DecodeWasmModule failed: %s",
61
                          decoding_result.error().message().c_str());
62 63
  }

64
  return std::move(decoding_result).value();
65 66
}

67 68 69 70
bool InterpretWasmModuleForTesting(Isolate* isolate,
                                   Handle<WasmInstanceObject> instance,
                                   const char* name, size_t argc,
                                   WasmValue* args) {
71 72
  HandleScope handle_scope(isolate);  // Avoid leaking handles.
  WasmCodeRefScope code_ref_scope;
73 74 75 76 77 78 79 80 81 82 83
  MaybeHandle<WasmExportedFunction> maybe_function =
      GetExportedFunction(isolate, instance, "main");
  Handle<WasmExportedFunction> function;
  if (!maybe_function.ToHandle(&function)) {
    return false;
  }
  int function_index = function->function_index();
  FunctionSig* signature = instance->module()->functions[function_index].sig;
  size_t param_count = signature->parameter_count();
  std::unique_ptr<WasmValue[]> arguments(new WasmValue[param_count]);

84 85 86 87
  size_t arg_count = std::min(param_count, argc);
  if (arg_count > 0) {
    memcpy(arguments.get(), args, arg_count);
  }
88 89 90 91

  // Fill the parameters up with default values.
  for (size_t i = argc; i < param_count; ++i) {
    switch (signature->GetParam(i)) {
92
      case kWasmI32:
93 94
        arguments[i] = WasmValue(int32_t{0});
        break;
95
      case kWasmI64:
96 97
        arguments[i] = WasmValue(int64_t{0});
        break;
98
      case kWasmF32:
99 100
        arguments[i] = WasmValue(0.0f);
        break;
101
      case kWasmF64:
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
        arguments[i] = WasmValue(0.0);
        break;
      default:
        UNREACHABLE();
    }
  }

  // Don't execute more than 16k steps.
  constexpr int kMaxNumSteps = 16 * 1024;

  Zone zone(isolate->allocator(), ZONE_NAME);

  WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
  WasmInterpreter::Thread* thread = interpreter->GetThread(0);
  thread->Reset();
117 118 119 120 121 122 123

  // Start an activation so that we can deal with stack overflows. We do not
  // finish the activation. An activation is just part of the state of the
  // interpreter, and we do not reuse the interpreter anyways. In addition,
  // finishing the activation is not correct in all cases, e.g. when the
  // execution of the interpreter did not finish after kMaxNumSteps.
  thread->StartActivation();
124 125
  thread->InitFrame(&instance->module()->functions[function_index],
                    arguments.get());
126 127
  WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);

128 129
  isolate->clear_pending_exception();

130 131 132
  return interpreter_result != WasmInterpreter::PAUSED;
}

133 134 135
int32_t RunWasmModuleForTesting(Isolate* isolate,
                                Handle<WasmInstanceObject> instance, int argc,
                                Handle<Object> argv[]) {
136
  ErrorThrower thrower(isolate, "RunWasmModule");
137 138
  return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
                                    argv);
139 140
}

141
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
142
                                const byte* module_end) {
143
  HandleScope scope(isolate);
144
  ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
145 146
  MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
      isolate, &thrower, ModuleWireBytes(module_start, module_end));
147 148 149
  if (instance.is_null()) {
    return -1;
  }
150
  return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
151
                                 nullptr);
152 153 154 155 156 157
}

int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
                                   const byte* module_end) {
  HandleScope scope(isolate);
  ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
158
  MaybeHandle<AsmWasmData> data =
159 160
      isolate->wasm_engine()->SyncCompileTranslatedAsmJs(
          isolate, &thrower, ModuleWireBytes(module_start, module_end),
161 162 163 164 165 166 167
          Vector<const byte>(), Handle<HeapNumber>());
  DCHECK_EQ(thrower.error(), data.is_null());
  if (data.is_null()) return -1;

  MaybeHandle<WasmModuleObject> module =
      isolate->wasm_engine()->FinalizeTranslatedAsmJs(
          isolate, data.ToHandleChecked(), Handle<Script>::null());
168

169 170 171 172
  MaybeHandle<WasmInstanceObject> instance =
      isolate->wasm_engine()->SyncInstantiate(
          isolate, &thrower, module.ToHandleChecked(),
          Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null());
173 174 175 176
  DCHECK_EQ(thrower.error(), instance.is_null());
  if (instance.is_null()) return -1;

  return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
177
                                 nullptr);
178
}
179 180 181
WasmInterpretationResult InterpretWasmModule(
    Isolate* isolate, Handle<WasmInstanceObject> instance,
    int32_t function_index, WasmValue* args) {
182 183 184
  // Don't execute more than 16k steps.
  constexpr int kMaxNumSteps = 16 * 1024;

185
  Zone zone(isolate->allocator(), ZONE_NAME);
186 187
  v8::internal::HandleScope scope(isolate);

188
  WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
189
  WasmInterpreter::Thread* thread = interpreter->GetThread(0);
190
  thread->Reset();
191 192 193 194 195 196 197

  // Start an activation so that we can deal with stack overflows. We do not
  // finish the activation. An activation is just part of the state of the
  // interpreter, and we do not reuse the interpreter anyways. In addition,
  // finishing the activation is not correct in all cases, e.g. when the
  // execution of the interpreter did not finish after kMaxNumSteps.
  thread->StartActivation();
198
  thread->InitFrame(&(instance->module()->functions[function_index]), args);
199
  WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
200

201 202 203
  bool stack_overflow = isolate->has_pending_exception();
  isolate->clear_pending_exception();

204
  if (stack_overflow) return WasmInterpretationResult::Stopped();
205

206 207 208
  if (thread->state() == WasmInterpreter::TRAPPED) {
    return WasmInterpretationResult::Trapped(thread->PossibleNondeterminism());
  }
209

210 211 212 213 214
  if (interpreter_result == WasmInterpreter::FINISHED) {
    return WasmInterpretationResult::Finished(
        thread->GetReturnValue().to<int32_t>(),
        thread->PossibleNondeterminism());
  }
215

216
  return WasmInterpretationResult::Stopped();
217 218
}

219 220
MaybeHandle<WasmExportedFunction> GetExportedFunction(
    Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
221
  Handle<JSObject> exports_object;
222 223
  Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
  exports_object = Handle<JSObject>::cast(
224
      JSObject::GetProperty(isolate, instance, exports).ToHandleChecked());
225

226 227 228 229
  Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
  PropertyDescriptor desc;
  Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
      isolate, exports_object, main_name, &desc);
230
  if (!property_found.FromMaybe(false)) return {};
231
  if (!desc.value()->IsJSFunction()) return {};
232

233 234 235
  return Handle<WasmExportedFunction>::cast(desc.value());
}

236 237
int32_t CallWasmFunctionForTesting(Isolate* isolate,
                                   Handle<WasmInstanceObject> instance,
238 239 240 241 242 243 244 245
                                   ErrorThrower* thrower, const char* name,
                                   int argc, Handle<Object> argv[]) {
  MaybeHandle<WasmExportedFunction> maybe_export =
      GetExportedFunction(isolate, instance, name);
  Handle<WasmExportedFunction> main_export;
  if (!maybe_export.ToHandle(&main_export)) {
    return -1;
  }
246 247 248 249 250 251 252 253

  // Call the JS function.
  Handle<Object> undefined = isolate->factory()->undefined_value();
  MaybeHandle<Object> retval =
      Execution::Call(isolate, main_export, undefined, argc, argv);

  // The result should be a number.
  if (retval.is_null()) {
254 255
    DCHECK(isolate->has_pending_exception());
    isolate->clear_pending_exception();
256
    thrower->RuntimeError("Calling exported wasm function failed.");
257 258 259 260
    return -1;
  }
  Handle<Object> result = retval.ToHandleChecked();
  if (result->IsSmi()) {
jgruber's avatar
jgruber committed
261
    return Smi::ToInt(*result);
262 263 264 265
  }
  if (result->IsHeapNumber()) {
    return static_cast<int32_t>(HeapNumber::cast(*result)->value());
  }
266
  thrower->RuntimeError(
267
      "Calling exported wasm function failed: Return value should be number");
268 269 270
  return -1;
}

271
void SetupIsolateForWasmModule(Isolate* isolate) {
272
  WasmJs::Install(isolate, true);
273
}
274

275 276 277 278
}  // namespace testing
}  // namespace wasm
}  // namespace internal
}  // namespace v8