1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/wasm/wasm-objects.h"
6#include "src/wasm/wasm-module.h"
7
8#define TRACE(...)                                      \
9  do {                                                  \
10    if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \
11  } while (false)
12
13#define TRACE_CHAIN(instance)        \
14  do {                               \
15    instance->PrintInstancesChain(); \
16  } while (false)
17
18using namespace v8::internal;
19using namespace v8::internal::wasm;
20
21#define DEFINE_ACCESSORS(Container, name, field, type) \
22  type* Container::get_##name() {                      \
23    return type::cast(GetInternalField(field));        \
24  }                                                    \
25  void Container::set_##name(type* value) {            \
26    return SetInternalField(field, value);             \
27  }
28
29#define DEFINE_OPTIONAL_ACCESSORS(Container, name, field, type) \
30  bool Container::has_##name() {                                \
31    return !GetInternalField(field)->IsUndefined(GetIsolate()); \
32  }                                                             \
33  type* Container::get_##name() {                               \
34    return type::cast(GetInternalField(field));                 \
35  }                                                             \
36  void Container::set_##name(type* value) {                     \
37    return SetInternalField(field, value);                      \
38  }
39
40#define DEFINE_GETTER(Container, name, field, type) \
41  type* Container::get_##name() { return type::cast(GetInternalField(field)); }
42
43static uint32_t SafeUint32(Object* value) {
44  if (value->IsSmi()) {
45    int32_t val = Smi::cast(value)->value();
46    CHECK_GE(val, 0);
47    return static_cast<uint32_t>(val);
48  }
49  DCHECK(value->IsHeapNumber());
50  HeapNumber* num = HeapNumber::cast(value);
51  CHECK_GE(num->value(), 0.0);
52  CHECK_LE(num->value(), static_cast<double>(kMaxUInt32));
53  return static_cast<uint32_t>(num->value());
54}
55
56static int32_t SafeInt32(Object* value) {
57  if (value->IsSmi()) {
58    return Smi::cast(value)->value();
59  }
60  DCHECK(value->IsHeapNumber());
61  HeapNumber* num = HeapNumber::cast(value);
62  CHECK_GE(num->value(), static_cast<double>(Smi::kMinValue));
63  CHECK_LE(num->value(), static_cast<double>(Smi::kMaxValue));
64  return static_cast<int32_t>(num->value());
65}
66
67Handle<WasmModuleObject> WasmModuleObject::New(
68    Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
69  ModuleOrigin origin = compiled_module->module()->origin;
70
71  Handle<JSObject> module_object;
72  if (origin == ModuleOrigin::kWasmOrigin) {
73    Handle<JSFunction> module_cons(
74        isolate->native_context()->wasm_module_constructor());
75    module_object = isolate->factory()->NewJSObject(module_cons);
76    Handle<Symbol> module_sym(isolate->native_context()->wasm_module_sym());
77    Object::SetProperty(module_object, module_sym, module_object, STRICT)
78        .Check();
79  } else {
80    DCHECK(origin == ModuleOrigin::kAsmJsOrigin);
81    Handle<Map> map = isolate->factory()->NewMap(
82        JS_OBJECT_TYPE,
83        JSObject::kHeaderSize + WasmModuleObject::kFieldCount * kPointerSize);
84    module_object = isolate->factory()->NewJSObjectFromMap(map, TENURED);
85  }
86  module_object->SetInternalField(WasmModuleObject::kCompiledModule,
87                                  *compiled_module);
88  Handle<WeakCell> link_to_module =
89      isolate->factory()->NewWeakCell(module_object);
90  compiled_module->set_weak_wasm_module(link_to_module);
91  return Handle<WasmModuleObject>::cast(module_object);
92}
93
94WasmModuleObject* WasmModuleObject::cast(Object* object) {
95  DCHECK(object->IsJSObject());
96  // TODO(titzer): brand check for WasmModuleObject.
97  return reinterpret_cast<WasmModuleObject*>(object);
98}
99
100Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial,
101                                             uint32_t maximum,
102                                             Handle<FixedArray>* js_functions) {
103  Handle<JSFunction> table_ctor(
104      isolate->native_context()->wasm_table_constructor());
105  Handle<JSObject> table_obj = isolate->factory()->NewJSObject(table_ctor);
106  *js_functions = isolate->factory()->NewFixedArray(initial);
107  Object* null = isolate->heap()->null_value();
108  for (int i = 0; i < static_cast<int>(initial); ++i) {
109    (*js_functions)->set(i, null);
110  }
111  table_obj->SetInternalField(kFunctions, *(*js_functions));
112  table_obj->SetInternalField(kMaximum,
113                              static_cast<Object*>(Smi::FromInt(maximum)));
114
115  Handle<FixedArray> dispatch_tables = isolate->factory()->NewFixedArray(0);
116  table_obj->SetInternalField(kDispatchTables, *dispatch_tables);
117  Handle<Symbol> table_sym(isolate->native_context()->wasm_table_sym());
118  Object::SetProperty(table_obj, table_sym, table_obj, STRICT).Check();
119  return Handle<WasmTableObject>::cast(table_obj);
120}
121
122DEFINE_GETTER(WasmTableObject, dispatch_tables, kDispatchTables, FixedArray)
123
124Handle<FixedArray> WasmTableObject::AddDispatchTable(
125    Isolate* isolate, Handle<WasmTableObject> table_obj,
126    Handle<WasmInstanceObject> instance, int table_index,
127    Handle<FixedArray> dispatch_table) {
128  Handle<FixedArray> dispatch_tables(
129      FixedArray::cast(table_obj->GetInternalField(kDispatchTables)), isolate);
130  DCHECK_EQ(0, dispatch_tables->length() % 3);
131
132  if (instance.is_null()) return dispatch_tables;
133  // TODO(titzer): use weak cells here to avoid leaking instances.
134
135  // Grow the dispatch table and add a new triple at the end.
136  Handle<FixedArray> new_dispatch_tables =
137      isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 3);
138
139  new_dispatch_tables->set(dispatch_tables->length() + 0, *instance);
140  new_dispatch_tables->set(dispatch_tables->length() + 1,
141                           Smi::FromInt(table_index));
142  new_dispatch_tables->set(dispatch_tables->length() + 2, *dispatch_table);
143
144  table_obj->SetInternalField(WasmTableObject::kDispatchTables,
145                              *new_dispatch_tables);
146
147  return new_dispatch_tables;
148}
149
150DEFINE_ACCESSORS(WasmTableObject, functions, kFunctions, FixedArray)
151
152uint32_t WasmTableObject::current_length() { return get_functions()->length(); }
153
154uint32_t WasmTableObject::maximum_length() {
155  return SafeUint32(GetInternalField(kMaximum));
156}
157
158WasmTableObject* WasmTableObject::cast(Object* object) {
159  DCHECK(object && object->IsJSObject());
160  // TODO(titzer): brand check for WasmTableObject.
161  return reinterpret_cast<WasmTableObject*>(object);
162}
163
164Handle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate,
165                                               Handle<JSArrayBuffer> buffer,
166                                               int maximum) {
167  Handle<JSFunction> memory_ctor(
168      isolate->native_context()->wasm_memory_constructor());
169  Handle<JSObject> memory_obj = isolate->factory()->NewJSObject(memory_ctor);
170  memory_obj->SetInternalField(kArrayBuffer, *buffer);
171  memory_obj->SetInternalField(kMaximum,
172                               static_cast<Object*>(Smi::FromInt(maximum)));
173  Handle<Symbol> memory_sym(isolate->native_context()->wasm_memory_sym());
174  Object::SetProperty(memory_obj, memory_sym, memory_obj, STRICT).Check();
175  return Handle<WasmMemoryObject>::cast(memory_obj);
176}
177
178DEFINE_ACCESSORS(WasmMemoryObject, buffer, kArrayBuffer, JSArrayBuffer)
179
180uint32_t WasmMemoryObject::current_pages() {
181  return SafeUint32(get_buffer()->byte_length()) / wasm::WasmModule::kPageSize;
182}
183
184int32_t WasmMemoryObject::maximum_pages() {
185  return SafeInt32(GetInternalField(kMaximum));
186}
187
188WasmMemoryObject* WasmMemoryObject::cast(Object* object) {
189  DCHECK(object && object->IsJSObject());
190  // TODO(titzer): brand check for WasmMemoryObject.
191  return reinterpret_cast<WasmMemoryObject*>(object);
192}
193
194void WasmMemoryObject::AddInstance(WasmInstanceObject* instance) {
195  // TODO(gdeepti): This should be a weak list of instance objects
196  // for instances that share memory.
197  SetInternalField(kInstance, instance);
198}
199
200DEFINE_ACCESSORS(WasmInstanceObject, compiled_module, kCompiledModule,
201                 WasmCompiledModule)
202DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, globals_buffer,
203                          kGlobalsArrayBuffer, JSArrayBuffer)
204DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_buffer, kMemoryArrayBuffer,
205                          JSArrayBuffer)
206DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, memory_object, kMemoryObject,
207                          WasmMemoryObject)
208DEFINE_OPTIONAL_ACCESSORS(WasmInstanceObject, debug_info, kDebugInfo,
209                          WasmDebugInfo)
210
211WasmModuleObject* WasmInstanceObject::module_object() {
212  return WasmModuleObject::cast(*get_compiled_module()->wasm_module());
213}
214
215WasmModule* WasmInstanceObject::module() {
216  return reinterpret_cast<WasmModuleWrapper*>(
217             *get_compiled_module()->module_wrapper())
218      ->get();
219}
220
221WasmInstanceObject* WasmInstanceObject::cast(Object* object) {
222  DCHECK(IsWasmInstanceObject(object));
223  return reinterpret_cast<WasmInstanceObject*>(object);
224}
225
226bool WasmInstanceObject::IsWasmInstanceObject(Object* object) {
227  if (!object->IsObject()) return false;
228  if (!object->IsJSObject()) return false;
229
230  JSObject* obj = JSObject::cast(object);
231  Isolate* isolate = obj->GetIsolate();
232  if (obj->GetInternalFieldCount() != kFieldCount) {
233    return false;
234  }
235
236  Object* mem = obj->GetInternalField(kMemoryArrayBuffer);
237  if (!(mem->IsUndefined(isolate) || mem->IsJSArrayBuffer()) ||
238      !WasmCompiledModule::IsWasmCompiledModule(
239          obj->GetInternalField(kCompiledModule))) {
240    return false;
241  }
242
243  // All checks passed.
244  return true;
245}
246
247Handle<WasmInstanceObject> WasmInstanceObject::New(
248    Isolate* isolate, Handle<WasmCompiledModule> compiled_module) {
249  Handle<Map> map = isolate->factory()->NewMap(
250      JS_OBJECT_TYPE, JSObject::kHeaderSize + kFieldCount * kPointerSize);
251  Handle<WasmInstanceObject> instance(
252      reinterpret_cast<WasmInstanceObject*>(
253          *isolate->factory()->NewJSObjectFromMap(map, TENURED)),
254      isolate);
255
256  instance->SetInternalField(kCompiledModule, *compiled_module);
257  instance->SetInternalField(kMemoryObject, isolate->heap()->undefined_value());
258  return instance;
259}
260
261WasmInstanceObject* WasmExportedFunction::instance() {
262  return WasmInstanceObject::cast(GetInternalField(kInstance));
263}
264
265int WasmExportedFunction::function_index() {
266  return SafeInt32(GetInternalField(kIndex));
267}
268
269WasmExportedFunction* WasmExportedFunction::cast(Object* object) {
270  DCHECK(object && object->IsJSFunction());
271  DCHECK_EQ(Code::JS_TO_WASM_FUNCTION,
272            JSFunction::cast(object)->code()->kind());
273  // TODO(titzer): brand check for WasmExportedFunction.
274  return reinterpret_cast<WasmExportedFunction*>(object);
275}
276
277Handle<WasmExportedFunction> WasmExportedFunction::New(
278    Isolate* isolate, Handle<WasmInstanceObject> instance, Handle<String> name,
279    Handle<Code> export_wrapper, int arity, int func_index) {
280  DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
281  Handle<SharedFunctionInfo> shared =
282      isolate->factory()->NewSharedFunctionInfo(name, export_wrapper, false);
283  shared->set_length(arity);
284  shared->set_internal_formal_parameter_count(arity);
285  Handle<JSFunction> function = isolate->factory()->NewFunction(
286      isolate->wasm_function_map(), name, export_wrapper);
287  function->set_shared(*shared);
288
289  function->SetInternalField(kInstance, *instance);
290  function->SetInternalField(kIndex, Smi::FromInt(func_index));
291  return Handle<WasmExportedFunction>::cast(function);
292}
293
294Handle<WasmCompiledModule> WasmCompiledModule::New(
295    Isolate* isolate, Handle<WasmModuleWrapper> module_wrapper) {
296  Handle<FixedArray> ret =
297      isolate->factory()->NewFixedArray(PropertyIndices::Count, TENURED);
298  // WasmCompiledModule::cast would fail since module bytes are not set yet.
299  Handle<WasmCompiledModule> compiled_module(
300      reinterpret_cast<WasmCompiledModule*>(*ret), isolate);
301  compiled_module->InitId();
302  compiled_module->set_module_wrapper(module_wrapper);
303  return compiled_module;
304}
305
306wasm::WasmModule* WasmCompiledModule::module() const {
307  return reinterpret_cast<WasmModuleWrapper*>(*module_wrapper())->get();
308}
309
310void WasmCompiledModule::InitId() {
311#if DEBUG
312  static uint32_t instance_id_counter = 0;
313  set(kID_instance_id, Smi::FromInt(instance_id_counter++));
314  TRACE("New compiled module id: %d\n", instance_id());
315#endif
316}
317
318bool WasmCompiledModule::IsWasmCompiledModule(Object* obj) {
319  if (!obj->IsFixedArray()) return false;
320  FixedArray* arr = FixedArray::cast(obj);
321  if (arr->length() != PropertyIndices::Count) return false;
322  Isolate* isolate = arr->GetIsolate();
323#define WCM_CHECK_SMALL_NUMBER(TYPE, NAME) \
324  if (!arr->get(kID_##NAME)->IsSmi()) return false;
325#define WCM_CHECK_OBJECT_OR_WEAK(TYPE, NAME)         \
326  if (!arr->get(kID_##NAME)->IsUndefined(isolate) && \
327      !arr->get(kID_##NAME)->Is##TYPE())             \
328    return false;
329#define WCM_CHECK_OBJECT(TYPE, NAME) WCM_CHECK_OBJECT_OR_WEAK(TYPE, NAME)
330#define WCM_CHECK_WEAK_LINK(TYPE, NAME) WCM_CHECK_OBJECT_OR_WEAK(WeakCell, NAME)
331#define WCM_CHECK(KIND, TYPE, NAME) WCM_CHECK_##KIND(TYPE, NAME)
332  WCM_PROPERTY_TABLE(WCM_CHECK)
333#undef WCM_CHECK
334
335  // All checks passed.
336  return true;
337}
338
339void WasmCompiledModule::PrintInstancesChain() {
340#if DEBUG
341  if (!FLAG_trace_wasm_instances) return;
342  for (WasmCompiledModule* current = this; current != nullptr;) {
343    PrintF("->%d", current->instance_id());
344    if (current->ptr_to_weak_next_instance() == nullptr) break;
345    CHECK(!current->ptr_to_weak_next_instance()->cleared());
346    current =
347        WasmCompiledModule::cast(current->ptr_to_weak_next_instance()->value());
348  }
349  PrintF("\n");
350#endif
351}
352
353uint32_t WasmCompiledModule::mem_size() const {
354  return has_memory() ? memory()->byte_length()->Number() : default_mem_size();
355}
356
357uint32_t WasmCompiledModule::default_mem_size() const {
358  return min_mem_pages() * WasmModule::kPageSize;
359}
360