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