• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/api-natives.h"
6 #include "src/api.h"
7 #include "src/asmjs/asm-js.h"
8 #include "src/asmjs/asm-typer.h"
9 #include "src/asmjs/asm-wasm-builder.h"
10 #include "src/assert-scope.h"
11 #include "src/ast/ast.h"
12 #include "src/execution.h"
13 #include "src/factory.h"
14 #include "src/handles.h"
15 #include "src/isolate.h"
16 #include "src/objects-inl.h"
17 #include "src/objects.h"
18 #include "src/parsing/parse-info.h"
19 
20 #include "src/wasm/module-decoder.h"
21 #include "src/wasm/wasm-js.h"
22 #include "src/wasm/wasm-limits.h"
23 #include "src/wasm/wasm-module.h"
24 #include "src/wasm/wasm-objects.h"
25 #include "src/wasm/wasm-result.h"
26 
27 typedef uint8_t byte;
28 
29 using v8::internal::wasm::ErrorThrower;
30 
31 namespace v8 {
32 
33 namespace {
34 
35 #define RANGE_ERROR_MSG                                                        \
36   "Wasm compilation exceeds internal limits in this context for the provided " \
37   "arguments"
38 
39 // TODO(wasm): move brand check to the respective types, and don't throw
40 // in it, rather, use a provided ErrorThrower, or let caller handle it.
HasBrand(i::Handle<i::Object> value,i::Handle<i::Symbol> sym)41 static bool HasBrand(i::Handle<i::Object> value, i::Handle<i::Symbol> sym) {
42   if (!value->IsJSObject()) return false;
43   i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value);
44   Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym);
45   return has_brand.FromMaybe(false);
46 }
47 
BrandCheck(i::Handle<i::Object> value,i::Handle<i::Symbol> sym,ErrorThrower * thrower,const char * msg)48 static bool BrandCheck(i::Handle<i::Object> value, i::Handle<i::Symbol> sym,
49                        ErrorThrower* thrower, const char* msg) {
50   return HasBrand(value, sym) ? true : (thrower->TypeError("%s", msg), false);
51 }
52 
v8_str(i::Isolate * isolate,const char * str)53 i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
54   return isolate->factory()->NewStringFromAsciiChecked(str);
55 }
v8_str(Isolate * isolate,const char * str)56 Local<String> v8_str(Isolate* isolate, const char* str) {
57   return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str));
58 }
59 
GetFirstArgumentAsModule(const v8::FunctionCallbackInfo<v8::Value> & args,ErrorThrower * thrower)60 i::MaybeHandle<i::WasmModuleObject> GetFirstArgumentAsModule(
61     const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
62   v8::Isolate* isolate = args.GetIsolate();
63   if (args.Length() < 1) {
64     thrower->TypeError("Argument 0 must be a WebAssembly.Module");
65     return {};
66   }
67 
68   Local<Context> context = isolate->GetCurrentContext();
69   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
70   if (!BrandCheck(Utils::OpenHandle(*args[0]),
71                   i::handle(i_context->wasm_module_sym()), thrower,
72                   "Argument 0 must be a WebAssembly.Module")) {
73     return {};
74   }
75 
76   Local<Object> module_obj = Local<Object>::Cast(args[0]);
77   return i::Handle<i::WasmModuleObject>::cast(
78       v8::Utils::OpenHandle(*module_obj));
79 }
80 
IsCompilationAllowed(i::Isolate * isolate,ErrorThrower * thrower,v8::Local<v8::Value> source,bool is_async)81 bool IsCompilationAllowed(i::Isolate* isolate, ErrorThrower* thrower,
82                           v8::Local<v8::Value> source, bool is_async) {
83   // Allow caller to do one final check on thrower state, rather than
84   // one at each step. No information is lost - failure reason is captured
85   // in the thrower state.
86   if (thrower->error()) return false;
87 
88   AllowWasmCompileCallback callback = isolate->allow_wasm_compile_callback();
89   if (callback != nullptr &&
90       !callback(reinterpret_cast<v8::Isolate*>(isolate), source, is_async)) {
91     thrower->RangeError(RANGE_ERROR_MSG);
92     return false;
93   }
94   return true;
95 }
96 
IsInstantiationAllowed(i::Isolate * isolate,ErrorThrower * thrower,v8::Local<v8::Value> module_or_bytes,i::MaybeHandle<i::JSReceiver> ffi,bool is_async)97 bool IsInstantiationAllowed(i::Isolate* isolate, ErrorThrower* thrower,
98                             v8::Local<v8::Value> module_or_bytes,
99                             i::MaybeHandle<i::JSReceiver> ffi, bool is_async) {
100   // Allow caller to do one final check on thrower state, rather than
101   // one at each step. No information is lost - failure reason is captured
102   // in the thrower state.
103   if (thrower->error()) return false;
104   v8::MaybeLocal<v8::Value> v8_ffi;
105   if (!ffi.is_null()) {
106     v8_ffi = v8::Local<v8::Value>::Cast(Utils::ToLocal(ffi.ToHandleChecked()));
107   }
108   AllowWasmInstantiateCallback callback =
109       isolate->allow_wasm_instantiate_callback();
110   if (callback != nullptr &&
111       !callback(reinterpret_cast<v8::Isolate*>(isolate), module_or_bytes,
112                 v8_ffi, is_async)) {
113     thrower->RangeError(RANGE_ERROR_MSG);
114     return false;
115   }
116   return true;
117 }
118 
GetFirstArgumentAsBytes(const v8::FunctionCallbackInfo<v8::Value> & args,ErrorThrower * thrower)119 i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
120     const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
121   if (args.Length() < 1) {
122     thrower->TypeError("Argument 0 must be a buffer source");
123     return i::wasm::ModuleWireBytes(nullptr, nullptr);
124   }
125 
126   const byte* start = nullptr;
127   size_t length = 0;
128   v8::Local<v8::Value> source = args[0];
129   if (source->IsArrayBuffer()) {
130     // A raw array buffer was passed.
131     Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
132     ArrayBuffer::Contents contents = buffer->GetContents();
133 
134     start = reinterpret_cast<const byte*>(contents.Data());
135     length = contents.ByteLength();
136   } else if (source->IsTypedArray()) {
137     // A TypedArray was passed.
138     Local<TypedArray> array = Local<TypedArray>::Cast(source);
139     Local<ArrayBuffer> buffer = array->Buffer();
140 
141     ArrayBuffer::Contents contents = buffer->GetContents();
142 
143     start =
144         reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
145     length = array->ByteLength();
146   } else {
147     thrower->TypeError("Argument 0 must be a buffer source");
148   }
149   DCHECK_IMPLIES(length, start != nullptr);
150   if (length == 0) {
151     thrower->CompileError("BufferSource argument is empty");
152   }
153   if (length > i::wasm::kV8MaxWasmModuleSize) {
154     thrower->RangeError("buffer source exceeds maximum size of %zu (is %zu)",
155                         i::wasm::kV8MaxWasmModuleSize, length);
156   }
157   if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr);
158   // TODO(titzer): use the handle as well?
159   return i::wasm::ModuleWireBytes(start, start + length);
160 }
161 
GetSecondArgumentAsImports(const v8::FunctionCallbackInfo<v8::Value> & args,ErrorThrower * thrower)162 i::MaybeHandle<i::JSReceiver> GetSecondArgumentAsImports(
163     const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
164   if (args.Length() < 2) return {};
165   if (args[1]->IsUndefined()) return {};
166 
167   if (!args[1]->IsObject()) {
168     thrower->TypeError("Argument 1 must be an object");
169     return {};
170   }
171   Local<Object> obj = Local<Object>::Cast(args[1]);
172   return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
173 }
174 
175 // WebAssembly.compile(bytes) -> Promise
WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value> & args)176 void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
177   v8::Isolate* isolate = args.GetIsolate();
178   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
179   HandleScope scope(isolate);
180   ErrorThrower thrower(i_isolate, "WebAssembly.compile()");
181 
182   Local<Context> context = isolate->GetCurrentContext();
183   v8::Local<v8::Promise::Resolver> resolver;
184   if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
185   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
186   return_value.Set(resolver->GetPromise());
187 
188   auto bytes = GetFirstArgumentAsBytes(args, &thrower);
189   if (!IsCompilationAllowed(i_isolate, &thrower, args[0], true)) {
190     resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
191     return;
192   }
193   DCHECK(!thrower.error());
194   i::Handle<i::JSPromise> promise = Utils::OpenHandle(*resolver->GetPromise());
195   i::wasm::AsyncCompile(i_isolate, promise, bytes);
196 }
197 
198 // WebAssembly.validate(bytes) -> bool
WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value> & args)199 void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
200   v8::Isolate* isolate = args.GetIsolate();
201   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
202   HandleScope scope(isolate);
203   ErrorThrower thrower(i_isolate, "WebAssembly.validate()");
204 
205   auto bytes = GetFirstArgumentAsBytes(args, &thrower);
206 
207   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
208   if (!thrower.error() &&
209       i::wasm::SyncValidate(reinterpret_cast<i::Isolate*>(isolate), &thrower,
210                             bytes)) {
211     return_value.Set(v8::True(isolate));
212   } else {
213     if (thrower.wasm_error()) thrower.Reify();  // Clear error.
214     return_value.Set(v8::False(isolate));
215   }
216 }
217 
218 // new WebAssembly.Module(bytes) -> WebAssembly.Module
WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value> & args)219 void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
220   v8::Isolate* isolate = args.GetIsolate();
221   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
222   HandleScope scope(isolate);
223   ErrorThrower thrower(i_isolate, "WebAssembly.Module()");
224 
225   auto bytes = GetFirstArgumentAsBytes(args, &thrower);
226   if (!IsCompilationAllowed(i_isolate, &thrower, args[0], false)) return;
227 
228   DCHECK(!thrower.error());
229   i::MaybeHandle<i::Object> module_obj =
230       i::wasm::SyncCompile(i_isolate, &thrower, bytes);
231   if (module_obj.is_null()) return;
232 
233   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
234   return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked()));
235 }
236 
237 // WebAssembly.Module.imports(module) -> Array<Import>
WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value> & args)238 void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
239   HandleScope scope(args.GetIsolate());
240   v8::Isolate* isolate = args.GetIsolate();
241   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
242   ErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()");
243 
244   auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
245   if (thrower.error()) return;
246   auto imports = i::wasm::GetImports(i_isolate, maybe_module.ToHandleChecked());
247   args.GetReturnValue().Set(Utils::ToLocal(imports));
248 }
249 
250 // WebAssembly.Module.exports(module) -> Array<Export>
WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value> & args)251 void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
252   HandleScope scope(args.GetIsolate());
253   v8::Isolate* isolate = args.GetIsolate();
254   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
255   ErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()");
256 
257   auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
258   if (thrower.error()) return;
259   auto exports = i::wasm::GetExports(i_isolate, maybe_module.ToHandleChecked());
260   args.GetReturnValue().Set(Utils::ToLocal(exports));
261 }
262 
263 // WebAssembly.Module.customSections(module, name) -> Array<Section>
WebAssemblyModuleCustomSections(const v8::FunctionCallbackInfo<v8::Value> & args)264 void WebAssemblyModuleCustomSections(
265     const v8::FunctionCallbackInfo<v8::Value>& args) {
266   HandleScope scope(args.GetIsolate());
267   v8::Isolate* isolate = args.GetIsolate();
268   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
269   ErrorThrower thrower(i_isolate, "WebAssembly.Module.customSections()");
270 
271   auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
272   if (thrower.error()) return;
273 
274   if (args.Length() < 2) {
275     thrower.TypeError("Argument 1 must be a string");
276     return;
277   }
278 
279   i::Handle<i::Object> name = Utils::OpenHandle(*args[1]);
280   if (!name->IsString()) {
281     thrower.TypeError("Argument 1 must be a string");
282     return;
283   }
284 
285   auto custom_sections =
286       i::wasm::GetCustomSections(i_isolate, maybe_module.ToHandleChecked(),
287                                  i::Handle<i::String>::cast(name), &thrower);
288   if (thrower.error()) return;
289   args.GetReturnValue().Set(Utils::ToLocal(custom_sections));
290 }
291 
292 // new WebAssembly.Instance(module, imports) -> WebAssembly.Instance
WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value> & args)293 void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
294   HandleScope scope(args.GetIsolate());
295   v8::Isolate* isolate = args.GetIsolate();
296   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
297   ErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
298 
299   auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
300   if (thrower.error()) return;
301 
302   auto maybe_imports = GetSecondArgumentAsImports(args, &thrower);
303   if (!IsInstantiationAllowed(i_isolate, &thrower, args[0], maybe_imports,
304                               false)) {
305     return;
306   }
307   DCHECK(!thrower.error());
308 
309   i::MaybeHandle<i::Object> instance_object = i::wasm::SyncInstantiate(
310       i_isolate, &thrower, maybe_module.ToHandleChecked(), maybe_imports,
311       i::MaybeHandle<i::JSArrayBuffer>());
312   if (instance_object.is_null()) return;
313   args.GetReturnValue().Set(Utils::ToLocal(instance_object.ToHandleChecked()));
314 }
315 
316 // WebAssembly.instantiate(module, imports) -> WebAssembly.Instance
317 // WebAssembly.instantiate(bytes, imports) ->
318 //     {module: WebAssembly.Module, instance: WebAssembly.Instance}
WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value> & args)319 void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
320   v8::Isolate* isolate = args.GetIsolate();
321   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
322   ErrorThrower thrower(i_isolate, "WebAssembly.instantiate()");
323 
324   HandleScope scope(isolate);
325 
326   Local<Context> context = isolate->GetCurrentContext();
327   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
328 
329   v8::Local<v8::Promise::Resolver> resolver;
330   if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) return;
331   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
332   return_value.Set(resolver->GetPromise());
333 
334   if (args.Length() < 1) {
335     thrower.TypeError(
336         "Argument 0 must be provided and must be either a buffer source or a "
337         "WebAssembly.Module object");
338     resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
339     return;
340   }
341 
342   i::Handle<i::Object> first_arg = Utils::OpenHandle(*args[0]);
343   if (!first_arg->IsJSObject()) {
344     thrower.TypeError(
345         "Argument 0 must be a buffer source or a WebAssembly.Module object");
346     resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
347     return;
348   }
349 
350   auto maybe_imports = GetSecondArgumentAsImports(args, &thrower);
351   if (thrower.error()) {
352     resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
353     return;
354   }
355   if (!IsInstantiationAllowed(i_isolate, &thrower, args[0], maybe_imports,
356                               true)) {
357     resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
358     return;
359   }
360   i::Handle<i::JSPromise> promise = Utils::OpenHandle(*resolver->GetPromise());
361   if (HasBrand(first_arg, i::Handle<i::Symbol>(i_context->wasm_module_sym()))) {
362     // WebAssembly.instantiate(module, imports) -> WebAssembly.Instance
363     auto module_object = GetFirstArgumentAsModule(args, &thrower);
364     i::wasm::AsyncInstantiate(i_isolate, promise,
365                               module_object.ToHandleChecked(), maybe_imports);
366   } else {
367     // WebAssembly.instantiate(bytes, imports) -> {module, instance}
368     auto bytes = GetFirstArgumentAsBytes(args, &thrower);
369     if (thrower.error()) {
370       resolver->Reject(context, Utils::ToLocal(thrower.Reify()));
371       return;
372     }
373     i::wasm::AsyncCompileAndInstantiate(i_isolate, promise, bytes,
374                                         maybe_imports);
375   }
376 }
377 
GetIntegerProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,Local<v8::Object> object,Local<String> property,int * result,int64_t lower_bound,uint64_t upper_bound)378 bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
379                         Local<Context> context, Local<v8::Object> object,
380                         Local<String> property, int* result,
381                         int64_t lower_bound, uint64_t upper_bound) {
382   v8::MaybeLocal<v8::Value> maybe = object->Get(context, property);
383   v8::Local<v8::Value> value;
384   if (maybe.ToLocal(&value)) {
385     int64_t number;
386     if (!value->IntegerValue(context).To(&number)) return false;
387     if (number < lower_bound) {
388       thrower->RangeError("Property value %" PRId64
389                           " is below the lower bound %" PRIx64,
390                           number, lower_bound);
391       return false;
392     }
393     if (number > static_cast<int64_t>(upper_bound)) {
394       thrower->RangeError("Property value %" PRId64
395                           " is above the upper bound %" PRIu64,
396                           number, upper_bound);
397       return false;
398     }
399     *result = static_cast<int>(number);
400     return true;
401   }
402   return false;
403 }
404 
405 // new WebAssembly.Table(args) -> WebAssembly.Table
WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value> & args)406 void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
407   v8::Isolate* isolate = args.GetIsolate();
408   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
409   HandleScope scope(isolate);
410   ErrorThrower thrower(i_isolate, "WebAssembly.Module()");
411   if (args.Length() < 1 || !args[0]->IsObject()) {
412     thrower.TypeError("Argument 0 must be a table descriptor");
413     return;
414   }
415   Local<Context> context = isolate->GetCurrentContext();
416   Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
417   // The descriptor's 'element'.
418   {
419     v8::MaybeLocal<v8::Value> maybe =
420         descriptor->Get(context, v8_str(isolate, "element"));
421     v8::Local<v8::Value> value;
422     if (!maybe.ToLocal(&value)) return;
423     v8::Local<v8::String> string;
424     if (!value->ToString(context).ToLocal(&string)) return;
425     bool equal;
426     if (!string->Equals(context, v8_str(isolate, "anyfunc")).To(&equal)) return;
427     if (!equal) {
428       thrower.TypeError("Descriptor property 'element' must be 'anyfunc'");
429       return;
430     }
431   }
432   // The descriptor's 'initial'.
433   int initial = 0;
434   if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
435                           v8_str(isolate, "initial"), &initial, 0,
436                           i::FLAG_wasm_max_table_size)) {
437     return;
438   }
439   // The descriptor's 'maximum'.
440   int maximum = -1;
441   Local<String> maximum_key = v8_str(isolate, "maximum");
442   Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
443 
444   if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
445     if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
446                             &maximum, initial,
447                             i::wasm::kSpecMaxWasmTableSize)) {
448       return;
449     }
450   }
451 
452   i::Handle<i::FixedArray> fixed_array;
453   i::Handle<i::JSObject> table_obj =
454       i::WasmTableObject::New(i_isolate, initial, maximum, &fixed_array);
455   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
456   return_value.Set(Utils::ToLocal(table_obj));
457 }
458 
WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value> & args)459 void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
460   v8::Isolate* isolate = args.GetIsolate();
461   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
462   HandleScope scope(isolate);
463   ErrorThrower thrower(i_isolate, "WebAssembly.Memory()");
464   if (args.Length() < 1 || !args[0]->IsObject()) {
465     thrower.TypeError("Argument 0 must be a memory descriptor");
466     return;
467   }
468   Local<Context> context = isolate->GetCurrentContext();
469   Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked();
470   // The descriptor's 'initial'.
471   int initial = 0;
472   if (!GetIntegerProperty(isolate, &thrower, context, descriptor,
473                           v8_str(isolate, "initial"), &initial, 0,
474                           i::FLAG_wasm_max_mem_pages)) {
475     return;
476   }
477   // The descriptor's 'maximum'.
478   int maximum = -1;
479   Local<String> maximum_key = v8_str(isolate, "maximum");
480   Maybe<bool> has_maximum = descriptor->Has(context, maximum_key);
481 
482   if (!has_maximum.IsNothing() && has_maximum.FromJust()) {
483     if (!GetIntegerProperty(isolate, &thrower, context, descriptor, maximum_key,
484                             &maximum, initial,
485                             i::wasm::kSpecMaxWasmMemoryPages)) {
486       return;
487     }
488   }
489   size_t size = static_cast<size_t>(i::wasm::WasmModule::kPageSize) *
490                 static_cast<size_t>(initial);
491   i::Handle<i::JSArrayBuffer> buffer =
492       i::wasm::NewArrayBuffer(i_isolate, size, i::FLAG_wasm_guard_pages);
493   if (buffer.is_null()) {
494     thrower.RangeError("could not allocate memory");
495     return;
496   }
497   i::Handle<i::JSObject> memory_obj =
498       i::WasmMemoryObject::New(i_isolate, buffer, maximum);
499   args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
500 }
501 
WebAssemblyTableGetLength(const v8::FunctionCallbackInfo<v8::Value> & args)502 void WebAssemblyTableGetLength(
503     const v8::FunctionCallbackInfo<v8::Value>& args) {
504   v8::Isolate* isolate = args.GetIsolate();
505   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
506   HandleScope scope(isolate);
507   ErrorThrower thrower(i_isolate, "WebAssembly.Table.length()");
508   Local<Context> context = isolate->GetCurrentContext();
509   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
510   if (!BrandCheck(Utils::OpenHandle(*args.This()),
511                   i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower,
512                   "Receiver is not a WebAssembly.Table")) {
513     return;
514   }
515   auto receiver =
516       i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
517   args.GetReturnValue().Set(
518       v8::Number::New(isolate, receiver->current_length()));
519 }
520 
521 // WebAssembly.Table.grow(num) -> num
WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value> & args)522 void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
523   v8::Isolate* isolate = args.GetIsolate();
524   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
525   HandleScope scope(isolate);
526   ErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()");
527   Local<Context> context = isolate->GetCurrentContext();
528   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
529   if (!BrandCheck(Utils::OpenHandle(*args.This()),
530                   i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower,
531                   "Receiver is not a WebAssembly.Table")) {
532     return;
533   }
534 
535   auto receiver =
536       i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
537   i::Handle<i::FixedArray> old_array(receiver->functions(), i_isolate);
538   int old_size = old_array->length();
539   int64_t new_size64 = 0;
540   if (args.Length() > 0 && !args[0]->IntegerValue(context).To(&new_size64)) {
541     return;
542   }
543   new_size64 += old_size;
544 
545   int64_t max_size64 = receiver->maximum_length();
546   if (max_size64 < 0 ||
547       max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_table_size)) {
548     max_size64 = i::FLAG_wasm_max_table_size;
549   }
550 
551   if (new_size64 < old_size || new_size64 > max_size64) {
552     thrower.RangeError(new_size64 < old_size ? "trying to shrink table"
553                                              : "maximum table size exceeded");
554     return;
555   }
556 
557   int new_size = static_cast<int>(new_size64);
558   i::WasmTableObject::Grow(i_isolate, receiver,
559                            static_cast<uint32_t>(new_size - old_size));
560 
561   if (new_size != old_size) {
562     i::Handle<i::FixedArray> new_array =
563         i_isolate->factory()->NewFixedArray(new_size);
564     for (int i = 0; i < old_size; ++i) new_array->set(i, old_array->get(i));
565     i::Object* null = i_isolate->heap()->null_value();
566     for (int i = old_size; i < new_size; ++i) new_array->set(i, null);
567     receiver->set_functions(*new_array);
568   }
569 
570   // TODO(gdeepti): use weak links for instances
571   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
572   return_value.Set(old_size);
573 }
574 
575 // WebAssembly.Table.get(num) -> JSFunction
WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value> & args)576 void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
577   v8::Isolate* isolate = args.GetIsolate();
578   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
579   HandleScope scope(isolate);
580   ErrorThrower thrower(i_isolate, "WebAssembly.Table.get()");
581   Local<Context> context = isolate->GetCurrentContext();
582   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
583   if (!BrandCheck(Utils::OpenHandle(*args.This()),
584                   i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower,
585                   "Receiver is not a WebAssembly.Table")) {
586     return;
587   }
588 
589   auto receiver =
590       i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
591   i::Handle<i::FixedArray> array(receiver->functions(), i_isolate);
592   int i = 0;
593   if (args.Length() > 0 && !args[0]->Int32Value(context).To(&i)) return;
594   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
595   if (i < 0 || i >= array->length()) {
596     thrower.RangeError("index out of bounds");
597     return;
598   }
599 
600   i::Handle<i::Object> value(array->get(i), i_isolate);
601   return_value.Set(Utils::ToLocal(value));
602 }
603 
604 // WebAssembly.Table.set(num, JSFunction)
WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value> & args)605 void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
606   v8::Isolate* isolate = args.GetIsolate();
607   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
608   HandleScope scope(isolate);
609   ErrorThrower thrower(i_isolate, "WebAssembly.Table.set()");
610   Local<Context> context = isolate->GetCurrentContext();
611   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
612   if (!BrandCheck(Utils::OpenHandle(*args.This()),
613                   i::Handle<i::Symbol>(i_context->wasm_table_sym()), &thrower,
614                   "Receiver is not a WebAssembly.Table")) {
615     return;
616   }
617   if (args.Length() < 2) {
618     thrower.TypeError("Argument 1 must be null or a function");
619     return;
620   }
621   i::Handle<i::Object> value = Utils::OpenHandle(*args[1]);
622   if (!value->IsNull(i_isolate) &&
623       (!value->IsJSFunction() ||
624        i::Handle<i::JSFunction>::cast(value)->code()->kind() !=
625            i::Code::JS_TO_WASM_FUNCTION)) {
626     thrower.TypeError("Argument 1 must be null or a WebAssembly function");
627     return;
628   }
629 
630   auto receiver =
631       i::Handle<i::WasmTableObject>::cast(Utils::OpenHandle(*args.This()));
632   i::Handle<i::FixedArray> array(receiver->functions(), i_isolate);
633   int i;
634   if (!args[0]->Int32Value(context).To(&i)) return;
635   if (i < 0 || i >= array->length()) {
636     thrower.RangeError("index out of bounds");
637     return;
638   }
639 
640   i::Handle<i::FixedArray> dispatch_tables(receiver->dispatch_tables(),
641                                            i_isolate);
642   if (value->IsNull(i_isolate)) {
643     i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
644                                   i::Handle<i::JSFunction>::null());
645   } else {
646     i::wasm::UpdateDispatchTables(i_isolate, dispatch_tables, i,
647                                   i::Handle<i::JSFunction>::cast(value));
648   }
649 
650   i::Handle<i::FixedArray>::cast(array)->set(i, *value);
651 }
652 
653 // WebAssembly.Memory.grow(num) -> num
WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value> & args)654 void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
655   v8::Isolate* isolate = args.GetIsolate();
656   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
657   HandleScope scope(isolate);
658   ErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()");
659   Local<Context> context = isolate->GetCurrentContext();
660   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
661   if (!BrandCheck(Utils::OpenHandle(*args.This()),
662                   i::Handle<i::Symbol>(i_context->wasm_memory_sym()), &thrower,
663                   "Receiver is not a WebAssembly.Memory")) {
664     return;
665   }
666   int64_t delta_size = 0;
667   if (args.Length() < 1 || !args[0]->IntegerValue(context).To(&delta_size)) {
668     thrower.TypeError("Argument 0 required, must be numeric value of pages");
669     return;
670   }
671   i::Handle<i::WasmMemoryObject> receiver =
672       i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
673   int64_t max_size64 = receiver->maximum_pages();
674   if (max_size64 < 0 ||
675       max_size64 > static_cast<int64_t>(i::FLAG_wasm_max_mem_pages)) {
676     max_size64 = i::FLAG_wasm_max_mem_pages;
677   }
678   i::Handle<i::JSArrayBuffer> old_buffer(receiver->buffer());
679   uint32_t old_size =
680       old_buffer->byte_length()->Number() / i::wasm::kSpecMaxWasmMemoryPages;
681   int64_t new_size64 = old_size + delta_size;
682   if (delta_size < 0 || max_size64 < new_size64 || new_size64 < old_size) {
683     thrower.RangeError(new_size64 < old_size ? "trying to shrink memory"
684                                              : "maximum memory size exceeded");
685     return;
686   }
687   int32_t ret = i::wasm::GrowWebAssemblyMemory(
688       i_isolate, receiver, static_cast<uint32_t>(delta_size));
689   if (ret == -1) {
690     thrower.RangeError("Unable to grow instance memory.");
691     return;
692   }
693   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
694   return_value.Set(ret);
695 }
696 
697 // WebAssembly.Memory.buffer -> ArrayBuffer
WebAssemblyMemoryGetBuffer(const v8::FunctionCallbackInfo<v8::Value> & args)698 void WebAssemblyMemoryGetBuffer(
699     const v8::FunctionCallbackInfo<v8::Value>& args) {
700   v8::Isolate* isolate = args.GetIsolate();
701   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
702   HandleScope scope(isolate);
703   ErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer");
704   Local<Context> context = isolate->GetCurrentContext();
705   i::Handle<i::Context> i_context = Utils::OpenHandle(*context);
706   if (!BrandCheck(Utils::OpenHandle(*args.This()),
707                   i::Handle<i::Symbol>(i_context->wasm_memory_sym()), &thrower,
708                   "Receiver is not a WebAssembly.Memory")) {
709     return;
710   }
711   i::Handle<i::WasmMemoryObject> receiver =
712       i::Handle<i::WasmMemoryObject>::cast(Utils::OpenHandle(*args.This()));
713   i::Handle<i::Object> buffer(receiver->buffer(), i_isolate);
714   DCHECK(buffer->IsJSArrayBuffer());
715   v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
716   return_value.Set(Utils::ToLocal(buffer));
717 }
718 }  // namespace
719 
720 // TODO(titzer): we use the API to create the function template because the
721 // internal guts are too ugly to replicate here.
NewTemplate(i::Isolate * i_isolate,FunctionCallback func)722 static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
723                                                       FunctionCallback func) {
724   Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
725   Local<FunctionTemplate> templ = FunctionTemplate::New(isolate, func);
726   templ->ReadOnlyPrototype();
727   return v8::Utils::OpenHandle(*templ);
728 }
729 
730 namespace internal {
731 
InstallFunc(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func,int length=0)732 Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
733                                const char* str, FunctionCallback func,
734                                int length = 0) {
735   Handle<String> name = v8_str(isolate, str);
736   Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
737   Handle<JSFunction> function =
738       ApiNatives::InstantiateFunction(temp).ToHandleChecked();
739   JSFunction::SetName(function, name, isolate->factory()->empty_string());
740   function->shared()->set_length(length);
741   PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
742   JSObject::AddProperty(object, name, function, attributes);
743   return function;
744 }
745 
InstallGetter(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func)746 Handle<JSFunction> InstallGetter(Isolate* isolate, Handle<JSObject> object,
747                                  const char* str, FunctionCallback func) {
748   Handle<String> name = v8_str(isolate, str);
749   Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
750   Handle<JSFunction> function =
751       ApiNatives::InstantiateFunction(temp).ToHandleChecked();
752   v8::PropertyAttribute attributes =
753       static_cast<v8::PropertyAttribute>(v8::DontEnum);
754   Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
755                                               Utils::ToLocal(function),
756                                               Local<Function>(), attributes);
757   return function;
758 }
759 
Install(Isolate * isolate)760 void WasmJs::Install(Isolate* isolate) {
761   Handle<JSGlobalObject> global = isolate->global_object();
762   Handle<Context> context(global->native_context(), isolate);
763   // TODO(titzer): once FLAG_expose_wasm is gone, this should become a DCHECK.
764   if (context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) return;
765 
766   // Install Maps.
767 
768   // TODO(titzer): Also make one for strict mode functions?
769   Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
770 
771   InstanceType instance_type = prev_map->instance_type();
772   int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
773   CHECK_EQ(0, internal_fields);
774   int pre_allocated =
775       prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
776   int instance_size = 0;
777   int in_object_properties = 0;
778   int wasm_internal_fields = internal_fields + 1  // module instance object
779       + 1                  // function arity
780       + 1;                 // function signature
781   JSFunction::CalculateInstanceSizeHelper(instance_type, wasm_internal_fields,
782                                           0, &instance_size,
783                                           &in_object_properties);
784 
785   int unused_property_fields = in_object_properties - pre_allocated;
786   Handle<Map> map = Map::CopyInitialMap(
787       prev_map, instance_size, in_object_properties, unused_property_fields);
788 
789   context->set_wasm_function_map(*map);
790 
791   // Install symbols.
792 
793   Factory* factory = isolate->factory();
794   // Create private symbols.
795   Handle<Symbol> module_sym = factory->NewPrivateSymbol();
796   context->set_wasm_module_sym(*module_sym);
797 
798   Handle<Symbol> instance_sym = factory->NewPrivateSymbol();
799   context->set_wasm_instance_sym(*instance_sym);
800 
801   Handle<Symbol> table_sym = factory->NewPrivateSymbol();
802   context->set_wasm_table_sym(*table_sym);
803 
804   Handle<Symbol> memory_sym = factory->NewPrivateSymbol();
805   context->set_wasm_memory_sym(*memory_sym);
806 
807   // Install the JS API.
808 
809   // Setup WebAssembly
810   Handle<String> name = v8_str(isolate, "WebAssembly");
811   Handle<JSFunction> cons = factory->NewFunction(name);
812   JSFunction::SetInstancePrototype(
813       cons, Handle<Object>(context->initial_object_prototype(), isolate));
814   cons->shared()->set_instance_class_name(*name);
815   Handle<JSObject> webassembly = factory->NewJSObject(cons, TENURED);
816   PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
817   JSObject::AddProperty(global, name, webassembly, attributes);
818   PropertyAttributes ro_attributes =
819       static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
820   JSObject::AddProperty(webassembly, factory->to_string_tag_symbol(),
821                         v8_str(isolate, "WebAssembly"), ro_attributes);
822   InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1);
823   InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
824   InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1);
825 
826   // Setup Module
827   Handle<JSFunction> module_constructor =
828       InstallFunc(isolate, webassembly, "Module", WebAssemblyModule, 1);
829   context->set_wasm_module_constructor(*module_constructor);
830   Handle<JSObject> module_proto =
831       factory->NewJSObject(module_constructor, TENURED);
832   i::Handle<i::Map> module_map = isolate->factory()->NewMap(
833       i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
834                              WasmModuleObject::kFieldCount * i::kPointerSize);
835   JSFunction::SetInitialMap(module_constructor, module_map, module_proto);
836   InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports,
837               1);
838   InstallFunc(isolate, module_constructor, "exports", WebAssemblyModuleExports,
839               1);
840   InstallFunc(isolate, module_constructor, "customSections",
841               WebAssemblyModuleCustomSections, 2);
842   JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(),
843                         module_constructor, DONT_ENUM);
844   JSObject::AddProperty(module_proto, factory->to_string_tag_symbol(),
845                         v8_str(isolate, "WebAssembly.Module"), ro_attributes);
846 
847   // Setup Instance
848   Handle<JSFunction> instance_constructor =
849       InstallFunc(isolate, webassembly, "Instance", WebAssemblyInstance, 1);
850   context->set_wasm_instance_constructor(*instance_constructor);
851   Handle<JSObject> instance_proto =
852       factory->NewJSObject(instance_constructor, TENURED);
853   i::Handle<i::Map> instance_map = isolate->factory()->NewMap(
854       i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
855                              WasmInstanceObject::kFieldCount * i::kPointerSize);
856   JSFunction::SetInitialMap(instance_constructor, instance_map, instance_proto);
857   JSObject::AddProperty(instance_proto,
858                         isolate->factory()->constructor_string(),
859                         instance_constructor, DONT_ENUM);
860   JSObject::AddProperty(instance_proto, factory->to_string_tag_symbol(),
861                         v8_str(isolate, "WebAssembly.Instance"), ro_attributes);
862 
863   // Setup Table
864   Handle<JSFunction> table_constructor =
865       InstallFunc(isolate, webassembly, "Table", WebAssemblyTable, 1);
866   context->set_wasm_table_constructor(*table_constructor);
867   Handle<JSObject> table_proto =
868       factory->NewJSObject(table_constructor, TENURED);
869   i::Handle<i::Map> table_map = isolate->factory()->NewMap(
870       i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
871                              WasmTableObject::kFieldCount * i::kPointerSize);
872   JSFunction::SetInitialMap(table_constructor, table_map, table_proto);
873   JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(),
874                         table_constructor, DONT_ENUM);
875   InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
876   InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1);
877   InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1);
878   InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 2);
879   JSObject::AddProperty(table_proto, factory->to_string_tag_symbol(),
880                         v8_str(isolate, "WebAssembly.Table"), ro_attributes);
881 
882   // Setup Memory
883   Handle<JSFunction> memory_constructor =
884       InstallFunc(isolate, webassembly, "Memory", WebAssemblyMemory, 1);
885   context->set_wasm_memory_constructor(*memory_constructor);
886   Handle<JSObject> memory_proto =
887       factory->NewJSObject(memory_constructor, TENURED);
888   i::Handle<i::Map> memory_map = isolate->factory()->NewMap(
889       i::JS_API_OBJECT_TYPE, i::JSObject::kHeaderSize +
890                              WasmMemoryObject::kFieldCount * i::kPointerSize);
891   JSFunction::SetInitialMap(memory_constructor, memory_map, memory_proto);
892   JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(),
893                         memory_constructor, DONT_ENUM);
894   InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1);
895   InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
896   JSObject::AddProperty(memory_proto, factory->to_string_tag_symbol(),
897                         v8_str(isolate, "WebAssembly.Memory"), ro_attributes);
898 
899   // Setup errors
900   attributes = static_cast<PropertyAttributes>(DONT_ENUM);
901   Handle<JSFunction> compile_error(
902       isolate->native_context()->wasm_compile_error_function());
903   JSObject::AddProperty(webassembly, isolate->factory()->CompileError_string(),
904                         compile_error, attributes);
905   Handle<JSFunction> link_error(
906       isolate->native_context()->wasm_link_error_function());
907   JSObject::AddProperty(webassembly, isolate->factory()->LinkError_string(),
908                         link_error, attributes);
909   Handle<JSFunction> runtime_error(
910       isolate->native_context()->wasm_runtime_error_function());
911   JSObject::AddProperty(webassembly, isolate->factory()->RuntimeError_string(),
912                         runtime_error, attributes);
913 }
914 
IsWasmMemoryObject(Isolate * isolate,Handle<Object> value)915 bool WasmJs::IsWasmMemoryObject(Isolate* isolate, Handle<Object> value) {
916   i::Handle<i::Symbol> symbol(isolate->context()->wasm_memory_sym(), isolate);
917   return HasBrand(value, symbol);
918 }
919 
IsWasmTableObject(Isolate * isolate,Handle<Object> value)920 bool WasmJs::IsWasmTableObject(Isolate* isolate, Handle<Object> value) {
921   i::Handle<i::Symbol> symbol(isolate->context()->wasm_table_sym(), isolate);
922   return HasBrand(value, symbol);
923 }
924 }  // namespace internal
925 }  // namespace v8
926