wasm-module-runner.cc 9.83 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 11 12
#include "src/objects.h"
#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 32 33 34 35 36 37 38 39
MaybeHandle<WasmInstanceObject> CompileAndInstantiateForTesting(
    Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes) {
  MaybeHandle<WasmModuleObject> module =
      isolate->wasm_engine()->SyncCompile(isolate, thrower, bytes);
  DCHECK_EQ(thrower->error(), module.is_null());
  if (module.is_null()) return {};

  return isolate->wasm_engine()->SyncInstantiate(
      isolate, thrower, module.ToHandleChecked(), {}, {});
}

40
std::unique_ptr<WasmModule> DecodeWasmModuleForTesting(
41 42
    Isolate* isolate, ErrorThrower* thrower, const byte* module_start,
    const byte* module_end, ModuleOrigin origin, bool verify_functions) {
43 44
  // Decode the module, but don't verify function bodies, since we'll
  // be compiling them anyway.
45
  ModuleResult decoding_result = SyncDecodeWasmModule(
46
      isolate, module_start, module_end, verify_functions, origin);
47 48 49

  if (decoding_result.failed()) {
    // Module verification failed. throw.
50
    thrower->CompileError("DecodeWasmModule failed: %s",
51
                          decoding_result.error_msg().c_str());
52 53
  }

54
  return std::move(decoding_result.val);
55 56
}

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
bool InterpretWasmModuleForTesting(Isolate* isolate,
                                   Handle<WasmInstanceObject> instance,
                                   const char* name, size_t argc,
                                   WasmValue* args) {
  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]);

  memcpy(arguments.get(), args, std::min(param_count, argc));

  // Fill the parameters up with default values.
  for (size_t i = argc; i < param_count; ++i) {
    switch (signature->GetParam(i)) {
      case MachineRepresentation::kWord32:
        arguments[i] = WasmValue(int32_t{0});
        break;
      case MachineRepresentation::kWord64:
        arguments[i] = WasmValue(int64_t{0});
        break;
      case MachineRepresentation::kFloat32:
        arguments[i] = WasmValue(0.0f);
        break;
      case MachineRepresentation::kFloat64:
        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::HeapObjectsScope heap_objects_scope(interpreter, instance);
  WasmInterpreter::Thread* thread = interpreter->GetThread(0);
  thread->Reset();
103 104 105 106 107 108 109

  // 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();
110 111
  thread->InitFrame(&instance->module()->functions[function_index],
                    arguments.get());
112 113
  WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);

114 115
  isolate->clear_pending_exception();

116 117 118
  return interpreter_result != WasmInterpreter::PAUSED;
}

119 120 121
int32_t RunWasmModuleForTesting(Isolate* isolate,
                                Handle<WasmInstanceObject> instance, int argc,
                                Handle<Object> argv[]) {
122
  ErrorThrower thrower(isolate, "RunWasmModule");
123 124
  return CallWasmFunctionForTesting(isolate, instance, &thrower, "main", argc,
                                    argv);
125 126
}

127
int32_t CompileAndRunWasmModule(Isolate* isolate, const byte* module_start,
128
                                const byte* module_end) {
129
  HandleScope scope(isolate);
130
  ErrorThrower thrower(isolate, "CompileAndRunWasmModule");
131 132
  MaybeHandle<WasmInstanceObject> instance = CompileAndInstantiateForTesting(
      isolate, &thrower, ModuleWireBytes(module_start, module_end));
133 134 135
  if (instance.is_null()) {
    return -1;
  }
136
  return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
137
                                 nullptr);
138 139 140 141 142 143
}

int32_t CompileAndRunAsmWasmModule(Isolate* isolate, const byte* module_start,
                                   const byte* module_end) {
  HandleScope scope(isolate);
  ErrorThrower thrower(isolate, "CompileAndRunAsmWasmModule");
144 145 146 147
  MaybeHandle<WasmModuleObject> module =
      isolate->wasm_engine()->SyncCompileTranslatedAsmJs(
          isolate, &thrower, ModuleWireBytes(module_start, module_end),
          Handle<Script>::null(), Vector<const byte>());
148 149 150
  DCHECK_EQ(thrower.error(), module.is_null());
  if (module.is_null()) return -1;

151 152 153 154
  MaybeHandle<WasmInstanceObject> instance =
      isolate->wasm_engine()->SyncInstantiate(
          isolate, &thrower, module.ToHandleChecked(),
          Handle<JSReceiver>::null(), Handle<JSArrayBuffer>::null());
155 156 157 158
  DCHECK_EQ(thrower.error(), instance.is_null());
  if (instance.is_null()) return -1;

  return RunWasmModuleForTesting(isolate, instance.ToHandleChecked(), 0,
159
                                 nullptr);
160
}
161 162 163
int32_t InterpretWasmModule(Isolate* isolate,
                            Handle<WasmInstanceObject> instance,
                            ErrorThrower* thrower, int32_t function_index,
164
                            WasmValue* args, bool* possible_nondeterminism) {
165 166 167
  // Don't execute more than 16k steps.
  constexpr int kMaxNumSteps = 16 * 1024;

168
  Zone zone(isolate->allocator(), ZONE_NAME);
169 170
  v8::internal::HandleScope scope(isolate);

171
  WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
172
  WasmInterpreter::HeapObjectsScope heap_objects_scope(interpreter, instance);
173
  WasmInterpreter::Thread* thread = interpreter->GetThread(0);
174
  thread->Reset();
175 176 177 178 179 180 181

  // 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();
182
  thread->InitFrame(&(instance->module()->functions[function_index]), args);
183
  WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
184

185 186 187
  bool stack_overflow = isolate->has_pending_exception();
  isolate->clear_pending_exception();

188
  *possible_nondeterminism = thread->PossibleNondeterminism();
189
  if (stack_overflow) return 0xDEADBEEF;
190

191
  if (thread->state() == WasmInterpreter::TRAPPED) return 0xDEADBEEF;
192 193 194 195 196 197 198

  if (interpreter_result == WasmInterpreter::FINISHED)
    return thread->GetReturnValue().to<int32_t>();

  thrower->RangeError(
      "Interpreter did not finish execution within its step bound");
  return -1;
199 200
}

201 202
MaybeHandle<WasmExportedFunction> GetExportedFunction(
    Isolate* isolate, Handle<WasmInstanceObject> instance, const char* name) {
203
  Handle<JSObject> exports_object;
204 205 206 207
  Handle<Name> exports = isolate->factory()->InternalizeUtf8String("exports");
  exports_object = Handle<JSObject>::cast(
      JSObject::GetProperty(instance, exports).ToHandleChecked());

208 209 210 211
  Handle<Name> main_name = isolate->factory()->NewStringFromAsciiChecked(name);
  PropertyDescriptor desc;
  Maybe<bool> property_found = JSReceiver::GetOwnPropertyDescriptor(
      isolate, exports_object, main_name, &desc);
212
  if (!property_found.FromMaybe(false)) return {};
213
  if (!desc.value()->IsJSFunction()) return {};
214

215 216 217
  return Handle<WasmExportedFunction>::cast(desc.value());
}

218 219
int32_t CallWasmFunctionForTesting(Isolate* isolate,
                                   Handle<WasmInstanceObject> instance,
220 221 222 223 224 225 226 227
                                   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;
  }
228 229 230 231 232 233 234 235

  // 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()) {
236 237
    DCHECK(isolate->has_pending_exception());
    isolate->clear_pending_exception();
238
    thrower->RuntimeError("Calling exported wasm function failed.");
239 240 241 242
    return -1;
  }
  Handle<Object> result = retval.ToHandleChecked();
  if (result->IsSmi()) {
jgruber's avatar
jgruber committed
243
    return Smi::ToInt(*result);
244 245 246 247
  }
  if (result->IsHeapNumber()) {
    return static_cast<int32_t>(HeapNumber::cast(*result)->value());
  }
248
  thrower->RuntimeError(
249
      "Calling exported wasm function failed: Return value should be number");
250 251 252
  return -1;
}

253
void SetupIsolateForWasmModule(Isolate* isolate) {
254
  WasmJs::Install(isolate, true);
255
}
256

257 258 259 260
}  // namespace testing
}  // namespace wasm
}  // namespace internal
}  // namespace v8