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-js.h"
6
7 #include <cinttypes>
8 #include <cstring>
9
10 #include "include/v8-function.h"
11 #include "include/v8-wasm.h"
12 #include "src/api/api-inl.h"
13 #include "src/api/api-natives.h"
14 #include "src/ast/ast.h"
15 #include "src/base/logging.h"
16 #include "src/base/overflowing-math.h"
17 #include "src/base/platform/wrappers.h"
18 #include "src/common/assert-scope.h"
19 #include "src/execution/execution.h"
20 #include "src/execution/frames-inl.h"
21 #include "src/execution/isolate.h"
22 #include "src/handles/global-handles-inl.h"
23 #include "src/handles/handles.h"
24 #include "src/heap/factory.h"
25 #include "src/init/v8.h"
26 #include "src/objects/fixed-array.h"
27 #include "src/objects/instance-type.h"
28 #include "src/objects/js-function.h"
29 #include "src/objects/js-promise-inl.h"
30 #include "src/objects/managed-inl.h"
31 #include "src/objects/objects-inl.h"
32 #include "src/objects/shared-function-info.h"
33 #include "src/objects/templates.h"
34 #include "src/parsing/parse-info.h"
35 #include "src/tasks/task-utils.h"
36 #include "src/trap-handler/trap-handler.h"
37 #include "src/wasm/function-compiler.h"
38 #include "src/wasm/streaming-decoder.h"
39 #include "src/wasm/value-type.h"
40 #include "src/wasm/wasm-debug.h"
41 #include "src/wasm/wasm-engine.h"
42 #include "src/wasm/wasm-limits.h"
43 #include "src/wasm/wasm-objects-inl.h"
44 #include "src/wasm/wasm-serialization.h"
45 #include "src/wasm/wasm-value.h"
46
47 using v8::internal::wasm::ErrorThrower;
48 using v8::internal::wasm::ScheduledErrorThrower;
49
50 namespace v8 {
51
52 class WasmStreaming::WasmStreamingImpl {
53 public:
WasmStreamingImpl(Isolate * isolate,const char * api_method_name,std::shared_ptr<internal::wasm::CompilationResultResolver> resolver)54 WasmStreamingImpl(
55 Isolate* isolate, const char* api_method_name,
56 std::shared_ptr<internal::wasm::CompilationResultResolver> resolver)
57 : isolate_(isolate), resolver_(std::move(resolver)) {
58 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_);
59 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
60 streaming_decoder_ = i::wasm::GetWasmEngine()->StartStreamingCompilation(
61 i_isolate, enabled_features, handle(i_isolate->context(), i_isolate),
62 api_method_name, resolver_);
63 }
64
OnBytesReceived(const uint8_t * bytes,size_t size)65 void OnBytesReceived(const uint8_t* bytes, size_t size) {
66 streaming_decoder_->OnBytesReceived(base::VectorOf(bytes, size));
67 }
Finish(bool can_use_compiled_module)68 void Finish(bool can_use_compiled_module) {
69 streaming_decoder_->Finish(can_use_compiled_module);
70 }
71
Abort(MaybeLocal<Value> exception)72 void Abort(MaybeLocal<Value> exception) {
73 i::HandleScope scope(reinterpret_cast<i::Isolate*>(isolate_));
74 streaming_decoder_->Abort();
75
76 // If no exception value is provided, we do not reject the promise. This can
77 // happen when streaming compilation gets aborted when no script execution
78 // is allowed anymore, e.g. when a browser tab gets refreshed.
79 if (exception.IsEmpty()) return;
80
81 resolver_->OnCompilationFailed(
82 Utils::OpenHandle(*exception.ToLocalChecked()));
83 }
84
SetCompiledModuleBytes(const uint8_t * bytes,size_t size)85 bool SetCompiledModuleBytes(const uint8_t* bytes, size_t size) {
86 if (!i::wasm::IsSupportedVersion({bytes, size})) return false;
87 return streaming_decoder_->SetCompiledModuleBytes({bytes, size});
88 }
89
SetClient(std::shared_ptr<Client> client)90 void SetClient(std::shared_ptr<Client> client) {
91 streaming_decoder_->SetModuleCompiledCallback(
92 [client, streaming_decoder = streaming_decoder_](
93 const std::shared_ptr<i::wasm::NativeModule>& native_module) {
94 base::Vector<const char> url = streaming_decoder->url();
95 auto compiled_wasm_module =
96 CompiledWasmModule(native_module, url.begin(), url.size());
97 client->OnModuleCompiled(compiled_wasm_module);
98 });
99 }
100
SetUrl(base::Vector<const char> url)101 void SetUrl(base::Vector<const char> url) { streaming_decoder_->SetUrl(url); }
102
103 private:
104 Isolate* const isolate_;
105 std::shared_ptr<internal::wasm::StreamingDecoder> streaming_decoder_;
106 std::shared_ptr<internal::wasm::CompilationResultResolver> resolver_;
107 };
108
WasmStreaming(std::unique_ptr<WasmStreamingImpl> impl)109 WasmStreaming::WasmStreaming(std::unique_ptr<WasmStreamingImpl> impl)
110 : impl_(std::move(impl)) {
111 TRACE_EVENT0("v8.wasm", "wasm.InitializeStreaming");
112 }
113
114 // The destructor is defined here because we have a unique_ptr with forward
115 // declaration.
116 WasmStreaming::~WasmStreaming() = default;
117
OnBytesReceived(const uint8_t * bytes,size_t size)118 void WasmStreaming::OnBytesReceived(const uint8_t* bytes, size_t size) {
119 TRACE_EVENT1("v8.wasm", "wasm.OnBytesReceived", "bytes", size);
120 impl_->OnBytesReceived(bytes, size);
121 }
122
Finish(bool can_use_compiled_module)123 void WasmStreaming::Finish(bool can_use_compiled_module) {
124 TRACE_EVENT0("v8.wasm", "wasm.FinishStreaming");
125 impl_->Finish(can_use_compiled_module);
126 }
127
Abort(MaybeLocal<Value> exception)128 void WasmStreaming::Abort(MaybeLocal<Value> exception) {
129 TRACE_EVENT0("v8.wasm", "wasm.AbortStreaming");
130 impl_->Abort(exception);
131 }
132
SetCompiledModuleBytes(const uint8_t * bytes,size_t size)133 bool WasmStreaming::SetCompiledModuleBytes(const uint8_t* bytes, size_t size) {
134 TRACE_EVENT0("v8.wasm", "wasm.SetCompiledModuleBytes");
135 return impl_->SetCompiledModuleBytes(bytes, size);
136 }
137
SetClient(std::shared_ptr<Client> client)138 void WasmStreaming::SetClient(std::shared_ptr<Client> client) {
139 TRACE_EVENT0("v8.wasm", "wasm.WasmStreaming.SetClient");
140 impl_->SetClient(client);
141 }
142
SetUrl(const char * url,size_t length)143 void WasmStreaming::SetUrl(const char* url, size_t length) {
144 DCHECK_EQ('\0', url[length]); // {url} is null-terminated.
145 TRACE_EVENT1("v8.wasm", "wasm.SetUrl", "url", url);
146 impl_->SetUrl(base::VectorOf(url, length));
147 }
148
149 // static
Unpack(Isolate * isolate,Local<Value> value)150 std::shared_ptr<WasmStreaming> WasmStreaming::Unpack(Isolate* isolate,
151 Local<Value> value) {
152 TRACE_EVENT0("v8.wasm", "wasm.WasmStreaming.Unpack");
153 i::HandleScope scope(reinterpret_cast<i::Isolate*>(isolate));
154 auto managed =
155 i::Handle<i::Managed<WasmStreaming>>::cast(Utils::OpenHandle(*value));
156 return managed->get();
157 }
158
159 namespace {
160
161 #define ASSIGN(type, var, expr) \
162 Local<type> var; \
163 do { \
164 if (!expr.ToLocal(&var)) { \
165 DCHECK(i_isolate->has_scheduled_exception()); \
166 return; \
167 } else { \
168 DCHECK(!i_isolate->has_scheduled_exception()); \
169 } \
170 } while (false)
171
v8_str(i::Isolate * isolate,const char * str)172 i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
173 return isolate->factory()->NewStringFromAsciiChecked(str);
174 }
v8_str(Isolate * isolate,const char * str)175 Local<String> v8_str(Isolate* isolate, const char* str) {
176 return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str));
177 }
178
179 #define GET_FIRST_ARGUMENT_AS(Type) \
180 i::MaybeHandle<i::Wasm##Type##Object> GetFirstArgumentAs##Type( \
181 const v8::FunctionCallbackInfo<v8::Value>& args, \
182 ErrorThrower* thrower) { \
183 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]); \
184 if (!arg0->IsWasm##Type##Object()) { \
185 thrower->TypeError("Argument 0 must be a WebAssembly." #Type); \
186 return {}; \
187 } \
188 return i::Handle<i::Wasm##Type##Object>::cast(arg0); \
189 }
190
191 GET_FIRST_ARGUMENT_AS(Module)
GET_FIRST_ARGUMENT_AS(Tag)192 GET_FIRST_ARGUMENT_AS(Tag)
193
194 #undef GET_FIRST_ARGUMENT_AS
195
196 i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
197 const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower,
198 bool* is_shared) {
199 const uint8_t* start = nullptr;
200 size_t length = 0;
201 v8::Local<v8::Value> source = args[0];
202 if (source->IsArrayBuffer()) {
203 // A raw array buffer was passed.
204 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
205 auto backing_store = buffer->GetBackingStore();
206
207 start = reinterpret_cast<const uint8_t*>(backing_store->Data());
208 length = backing_store->ByteLength();
209 *is_shared = buffer->IsSharedArrayBuffer();
210 } else if (source->IsTypedArray()) {
211 // A TypedArray was passed.
212 Local<TypedArray> array = Local<TypedArray>::Cast(source);
213 Local<ArrayBuffer> buffer = array->Buffer();
214
215 auto backing_store = buffer->GetBackingStore();
216
217 start = reinterpret_cast<const uint8_t*>(backing_store->Data()) +
218 array->ByteOffset();
219 length = array->ByteLength();
220 *is_shared = buffer->IsSharedArrayBuffer();
221 } else {
222 thrower->TypeError("Argument 0 must be a buffer source");
223 }
224 DCHECK_IMPLIES(length, start != nullptr);
225 if (length == 0) {
226 thrower->CompileError("BufferSource argument is empty");
227 }
228 size_t max_length = i::wasm::max_module_size();
229 if (length > max_length) {
230 thrower->RangeError("buffer source exceeds maximum size of %zu (is %zu)",
231 max_length, length);
232 }
233 if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr);
234 return i::wasm::ModuleWireBytes(start, start + length);
235 }
236
GetFirstArgumentAsJSFunction(const v8::FunctionCallbackInfo<v8::Value> & args,ErrorThrower * thrower)237 i::MaybeHandle<i::JSFunction> GetFirstArgumentAsJSFunction(
238 const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
239 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
240 if (!arg0->IsJSFunction()) {
241 thrower->TypeError("Argument 0 must be a function");
242 return {};
243 }
244 return i::Handle<i::JSFunction>::cast(arg0);
245 }
246
GetValueAsImports(Local<Value> arg,ErrorThrower * thrower)247 i::MaybeHandle<i::JSReceiver> GetValueAsImports(Local<Value> arg,
248 ErrorThrower* thrower) {
249 if (arg->IsUndefined()) return {};
250
251 if (!arg->IsObject()) {
252 thrower->TypeError("Argument 1 must be an object");
253 return {};
254 }
255 Local<Object> obj = Local<Object>::Cast(arg);
256 return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
257 }
258
259 namespace {
260 // This class resolves the result of WebAssembly.compile. It just places the
261 // compilation result in the supplied {promise}.
262 class AsyncCompilationResolver : public i::wasm::CompilationResultResolver {
263 public:
AsyncCompilationResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise)264 AsyncCompilationResolver(i::Isolate* isolate, i::Handle<i::JSPromise> promise)
265 : promise_(isolate->global_handles()->Create(*promise)) {
266 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
267 kGlobalPromiseHandle);
268 }
269
~AsyncCompilationResolver()270 ~AsyncCompilationResolver() override {
271 i::GlobalHandles::Destroy(promise_.location());
272 }
273
OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result)274 void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result) override {
275 if (finished_) return;
276 finished_ = true;
277 i::MaybeHandle<i::Object> promise_result =
278 i::JSPromise::Resolve(promise_, result);
279 CHECK_EQ(promise_result.is_null(),
280 promise_->GetIsolate()->has_pending_exception());
281 }
282
OnCompilationFailed(i::Handle<i::Object> error_reason)283 void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
284 if (finished_) return;
285 finished_ = true;
286 i::MaybeHandle<i::Object> promise_result =
287 i::JSPromise::Reject(promise_, error_reason);
288 CHECK_EQ(promise_result.is_null(),
289 promise_->GetIsolate()->has_pending_exception());
290 }
291
292 private:
293 static constexpr char kGlobalPromiseHandle[] =
294 "AsyncCompilationResolver::promise_";
295 bool finished_ = false;
296 i::Handle<i::JSPromise> promise_;
297 };
298
299 constexpr char AsyncCompilationResolver::kGlobalPromiseHandle[];
300
301 // This class resolves the result of WebAssembly.instantiate(module, imports).
302 // It just places the instantiation result in the supplied {promise}.
303 class InstantiateModuleResultResolver
304 : public i::wasm::InstantiationResultResolver {
305 public:
InstantiateModuleResultResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise)306 InstantiateModuleResultResolver(i::Isolate* isolate,
307 i::Handle<i::JSPromise> promise)
308 : promise_(isolate->global_handles()->Create(*promise)) {
309 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
310 kGlobalPromiseHandle);
311 }
312
~InstantiateModuleResultResolver()313 ~InstantiateModuleResultResolver() override {
314 i::GlobalHandles::Destroy(promise_.location());
315 }
316
OnInstantiationSucceeded(i::Handle<i::WasmInstanceObject> instance)317 void OnInstantiationSucceeded(
318 i::Handle<i::WasmInstanceObject> instance) override {
319 i::MaybeHandle<i::Object> promise_result =
320 i::JSPromise::Resolve(promise_, instance);
321 CHECK_EQ(promise_result.is_null(),
322 promise_->GetIsolate()->has_pending_exception());
323 }
324
OnInstantiationFailed(i::Handle<i::Object> error_reason)325 void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
326 i::MaybeHandle<i::Object> promise_result =
327 i::JSPromise::Reject(promise_, error_reason);
328 CHECK_EQ(promise_result.is_null(),
329 promise_->GetIsolate()->has_pending_exception());
330 }
331
332 private:
333 static constexpr char kGlobalPromiseHandle[] =
334 "InstantiateModuleResultResolver::promise_";
335 i::Handle<i::JSPromise> promise_;
336 };
337
338 constexpr char InstantiateModuleResultResolver::kGlobalPromiseHandle[];
339
340 // This class resolves the result of WebAssembly.instantiate(bytes, imports).
341 // For that it creates a new {JSObject} which contains both the provided
342 // {WasmModuleObject} and the resulting {WebAssemblyInstanceObject} itself.
343 class InstantiateBytesResultResolver
344 : public i::wasm::InstantiationResultResolver {
345 public:
InstantiateBytesResultResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise,i::Handle<i::WasmModuleObject> module)346 InstantiateBytesResultResolver(i::Isolate* isolate,
347 i::Handle<i::JSPromise> promise,
348 i::Handle<i::WasmModuleObject> module)
349 : isolate_(isolate),
350 promise_(isolate_->global_handles()->Create(*promise)),
351 module_(isolate_->global_handles()->Create(*module)) {
352 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
353 kGlobalPromiseHandle);
354 i::GlobalHandles::AnnotateStrongRetainer(module_.location(),
355 kGlobalModuleHandle);
356 }
357
~InstantiateBytesResultResolver()358 ~InstantiateBytesResultResolver() override {
359 i::GlobalHandles::Destroy(promise_.location());
360 i::GlobalHandles::Destroy(module_.location());
361 }
362
OnInstantiationSucceeded(i::Handle<i::WasmInstanceObject> instance)363 void OnInstantiationSucceeded(
364 i::Handle<i::WasmInstanceObject> instance) override {
365 // The result is a JSObject with 2 fields which contain the
366 // WasmInstanceObject and the WasmModuleObject.
367 i::Handle<i::JSObject> result =
368 isolate_->factory()->NewJSObject(isolate_->object_function());
369
370 i::Handle<i::String> instance_name =
371 isolate_->factory()->NewStringFromStaticChars("instance");
372
373 i::Handle<i::String> module_name =
374 isolate_->factory()->NewStringFromStaticChars("module");
375
376 i::JSObject::AddProperty(isolate_, result, instance_name, instance,
377 i::NONE);
378 i::JSObject::AddProperty(isolate_, result, module_name, module_, i::NONE);
379
380 i::MaybeHandle<i::Object> promise_result =
381 i::JSPromise::Resolve(promise_, result);
382 CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
383 }
384
OnInstantiationFailed(i::Handle<i::Object> error_reason)385 void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
386 i::MaybeHandle<i::Object> promise_result =
387 i::JSPromise::Reject(promise_, error_reason);
388 CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
389 }
390
391 private:
392 static constexpr char kGlobalPromiseHandle[] =
393 "InstantiateBytesResultResolver::promise_";
394 static constexpr char kGlobalModuleHandle[] =
395 "InstantiateBytesResultResolver::module_";
396 i::Isolate* isolate_;
397 i::Handle<i::JSPromise> promise_;
398 i::Handle<i::WasmModuleObject> module_;
399 };
400
401 constexpr char InstantiateBytesResultResolver::kGlobalPromiseHandle[];
402 constexpr char InstantiateBytesResultResolver::kGlobalModuleHandle[];
403
404 // This class is the {CompilationResultResolver} for
405 // WebAssembly.instantiate(bytes, imports). When compilation finishes,
406 // {AsyncInstantiate} is started on the compilation result.
407 class AsyncInstantiateCompileResultResolver
408 : public i::wasm::CompilationResultResolver {
409 public:
AsyncInstantiateCompileResultResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise,i::MaybeHandle<i::JSReceiver> maybe_imports)410 AsyncInstantiateCompileResultResolver(
411 i::Isolate* isolate, i::Handle<i::JSPromise> promise,
412 i::MaybeHandle<i::JSReceiver> maybe_imports)
413 : isolate_(isolate),
414 promise_(isolate_->global_handles()->Create(*promise)),
415 maybe_imports_(maybe_imports.is_null()
416 ? maybe_imports
417 : isolate_->global_handles()->Create(
418 *maybe_imports.ToHandleChecked())) {
419 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
420 kGlobalPromiseHandle);
421 if (!maybe_imports_.is_null()) {
422 i::GlobalHandles::AnnotateStrongRetainer(
423 maybe_imports_.ToHandleChecked().location(), kGlobalImportsHandle);
424 }
425 }
426
~AsyncInstantiateCompileResultResolver()427 ~AsyncInstantiateCompileResultResolver() override {
428 i::GlobalHandles::Destroy(promise_.location());
429 if (!maybe_imports_.is_null()) {
430 i::GlobalHandles::Destroy(maybe_imports_.ToHandleChecked().location());
431 }
432 }
433
OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result)434 void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result) override {
435 if (finished_) return;
436 finished_ = true;
437 i::wasm::GetWasmEngine()->AsyncInstantiate(
438 isolate_,
439 std::make_unique<InstantiateBytesResultResolver>(isolate_, promise_,
440 result),
441 result, maybe_imports_);
442 }
443
OnCompilationFailed(i::Handle<i::Object> error_reason)444 void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
445 if (finished_) return;
446 finished_ = true;
447 i::MaybeHandle<i::Object> promise_result =
448 i::JSPromise::Reject(promise_, error_reason);
449 CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
450 }
451
452 private:
453 static constexpr char kGlobalPromiseHandle[] =
454 "AsyncInstantiateCompileResultResolver::promise_";
455 static constexpr char kGlobalImportsHandle[] =
456 "AsyncInstantiateCompileResultResolver::module_";
457 bool finished_ = false;
458 i::Isolate* isolate_;
459 i::Handle<i::JSPromise> promise_;
460 i::MaybeHandle<i::JSReceiver> maybe_imports_;
461 };
462
463 constexpr char AsyncInstantiateCompileResultResolver::kGlobalPromiseHandle[];
464 constexpr char AsyncInstantiateCompileResultResolver::kGlobalImportsHandle[];
465
ToString(const char * name)466 std::string ToString(const char* name) { return std::string(name); }
467
ToString(const i::Handle<i::String> name)468 std::string ToString(const i::Handle<i::String> name) {
469 return std::string("Property '") + name->ToCString().get() + "'";
470 }
471
472 // Web IDL: '[EnforceRange] unsigned long'
473 // Previously called ToNonWrappingUint32 in the draft WebAssembly JS spec.
474 // https://heycam.github.io/webidl/#EnforceRange
475 template <typename T>
EnforceUint32(T argument_name,Local<v8::Value> v,Local<Context> context,ErrorThrower * thrower,uint32_t * res)476 bool EnforceUint32(T argument_name, Local<v8::Value> v, Local<Context> context,
477 ErrorThrower* thrower, uint32_t* res) {
478 double double_number;
479
480 if (!v->NumberValue(context).To(&double_number)) {
481 thrower->TypeError("%s must be convertible to a number",
482 ToString(argument_name).c_str());
483 return false;
484 }
485 if (!std::isfinite(double_number)) {
486 thrower->TypeError("%s must be convertible to a valid number",
487 ToString(argument_name).c_str());
488 return false;
489 }
490 if (double_number < 0) {
491 thrower->TypeError("%s must be non-negative",
492 ToString(argument_name).c_str());
493 return false;
494 }
495 if (double_number > std::numeric_limits<uint32_t>::max()) {
496 thrower->TypeError("%s must be in the unsigned long range",
497 ToString(argument_name).c_str());
498 return false;
499 }
500
501 *res = static_cast<uint32_t>(double_number);
502 return true;
503 }
504 } // namespace
505
506 // WebAssembly.compile(bytes) -> Promise
WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value> & args)507 void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
508 constexpr const char* kAPIMethodName = "WebAssembly.compile()";
509 v8::Isolate* isolate = args.GetIsolate();
510 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
511
512 HandleScope scope(isolate);
513 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
514
515 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
516 thrower.CompileError("Wasm code generation disallowed by embedder");
517 }
518
519 Local<Context> context = isolate->GetCurrentContext();
520 ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
521 Local<Promise> promise = promise_resolver->GetPromise();
522 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
523 return_value.Set(promise);
524
525 std::shared_ptr<i::wasm::CompilationResultResolver> resolver(
526 new AsyncCompilationResolver(i_isolate, Utils::OpenHandle(*promise)));
527
528 bool is_shared = false;
529 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
530 if (thrower.error()) {
531 resolver->OnCompilationFailed(thrower.Reify());
532 return;
533 }
534 // Asynchronous compilation handles copying wire bytes if necessary.
535 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
536 i::wasm::GetWasmEngine()->AsyncCompile(i_isolate, enabled_features,
537 std::move(resolver), bytes, is_shared,
538 kAPIMethodName);
539 }
540
WasmStreamingCallbackForTesting(const v8::FunctionCallbackInfo<v8::Value> & args)541 void WasmStreamingCallbackForTesting(
542 const v8::FunctionCallbackInfo<v8::Value>& args) {
543 v8::Isolate* isolate = args.GetIsolate();
544 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
545
546 HandleScope scope(isolate);
547 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.compile()");
548
549 std::shared_ptr<v8::WasmStreaming> streaming =
550 v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
551
552 bool is_shared = false;
553 i::wasm::ModuleWireBytes bytes =
554 GetFirstArgumentAsBytes(args, &thrower, &is_shared);
555 if (thrower.error()) {
556 streaming->Abort(Utils::ToLocal(thrower.Reify()));
557 return;
558 }
559 streaming->OnBytesReceived(bytes.start(), bytes.length());
560 streaming->Finish();
561 CHECK(!thrower.error());
562 }
563
WasmStreamingPromiseFailedCallback(const v8::FunctionCallbackInfo<v8::Value> & args)564 void WasmStreamingPromiseFailedCallback(
565 const v8::FunctionCallbackInfo<v8::Value>& args) {
566 std::shared_ptr<v8::WasmStreaming> streaming =
567 v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
568 streaming->Abort(args[0]);
569 }
570
571 // WebAssembly.compileStreaming(Response | Promise<Response>)
572 // -> Promise<WebAssembly.Module>
WebAssemblyCompileStreaming(const v8::FunctionCallbackInfo<v8::Value> & args)573 void WebAssemblyCompileStreaming(
574 const v8::FunctionCallbackInfo<v8::Value>& args) {
575 v8::Isolate* isolate = args.GetIsolate();
576 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
577 HandleScope scope(isolate);
578 const char* const kAPIMethodName = "WebAssembly.compileStreaming()";
579 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
580 Local<Context> context = isolate->GetCurrentContext();
581
582 // Create and assign the return value of this function.
583 ASSIGN(Promise::Resolver, result_resolver, Promise::Resolver::New(context));
584 Local<Promise> promise = result_resolver->GetPromise();
585 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
586 return_value.Set(promise);
587
588 // Prepare the CompilationResultResolver for the compilation.
589 auto resolver = std::make_shared<AsyncCompilationResolver>(
590 i_isolate, Utils::OpenHandle(*promise));
591
592 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
593 thrower.CompileError("Wasm code generation disallowed by embedder");
594 resolver->OnCompilationFailed(thrower.Reify());
595 return;
596 }
597
598 // Allocate the streaming decoder in a Managed so we can pass it to the
599 // embedder.
600 i::Handle<i::Managed<WasmStreaming>> data =
601 i::Managed<WasmStreaming>::Allocate(
602 i_isolate, 0,
603 std::make_unique<WasmStreaming::WasmStreamingImpl>(
604 isolate, kAPIMethodName, resolver));
605
606 DCHECK_NOT_NULL(i_isolate->wasm_streaming_callback());
607 ASSIGN(
608 v8::Function, compile_callback,
609 v8::Function::New(context, i_isolate->wasm_streaming_callback(),
610 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
611 ASSIGN(
612 v8::Function, reject_callback,
613 v8::Function::New(context, WasmStreamingPromiseFailedCallback,
614 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
615
616 // The parameter may be of type {Response} or of type {Promise<Response>}.
617 // Treat either case of parameter as Promise.resolve(parameter)
618 // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments
619
620 // Ending with:
621 // return Promise.resolve(parameter).then(compile_callback);
622 ASSIGN(Promise::Resolver, input_resolver, Promise::Resolver::New(context));
623 if (!input_resolver->Resolve(context, args[0]).IsJust()) return;
624
625 // We do not have any use of the result here. The {compile_callback} will
626 // start streaming compilation, which will eventually resolve the promise we
627 // set as result value.
628 USE(input_resolver->GetPromise()->Then(context, compile_callback,
629 reject_callback));
630 }
631
632 // WebAssembly.validate(bytes) -> bool
WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value> & args)633 void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
634 v8::Isolate* isolate = args.GetIsolate();
635 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
636 HandleScope scope(isolate);
637 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.validate()");
638
639 bool is_shared = false;
640 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
641
642 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
643
644 if (thrower.error()) {
645 if (thrower.wasm_error()) thrower.Reset(); // Clear error.
646 return_value.Set(v8::False(isolate));
647 return;
648 }
649
650 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
651 bool validated = false;
652 if (is_shared) {
653 // Make a copy of the wire bytes to avoid concurrent modification.
654 std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
655 memcpy(copy.get(), bytes.start(), bytes.length());
656 i::wasm::ModuleWireBytes bytes_copy(copy.get(),
657 copy.get() + bytes.length());
658 validated = i::wasm::GetWasmEngine()->SyncValidate(
659 i_isolate, enabled_features, bytes_copy);
660 } else {
661 // The wire bytes are not shared, OK to use them directly.
662 validated = i::wasm::GetWasmEngine()->SyncValidate(i_isolate,
663 enabled_features, bytes);
664 }
665
666 return_value.Set(Boolean::New(isolate, validated));
667 }
668
669 namespace {
TransferPrototype(i::Isolate * isolate,i::Handle<i::JSObject> destination,i::Handle<i::JSReceiver> source)670 bool TransferPrototype(i::Isolate* isolate, i::Handle<i::JSObject> destination,
671 i::Handle<i::JSReceiver> source) {
672 i::MaybeHandle<i::HeapObject> maybe_prototype =
673 i::JSObject::GetPrototype(isolate, source);
674 i::Handle<i::HeapObject> prototype;
675 if (maybe_prototype.ToHandle(&prototype)) {
676 Maybe<bool> result = i::JSObject::SetPrototype(
677 isolate, destination, prototype,
678 /*from_javascript=*/false, internal::kThrowOnError);
679 if (!result.FromJust()) {
680 DCHECK(isolate->has_pending_exception());
681 return false;
682 }
683 }
684 return true;
685 }
686 } // namespace
687
688 // new WebAssembly.Module(bytes) -> WebAssembly.Module
WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value> & args)689 void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
690 v8::Isolate* isolate = args.GetIsolate();
691 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
692 if (i_isolate->wasm_module_callback()(args)) return;
693
694 HandleScope scope(isolate);
695 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module()");
696
697 if (!args.IsConstructCall()) {
698 thrower.TypeError("WebAssembly.Module must be invoked with 'new'");
699 return;
700 }
701 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
702 thrower.CompileError("Wasm code generation disallowed by embedder");
703 return;
704 }
705
706 bool is_shared = false;
707 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
708
709 if (thrower.error()) {
710 return;
711 }
712 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
713 i::MaybeHandle<i::WasmModuleObject> maybe_module_obj;
714 if (is_shared) {
715 // Make a copy of the wire bytes to avoid concurrent modification.
716 std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
717 memcpy(copy.get(), bytes.start(), bytes.length());
718 i::wasm::ModuleWireBytes bytes_copy(copy.get(),
719 copy.get() + bytes.length());
720 maybe_module_obj = i::wasm::GetWasmEngine()->SyncCompile(
721 i_isolate, enabled_features, &thrower, bytes_copy);
722 } else {
723 // The wire bytes are not shared, OK to use them directly.
724 maybe_module_obj = i::wasm::GetWasmEngine()->SyncCompile(
725 i_isolate, enabled_features, &thrower, bytes);
726 }
727
728 i::Handle<i::WasmModuleObject> module_obj;
729 if (!maybe_module_obj.ToHandle(&module_obj)) return;
730
731 // The infrastructure for `new Foo` calls allocates an object, which is
732 // available here as {args.This()}. We're going to discard this object
733 // and use {module_obj} instead, but it does have the correct prototype,
734 // which we must harvest from it. This makes a difference when the JS
735 // constructor function wasn't {WebAssembly.Module} directly, but some
736 // subclass: {module_obj} has {WebAssembly.Module}'s prototype at this
737 // point, so we must overwrite that with the correct prototype for {Foo}.
738 if (!TransferPrototype(i_isolate, module_obj,
739 Utils::OpenHandle(*args.This()))) {
740 return;
741 }
742
743 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
744 return_value.Set(Utils::ToLocal(i::Handle<i::JSObject>::cast(module_obj)));
745 }
746
747 // WebAssembly.Module.imports(module) -> Array<Import>
WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value> & args)748 void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
749 HandleScope scope(args.GetIsolate());
750 v8::Isolate* isolate = args.GetIsolate();
751 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
752 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()");
753
754 auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
755 if (thrower.error()) return;
756 auto imports = i::wasm::GetImports(i_isolate, maybe_module.ToHandleChecked());
757 args.GetReturnValue().Set(Utils::ToLocal(imports));
758 }
759
760 // WebAssembly.Module.exports(module) -> Array<Export>
WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value> & args)761 void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
762 HandleScope scope(args.GetIsolate());
763 v8::Isolate* isolate = args.GetIsolate();
764 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
765 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()");
766
767 auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
768 if (thrower.error()) return;
769 auto exports = i::wasm::GetExports(i_isolate, maybe_module.ToHandleChecked());
770 args.GetReturnValue().Set(Utils::ToLocal(exports));
771 }
772
773 // WebAssembly.Module.customSections(module, name) -> Array<Section>
WebAssemblyModuleCustomSections(const v8::FunctionCallbackInfo<v8::Value> & args)774 void WebAssemblyModuleCustomSections(
775 const v8::FunctionCallbackInfo<v8::Value>& args) {
776 HandleScope scope(args.GetIsolate());
777 v8::Isolate* isolate = args.GetIsolate();
778 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
779 ScheduledErrorThrower thrower(i_isolate,
780 "WebAssembly.Module.customSections()");
781
782 auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
783 if (thrower.error()) return;
784
785 if (args[1]->IsUndefined()) {
786 thrower.TypeError("Argument 1 is required");
787 return;
788 }
789
790 i::MaybeHandle<i::Object> maybe_name =
791 i::Object::ToString(i_isolate, Utils::OpenHandle(*args[1]));
792 i::Handle<i::Object> name;
793 if (!maybe_name.ToHandle(&name)) return;
794 auto custom_sections =
795 i::wasm::GetCustomSections(i_isolate, maybe_module.ToHandleChecked(),
796 i::Handle<i::String>::cast(name), &thrower);
797 if (thrower.error()) return;
798 args.GetReturnValue().Set(Utils::ToLocal(custom_sections));
799 }
800
801 // new WebAssembly.Instance(module, imports) -> WebAssembly.Instance
WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value> & args)802 void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
803 Isolate* isolate = args.GetIsolate();
804 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
805 i_isolate->CountUsage(
806 v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
807
808 HandleScope scope(args.GetIsolate());
809 if (i_isolate->wasm_instance_callback()(args)) return;
810
811 i::MaybeHandle<i::JSObject> maybe_instance_obj;
812 {
813 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
814 if (!args.IsConstructCall()) {
815 thrower.TypeError("WebAssembly.Instance must be invoked with 'new'");
816 return;
817 }
818
819 i::MaybeHandle<i::WasmModuleObject> maybe_module =
820 GetFirstArgumentAsModule(args, &thrower);
821 if (thrower.error()) return;
822
823 i::Handle<i::WasmModuleObject> module_obj = maybe_module.ToHandleChecked();
824
825 i::MaybeHandle<i::JSReceiver> maybe_imports =
826 GetValueAsImports(args[1], &thrower);
827 if (thrower.error()) return;
828
829 maybe_instance_obj = i::wasm::GetWasmEngine()->SyncInstantiate(
830 i_isolate, &thrower, module_obj, maybe_imports,
831 i::MaybeHandle<i::JSArrayBuffer>());
832 }
833
834 i::Handle<i::JSObject> instance_obj;
835 if (!maybe_instance_obj.ToHandle(&instance_obj)) {
836 DCHECK(i_isolate->has_scheduled_exception());
837 return;
838 }
839
840 // The infrastructure for `new Foo` calls allocates an object, which is
841 // available here as {args.This()}. We're going to discard this object
842 // and use {instance_obj} instead, but it does have the correct prototype,
843 // which we must harvest from it. This makes a difference when the JS
844 // constructor function wasn't {WebAssembly.Instance} directly, but some
845 // subclass: {instance_obj} has {WebAssembly.Instance}'s prototype at this
846 // point, so we must overwrite that with the correct prototype for {Foo}.
847 if (!TransferPrototype(i_isolate, instance_obj,
848 Utils::OpenHandle(*args.This()))) {
849 return;
850 }
851
852 args.GetReturnValue().Set(Utils::ToLocal(instance_obj));
853 }
854
855 // WebAssembly.instantiateStreaming(Response | Promise<Response> [, imports])
856 // -> Promise<ResultObject>
857 // (where ResultObject has a "module" and an "instance" field)
WebAssemblyInstantiateStreaming(const v8::FunctionCallbackInfo<v8::Value> & args)858 void WebAssemblyInstantiateStreaming(
859 const v8::FunctionCallbackInfo<v8::Value>& args) {
860 v8::Isolate* isolate = args.GetIsolate();
861 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
862 i_isolate->CountUsage(
863 v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
864
865 HandleScope scope(isolate);
866 Local<Context> context = isolate->GetCurrentContext();
867 const char* const kAPIMethodName = "WebAssembly.instantiateStreaming()";
868 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
869
870 // Create and assign the return value of this function.
871 ASSIGN(Promise::Resolver, result_resolver, Promise::Resolver::New(context));
872 Local<Promise> promise = result_resolver->GetPromise();
873 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
874 return_value.Set(promise);
875
876 // Create an InstantiateResultResolver in case there is an issue with the
877 // passed parameters.
878 std::unique_ptr<i::wasm::InstantiationResultResolver> resolver(
879 new InstantiateModuleResultResolver(i_isolate,
880 Utils::OpenHandle(*promise)));
881
882 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
883 thrower.CompileError("Wasm code generation disallowed by embedder");
884 resolver->OnInstantiationFailed(thrower.Reify());
885 return;
886 }
887
888 // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
889 Local<Value> ffi = args[1];
890 i::MaybeHandle<i::JSReceiver> maybe_imports =
891 GetValueAsImports(ffi, &thrower);
892
893 if (thrower.error()) {
894 resolver->OnInstantiationFailed(thrower.Reify());
895 return;
896 }
897
898 // We start compilation now, we have no use for the
899 // {InstantiationResultResolver}.
900 resolver.reset();
901
902 std::shared_ptr<i::wasm::CompilationResultResolver> compilation_resolver(
903 new AsyncInstantiateCompileResultResolver(
904 i_isolate, Utils::OpenHandle(*promise), maybe_imports));
905
906 // Allocate the streaming decoder in a Managed so we can pass it to the
907 // embedder.
908 i::Handle<i::Managed<WasmStreaming>> data =
909 i::Managed<WasmStreaming>::Allocate(
910 i_isolate, 0,
911 std::make_unique<WasmStreaming::WasmStreamingImpl>(
912 isolate, kAPIMethodName, compilation_resolver));
913
914 DCHECK_NOT_NULL(i_isolate->wasm_streaming_callback());
915 ASSIGN(
916 v8::Function, compile_callback,
917 v8::Function::New(context, i_isolate->wasm_streaming_callback(),
918 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
919 ASSIGN(
920 v8::Function, reject_callback,
921 v8::Function::New(context, WasmStreamingPromiseFailedCallback,
922 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
923
924 // The parameter may be of type {Response} or of type {Promise<Response>}.
925 // Treat either case of parameter as Promise.resolve(parameter)
926 // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments
927
928 // Ending with:
929 // return Promise.resolve(parameter).then(compile_callback);
930 ASSIGN(Promise::Resolver, input_resolver, Promise::Resolver::New(context));
931 if (!input_resolver->Resolve(context, args[0]).IsJust()) return;
932
933 // We do not have any use of the result here. The {compile_callback} will
934 // start streaming compilation, which will eventually resolve the promise we
935 // set as result value.
936 USE(input_resolver->GetPromise()->Then(context, compile_callback,
937 reject_callback));
938 }
939
940 // WebAssembly.instantiate(module, imports) -> WebAssembly.Instance
941 // WebAssembly.instantiate(bytes, imports) ->
942 // {module: WebAssembly.Module, instance: WebAssembly.Instance}
WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value> & args)943 void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
944 constexpr const char* kAPIMethodName = "WebAssembly.instantiate()";
945 v8::Isolate* isolate = args.GetIsolate();
946 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
947 i_isolate->CountUsage(
948 v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
949
950 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
951
952 HandleScope scope(isolate);
953
954 Local<Context> context = isolate->GetCurrentContext();
955
956 ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
957 Local<Promise> promise = promise_resolver->GetPromise();
958 args.GetReturnValue().Set(promise);
959
960 std::unique_ptr<i::wasm::InstantiationResultResolver> resolver(
961 new InstantiateModuleResultResolver(i_isolate,
962 Utils::OpenHandle(*promise)));
963
964 Local<Value> first_arg_value = args[0];
965 i::Handle<i::Object> first_arg = Utils::OpenHandle(*first_arg_value);
966 if (!first_arg->IsJSObject()) {
967 thrower.TypeError(
968 "Argument 0 must be a buffer source or a WebAssembly.Module object");
969 resolver->OnInstantiationFailed(thrower.Reify());
970 return;
971 }
972
973 // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
974 Local<Value> ffi = args[1];
975 i::MaybeHandle<i::JSReceiver> maybe_imports =
976 GetValueAsImports(ffi, &thrower);
977
978 if (thrower.error()) {
979 resolver->OnInstantiationFailed(thrower.Reify());
980 return;
981 }
982
983 if (first_arg->IsWasmModuleObject()) {
984 i::Handle<i::WasmModuleObject> module_obj =
985 i::Handle<i::WasmModuleObject>::cast(first_arg);
986
987 i::wasm::GetWasmEngine()->AsyncInstantiate(i_isolate, std::move(resolver),
988 module_obj, maybe_imports);
989 return;
990 }
991
992 bool is_shared = false;
993 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
994 if (thrower.error()) {
995 resolver->OnInstantiationFailed(thrower.Reify());
996 return;
997 }
998
999 // We start compilation now, we have no use for the
1000 // {InstantiationResultResolver}.
1001 resolver.reset();
1002
1003 std::shared_ptr<i::wasm::CompilationResultResolver> compilation_resolver(
1004 new AsyncInstantiateCompileResultResolver(
1005 i_isolate, Utils::OpenHandle(*promise), maybe_imports));
1006
1007 // The first parameter is a buffer source, we have to check if we are allowed
1008 // to compile it.
1009 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
1010 thrower.CompileError("Wasm code generation disallowed by embedder");
1011 compilation_resolver->OnCompilationFailed(thrower.Reify());
1012 return;
1013 }
1014
1015 // Asynchronous compilation handles copying wire bytes if necessary.
1016 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1017 i::wasm::GetWasmEngine()->AsyncCompile(i_isolate, enabled_features,
1018 std::move(compilation_resolver), bytes,
1019 is_shared, kAPIMethodName);
1020 }
1021
GetIntegerProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,v8::Local<v8::Value> value,i::Handle<i::String> property_name,int64_t * result,int64_t lower_bound,uint64_t upper_bound)1022 bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
1023 Local<Context> context, v8::Local<v8::Value> value,
1024 i::Handle<i::String> property_name, int64_t* result,
1025 int64_t lower_bound, uint64_t upper_bound) {
1026 uint32_t number;
1027 if (!EnforceUint32(property_name, value, context, thrower, &number)) {
1028 return false;
1029 }
1030 if (number < lower_bound) {
1031 thrower->RangeError("Property '%s': value %" PRIu32
1032 " is below the lower bound %" PRIx64,
1033 property_name->ToCString().get(), number, lower_bound);
1034 return false;
1035 }
1036 if (number > upper_bound) {
1037 thrower->RangeError("Property '%s': value %" PRIu32
1038 " is above the upper bound %" PRIu64,
1039 property_name->ToCString().get(), number, upper_bound);
1040 return false;
1041 }
1042
1043 *result = static_cast<int64_t>(number);
1044 return true;
1045 }
1046
GetOptionalIntegerProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,Local<v8::Object> object,Local<String> property,bool * has_property,int64_t * result,int64_t lower_bound,uint64_t upper_bound)1047 bool GetOptionalIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
1048 Local<Context> context,
1049 Local<v8::Object> object,
1050 Local<String> property, bool* has_property,
1051 int64_t* result, int64_t lower_bound,
1052 uint64_t upper_bound) {
1053 v8::Local<v8::Value> value;
1054 if (!object->Get(context, property).ToLocal(&value)) {
1055 return false;
1056 }
1057
1058 // Web IDL: dictionary presence
1059 // https://heycam.github.io/webidl/#dfn-present
1060 if (value->IsUndefined()) {
1061 if (has_property != nullptr) *has_property = false;
1062 return true;
1063 }
1064
1065 if (has_property != nullptr) *has_property = true;
1066 i::Handle<i::String> property_name = v8::Utils::OpenHandle(*property);
1067
1068 return GetIntegerProperty(isolate, thrower, context, value, property_name,
1069 result, lower_bound, upper_bound);
1070 }
1071
1072 // Fetch 'initial' or 'minimum' property from object. If both are provided,
1073 // a TypeError is thrown.
1074 // TODO(aseemgarg): change behavior when the following bug is resolved:
1075 // https://github.com/WebAssembly/js-types/issues/6
GetInitialOrMinimumProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,Local<v8::Object> object,int64_t * result,int64_t lower_bound,uint64_t upper_bound)1076 bool GetInitialOrMinimumProperty(v8::Isolate* isolate, ErrorThrower* thrower,
1077 Local<Context> context,
1078 Local<v8::Object> object, int64_t* result,
1079 int64_t lower_bound, uint64_t upper_bound) {
1080 bool has_initial = false;
1081 if (!GetOptionalIntegerProperty(isolate, thrower, context, object,
1082 v8_str(isolate, "initial"), &has_initial,
1083 result, lower_bound, upper_bound)) {
1084 return false;
1085 }
1086 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(
1087 reinterpret_cast<i::Isolate*>(isolate));
1088 if (enabled_features.has_type_reflection()) {
1089 bool has_minimum = false;
1090 int64_t minimum = 0;
1091 if (!GetOptionalIntegerProperty(isolate, thrower, context, object,
1092 v8_str(isolate, "minimum"), &has_minimum,
1093 &minimum, lower_bound, upper_bound)) {
1094 return false;
1095 }
1096 if (has_initial && has_minimum) {
1097 thrower->TypeError(
1098 "The properties 'initial' and 'minimum' are not allowed at the same "
1099 "time");
1100 return false;
1101 }
1102 if (has_minimum) {
1103 // Only {minimum} exists, so we use {minimum} as {initial}.
1104 has_initial = true;
1105 *result = minimum;
1106 }
1107 }
1108 if (!has_initial) {
1109 // TODO(aseemgarg): update error message when the spec issue is resolved.
1110 thrower->TypeError("Property 'initial' is required");
1111 return false;
1112 }
1113 return true;
1114 }
1115
1116 namespace {
DefaultReferenceValue(i::Isolate * isolate,i::wasm::ValueType type)1117 i::Handle<i::Object> DefaultReferenceValue(i::Isolate* isolate,
1118 i::wasm::ValueType type) {
1119 if (type == i::wasm::kWasmFuncRef) {
1120 return isolate->factory()->null_value();
1121 }
1122 if (type.is_reference()) {
1123 return isolate->factory()->undefined_value();
1124 }
1125 UNREACHABLE();
1126 }
1127 } // namespace
1128
1129 // new WebAssembly.Table(args) -> WebAssembly.Table
WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value> & args)1130 void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
1131 v8::Isolate* isolate = args.GetIsolate();
1132 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1133 HandleScope scope(isolate);
1134 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table()");
1135 if (!args.IsConstructCall()) {
1136 thrower.TypeError("WebAssembly.Table must be invoked with 'new'");
1137 return;
1138 }
1139 if (!args[0]->IsObject()) {
1140 thrower.TypeError("Argument 0 must be a table descriptor");
1141 return;
1142 }
1143 Local<Context> context = isolate->GetCurrentContext();
1144 Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1145 i::wasm::ValueType type;
1146 // The descriptor's 'element'.
1147 {
1148 v8::MaybeLocal<v8::Value> maybe =
1149 descriptor->Get(context, v8_str(isolate, "element"));
1150 v8::Local<v8::Value> value;
1151 if (!maybe.ToLocal(&value)) return;
1152 v8::Local<v8::String> string;
1153 if (!value->ToString(context).ToLocal(&string)) return;
1154 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1155 // The JS api uses 'anyfunc' instead of 'funcref'.
1156 if (string->StringEquals(v8_str(isolate, "anyfunc"))) {
1157 type = i::wasm::kWasmFuncRef;
1158 } else if (enabled_features.has_type_reflection() &&
1159 string->StringEquals(v8_str(isolate, "funcref"))) {
1160 // With the type reflection proposal, "funcref" replaces "anyfunc",
1161 // and anyfunc just becomes an alias for "funcref".
1162 type = i::wasm::kWasmFuncRef;
1163 } else if (string->StringEquals(v8_str(isolate, "externref"))) {
1164 // externref is known as anyref as of wasm-gc.
1165 type = i::wasm::kWasmAnyRef;
1166 } else {
1167 thrower.TypeError(
1168 "Descriptor property 'element' must be a WebAssembly reference type");
1169 return;
1170 }
1171 }
1172
1173 int64_t initial = 0;
1174 if (!GetInitialOrMinimumProperty(isolate, &thrower, context, descriptor,
1175 &initial, 0,
1176 i::wasm::max_table_init_entries())) {
1177 return;
1178 }
1179 // The descriptor's 'maximum'.
1180 int64_t maximum = -1;
1181 bool has_maximum = true;
1182 if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
1183 v8_str(isolate, "maximum"), &has_maximum,
1184 &maximum, initial,
1185 std::numeric_limits<uint32_t>::max())) {
1186 return;
1187 }
1188
1189 i::Handle<i::FixedArray> fixed_array;
1190 i::Handle<i::WasmTableObject> table_obj =
1191 i::WasmTableObject::New(i_isolate, i::Handle<i::WasmInstanceObject>(),
1192 type, static_cast<uint32_t>(initial), has_maximum,
1193 static_cast<uint32_t>(maximum), &fixed_array,
1194 DefaultReferenceValue(i_isolate, type));
1195
1196 // The infrastructure for `new Foo` calls allocates an object, which is
1197 // available here as {args.This()}. We're going to discard this object
1198 // and use {table_obj} instead, but it does have the correct prototype,
1199 // which we must harvest from it. This makes a difference when the JS
1200 // constructor function wasn't {WebAssembly.Table} directly, but some
1201 // subclass: {table_obj} has {WebAssembly.Table}'s prototype at this
1202 // point, so we must overwrite that with the correct prototype for {Foo}.
1203 if (!TransferPrototype(i_isolate, table_obj,
1204 Utils::OpenHandle(*args.This()))) {
1205 return;
1206 }
1207
1208 if (initial > 0 && args.Length() >= 2 && !args[1]->IsUndefined()) {
1209 i::Handle<i::Object> element = Utils::OpenHandle(*args[1]);
1210 if (!i::WasmTableObject::IsValidElement(i_isolate, table_obj, element)) {
1211 thrower.TypeError(
1212 "Argument 2 must be undefined, null, or a value of type compatible "
1213 "with the type of the new table.");
1214 return;
1215 }
1216 // TODO(7748): Generalize this if other table types are allowed.
1217 if (type == i::wasm::kWasmFuncRef && !element->IsNull()) {
1218 element = i::WasmInternalFunction::FromExternal(element, i_isolate)
1219 .ToHandleChecked();
1220 }
1221 for (uint32_t index = 0; index < static_cast<uint32_t>(initial); ++index) {
1222 i::WasmTableObject::Set(i_isolate, table_obj, index, element);
1223 }
1224 }
1225 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1226 return_value.Set(Utils::ToLocal(i::Handle<i::JSObject>::cast(table_obj)));
1227 }
1228
WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value> & args)1229 void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
1230 v8::Isolate* isolate = args.GetIsolate();
1231 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1232 HandleScope scope(isolate);
1233 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory()");
1234 if (!args.IsConstructCall()) {
1235 thrower.TypeError("WebAssembly.Memory must be invoked with 'new'");
1236 return;
1237 }
1238 if (!args[0]->IsObject()) {
1239 thrower.TypeError("Argument 0 must be a memory descriptor");
1240 return;
1241 }
1242 Local<Context> context = isolate->GetCurrentContext();
1243 Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1244
1245 int64_t initial = 0;
1246 if (!GetInitialOrMinimumProperty(isolate, &thrower, context, descriptor,
1247 &initial, 0, i::wasm::kSpecMaxMemoryPages)) {
1248 return;
1249 }
1250 // The descriptor's 'maximum'.
1251 int64_t maximum = i::WasmMemoryObject::kNoMaximum;
1252 if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
1253 v8_str(isolate, "maximum"), nullptr, &maximum,
1254 initial, i::wasm::kSpecMaxMemoryPages)) {
1255 return;
1256 }
1257
1258 auto shared = i::SharedFlag::kNotShared;
1259 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1260 if (enabled_features.has_threads()) {
1261 // Shared property of descriptor
1262 Local<String> shared_key = v8_str(isolate, "shared");
1263 v8::MaybeLocal<v8::Value> maybe_value =
1264 descriptor->Get(context, shared_key);
1265 v8::Local<v8::Value> value;
1266 if (maybe_value.ToLocal(&value)) {
1267 shared = value->BooleanValue(isolate) ? i::SharedFlag::kShared
1268 : i::SharedFlag::kNotShared;
1269 } else {
1270 DCHECK(i_isolate->has_scheduled_exception());
1271 return;
1272 }
1273
1274 // Throw TypeError if shared is true, and the descriptor has no "maximum"
1275 if (shared == i::SharedFlag::kShared && maximum == -1) {
1276 thrower.TypeError(
1277 "If shared is true, maximum property should be defined.");
1278 return;
1279 }
1280 }
1281
1282 i::Handle<i::JSObject> memory_obj;
1283 if (!i::WasmMemoryObject::New(i_isolate, static_cast<int>(initial),
1284 static_cast<int>(maximum), shared)
1285 .ToHandle(&memory_obj)) {
1286 thrower.RangeError("could not allocate memory");
1287 return;
1288 }
1289
1290 // The infrastructure for `new Foo` calls allocates an object, which is
1291 // available here as {args.This()}. We're going to discard this object
1292 // and use {memory_obj} instead, but it does have the correct prototype,
1293 // which we must harvest from it. This makes a difference when the JS
1294 // constructor function wasn't {WebAssembly.Memory} directly, but some
1295 // subclass: {memory_obj} has {WebAssembly.Memory}'s prototype at this
1296 // point, so we must overwrite that with the correct prototype for {Foo}.
1297 if (!TransferPrototype(i_isolate, memory_obj,
1298 Utils::OpenHandle(*args.This()))) {
1299 return;
1300 }
1301
1302 if (shared == i::SharedFlag::kShared) {
1303 i::Handle<i::JSArrayBuffer> buffer(
1304 i::Handle<i::WasmMemoryObject>::cast(memory_obj)->array_buffer(),
1305 i_isolate);
1306 Maybe<bool> result =
1307 buffer->SetIntegrityLevel(buffer, i::FROZEN, i::kDontThrow);
1308 if (!result.FromJust()) {
1309 thrower.TypeError(
1310 "Status of setting SetIntegrityLevel of buffer is false.");
1311 return;
1312 }
1313 }
1314 args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
1315 }
1316
1317 // Determines the type encoded in a value type property (e.g. type reflection).
1318 // Returns false if there was an exception, true upon success. On success the
1319 // outgoing {type} is set accordingly, or set to {wasm::kWasmVoid} in case the
1320 // type could not be properly recognized.
GetValueType(Isolate * isolate,MaybeLocal<Value> maybe,Local<Context> context,i::wasm::ValueType * type,i::wasm::WasmFeatures enabled_features)1321 bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
1322 Local<Context> context, i::wasm::ValueType* type,
1323 i::wasm::WasmFeatures enabled_features) {
1324 v8::Local<v8::Value> value;
1325 if (!maybe.ToLocal(&value)) return false;
1326 v8::Local<v8::String> string;
1327 if (!value->ToString(context).ToLocal(&string)) return false;
1328 if (string->StringEquals(v8_str(isolate, "i32"))) {
1329 *type = i::wasm::kWasmI32;
1330 } else if (string->StringEquals(v8_str(isolate, "f32"))) {
1331 *type = i::wasm::kWasmF32;
1332 } else if (string->StringEquals(v8_str(isolate, "i64"))) {
1333 *type = i::wasm::kWasmI64;
1334 } else if (string->StringEquals(v8_str(isolate, "f64"))) {
1335 *type = i::wasm::kWasmF64;
1336 } else if (string->StringEquals(v8_str(isolate, "externref"))) {
1337 *type = i::wasm::kWasmAnyRef;
1338 } else if (enabled_features.has_type_reflection() &&
1339 string->StringEquals(v8_str(isolate, "funcref"))) {
1340 // The type reflection proposal renames "anyfunc" to "funcref", and makes
1341 // "anyfunc" an alias of "funcref".
1342 *type = i::wasm::kWasmFuncRef;
1343 } else if (string->StringEquals(v8_str(isolate, "anyfunc"))) {
1344 // The JS api spec uses 'anyfunc' instead of 'funcref'.
1345 *type = i::wasm::kWasmFuncRef;
1346 } else if (enabled_features.has_gc() &&
1347 string->StringEquals(v8_str(isolate, "eqref"))) {
1348 *type = i::wasm::kWasmEqRef;
1349 } else {
1350 // Unrecognized type.
1351 *type = i::wasm::kWasmVoid;
1352 }
1353 return true;
1354 }
1355
1356 namespace {
1357
ToI32(Local<v8::Value> value,Local<Context> context,int32_t * i32_value)1358 bool ToI32(Local<v8::Value> value, Local<Context> context, int32_t* i32_value) {
1359 if (!value->IsUndefined()) {
1360 v8::Local<v8::Int32> int32_value;
1361 if (!value->ToInt32(context).ToLocal(&int32_value)) return false;
1362 if (!int32_value->Int32Value(context).To(i32_value)) return false;
1363 }
1364 return true;
1365 }
1366
ToI64(Local<v8::Value> value,Local<Context> context,int64_t * i64_value)1367 bool ToI64(Local<v8::Value> value, Local<Context> context, int64_t* i64_value) {
1368 if (!value->IsUndefined()) {
1369 v8::Local<v8::BigInt> bigint_value;
1370 if (!value->ToBigInt(context).ToLocal(&bigint_value)) return false;
1371 *i64_value = bigint_value->Int64Value();
1372 }
1373 return true;
1374 }
1375
ToF32(Local<v8::Value> value,Local<Context> context,float * f32_value)1376 bool ToF32(Local<v8::Value> value, Local<Context> context, float* f32_value) {
1377 if (!value->IsUndefined()) {
1378 double f64_value = 0;
1379 v8::Local<v8::Number> number_value;
1380 if (!value->ToNumber(context).ToLocal(&number_value)) return false;
1381 if (!number_value->NumberValue(context).To(&f64_value)) return false;
1382 *f32_value = i::DoubleToFloat32(f64_value);
1383 }
1384 return true;
1385 }
1386
ToF64(Local<v8::Value> value,Local<Context> context,double * f64_value)1387 bool ToF64(Local<v8::Value> value, Local<Context> context, double* f64_value) {
1388 if (!value->IsUndefined()) {
1389 v8::Local<v8::Number> number_value;
1390 if (!value->ToNumber(context).ToLocal(&number_value)) return false;
1391 if (!number_value->NumberValue(context).To(f64_value)) return false;
1392 }
1393 return true;
1394 }
1395
1396 } // namespace
1397
1398 // WebAssembly.Global
WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value> & args)1399 void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1400 v8::Isolate* isolate = args.GetIsolate();
1401 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1402 HandleScope scope(isolate);
1403 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Global()");
1404 if (!args.IsConstructCall()) {
1405 thrower.TypeError("WebAssembly.Global must be invoked with 'new'");
1406 return;
1407 }
1408 if (!args[0]->IsObject()) {
1409 thrower.TypeError("Argument 0 must be a global descriptor");
1410 return;
1411 }
1412 Local<Context> context = isolate->GetCurrentContext();
1413 Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1414 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1415
1416 // The descriptor's 'mutable'.
1417 bool is_mutable = false;
1418 {
1419 Local<String> mutable_key = v8_str(isolate, "mutable");
1420 v8::MaybeLocal<v8::Value> maybe = descriptor->Get(context, mutable_key);
1421 v8::Local<v8::Value> value;
1422 if (maybe.ToLocal(&value)) {
1423 is_mutable = value->BooleanValue(isolate);
1424 } else {
1425 DCHECK(i_isolate->has_scheduled_exception());
1426 return;
1427 }
1428 }
1429
1430 // The descriptor's type, called 'value'. It is called 'value' because this
1431 // descriptor is planned to be re-used as the global's type for reflection,
1432 // so calling it 'type' is redundant.
1433 i::wasm::ValueType type;
1434 {
1435 v8::MaybeLocal<v8::Value> maybe =
1436 descriptor->Get(context, v8_str(isolate, "value"));
1437 if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
1438 if (type == i::wasm::kWasmVoid) {
1439 thrower.TypeError(
1440 "Descriptor property 'value' must be a WebAssembly type");
1441 return;
1442 }
1443 }
1444
1445 const uint32_t offset = 0;
1446 i::MaybeHandle<i::WasmGlobalObject> maybe_global_obj =
1447 i::WasmGlobalObject::New(i_isolate, i::Handle<i::WasmInstanceObject>(),
1448 i::MaybeHandle<i::JSArrayBuffer>(),
1449 i::MaybeHandle<i::FixedArray>(), type, offset,
1450 is_mutable);
1451
1452 i::Handle<i::WasmGlobalObject> global_obj;
1453 if (!maybe_global_obj.ToHandle(&global_obj)) {
1454 thrower.RangeError("could not allocate memory");
1455 return;
1456 }
1457
1458 // The infrastructure for `new Foo` calls allocates an object, which is
1459 // available here as {args.This()}. We're going to discard this object
1460 // and use {global_obj} instead, but it does have the correct prototype,
1461 // which we must harvest from it. This makes a difference when the JS
1462 // constructor function wasn't {WebAssembly.Global} directly, but some
1463 // subclass: {global_obj} has {WebAssembly.Global}'s prototype at this
1464 // point, so we must overwrite that with the correct prototype for {Foo}.
1465 if (!TransferPrototype(i_isolate, global_obj,
1466 Utils::OpenHandle(*args.This()))) {
1467 return;
1468 }
1469
1470 // Convert value to a WebAssembly value, the default value is 0.
1471 Local<v8::Value> value = Local<Value>::Cast(args[1]);
1472 switch (type.kind()) {
1473 case i::wasm::kI32: {
1474 int32_t i32_value = 0;
1475 if (!ToI32(value, context, &i32_value)) return;
1476 global_obj->SetI32(i32_value);
1477 break;
1478 }
1479 case i::wasm::kI64: {
1480 int64_t i64_value = 0;
1481 if (!ToI64(value, context, &i64_value)) return;
1482 global_obj->SetI64(i64_value);
1483 break;
1484 }
1485 case i::wasm::kF32: {
1486 float f32_value = 0;
1487 if (!ToF32(value, context, &f32_value)) return;
1488 global_obj->SetF32(f32_value);
1489 break;
1490 }
1491 case i::wasm::kF64: {
1492 double f64_value = 0;
1493 if (!ToF64(value, context, &f64_value)) return;
1494 global_obj->SetF64(f64_value);
1495 break;
1496 }
1497 case i::wasm::kRef:
1498 case i::wasm::kOptRef: {
1499 switch (type.heap_representation()) {
1500 case i::wasm::HeapType::kAny: {
1501 if (args.Length() < 2) {
1502 // When no initial value is provided, we have to use the WebAssembly
1503 // default value 'null', and not the JS default value 'undefined'.
1504 global_obj->SetExternRef(i_isolate->factory()->null_value());
1505 break;
1506 }
1507 global_obj->SetExternRef(Utils::OpenHandle(*value));
1508 break;
1509 }
1510 case i::wasm::HeapType::kFunc: {
1511 if (args.Length() < 2) {
1512 // When no initial value is provided, we have to use the WebAssembly
1513 // default value 'null', and not the JS default value 'undefined'.
1514 global_obj->SetFuncRef(i_isolate,
1515 i_isolate->factory()->null_value());
1516 break;
1517 }
1518
1519 if (!global_obj->SetFuncRef(i_isolate, Utils::OpenHandle(*value))) {
1520 thrower.TypeError(
1521 "The value of funcref globals must be null or an "
1522 "exported function");
1523 }
1524 break;
1525 }
1526 case internal::wasm::HeapType::kBottom:
1527 UNREACHABLE();
1528 case i::wasm::HeapType::kEq:
1529 case internal::wasm::HeapType::kI31:
1530 case internal::wasm::HeapType::kData:
1531 case internal::wasm::HeapType::kArray:
1532 default:
1533 // TODO(7748): Implement these.
1534 UNIMPLEMENTED();
1535 }
1536 break;
1537 }
1538 case i::wasm::kRtt:
1539 // TODO(7748): Implement.
1540 UNIMPLEMENTED();
1541 case i::wasm::kI8:
1542 case i::wasm::kI16:
1543 case i::wasm::kVoid:
1544 case i::wasm::kS128:
1545 case i::wasm::kBottom:
1546 UNREACHABLE();
1547 }
1548
1549 i::Handle<i::JSObject> global_js_object(global_obj);
1550 args.GetReturnValue().Set(Utils::ToLocal(global_js_object));
1551 }
1552
1553 namespace {
1554
GetIterableLength(i::Isolate * isolate,Local<Context> context,Local<Object> iterable)1555 uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context,
1556 Local<Object> iterable) {
1557 Local<String> length = Utils::ToLocal(isolate->factory()->length_string());
1558 MaybeLocal<Value> property = iterable->Get(context, length);
1559 if (property.IsEmpty()) return i::kMaxUInt32;
1560 MaybeLocal<Uint32> number = property.ToLocalChecked()->ToArrayIndex(context);
1561 if (number.IsEmpty()) return i::kMaxUInt32;
1562 DCHECK_NE(i::kMaxUInt32, number.ToLocalChecked()->Value());
1563 return number.ToLocalChecked()->Value();
1564 }
1565
1566 } // namespace
1567
1568 // WebAssembly.Tag
WebAssemblyTag(const v8::FunctionCallbackInfo<v8::Value> & args)1569 void WebAssemblyTag(const v8::FunctionCallbackInfo<v8::Value>& args) {
1570 v8::Isolate* isolate = args.GetIsolate();
1571 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1572 HandleScope scope(isolate);
1573
1574 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Tag()");
1575 if (!args.IsConstructCall()) {
1576 thrower.TypeError("WebAssembly.Tag must be invoked with 'new'");
1577 return;
1578 }
1579 if (!args[0]->IsObject()) {
1580 thrower.TypeError("Argument 0 must be a tag type");
1581 return;
1582 }
1583
1584 Local<Object> event_type = Local<Object>::Cast(args[0]);
1585 Local<Context> context = isolate->GetCurrentContext();
1586 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1587
1588 // Load the 'parameters' property of the event type.
1589 Local<String> parameters_key = v8_str(isolate, "parameters");
1590 v8::MaybeLocal<v8::Value> parameters_maybe =
1591 event_type->Get(context, parameters_key);
1592 v8::Local<v8::Value> parameters_value;
1593 if (!parameters_maybe.ToLocal(¶meters_value) ||
1594 !parameters_value->IsObject()) {
1595 thrower.TypeError("Argument 0 must be a tag type with 'parameters'");
1596 return;
1597 }
1598 Local<Object> parameters = parameters_value.As<Object>();
1599 uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
1600 if (parameters_len == i::kMaxUInt32) {
1601 thrower.TypeError("Argument 0 contains parameters without 'length'");
1602 return;
1603 }
1604 if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
1605 thrower.TypeError("Argument 0 contains too many parameters");
1606 return;
1607 }
1608
1609 // Decode the tag type and construct a signature.
1610 std::vector<i::wasm::ValueType> param_types(parameters_len,
1611 i::wasm::kWasmVoid);
1612 for (uint32_t i = 0; i < parameters_len; ++i) {
1613 i::wasm::ValueType& type = param_types[i];
1614 MaybeLocal<Value> maybe = parameters->Get(context, i);
1615 if (!GetValueType(isolate, maybe, context, &type, enabled_features) ||
1616 type == i::wasm::kWasmVoid) {
1617 thrower.TypeError(
1618 "Argument 0 parameter type at index #%u must be a value type", i);
1619 return;
1620 }
1621 }
1622 const i::wasm::FunctionSig sig{0, parameters_len, param_types.data()};
1623 // Set the tag index to 0. It is only used for debugging purposes, and has no
1624 // meaningful value when declared outside of a wasm module.
1625 auto tag = i::WasmExceptionTag::New(i_isolate, 0);
1626 i::Handle<i::JSObject> tag_object =
1627 i::WasmTagObject::New(i_isolate, &sig, tag);
1628 args.GetReturnValue().Set(Utils::ToLocal(tag_object));
1629 }
1630
1631 // WebAssembly.Suspender
WebAssemblySuspender(const v8::FunctionCallbackInfo<v8::Value> & args)1632 void WebAssemblySuspender(const v8::FunctionCallbackInfo<v8::Value>& args) {
1633 v8::Isolate* isolate = args.GetIsolate();
1634 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1635 HandleScope scope(isolate);
1636
1637 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Suspender()");
1638 if (!args.IsConstructCall()) {
1639 thrower.TypeError("WebAssembly.Suspender must be invoked with 'new'");
1640 return;
1641 }
1642
1643 i::Handle<i::JSObject> suspender = i::WasmSuspenderObject::New(i_isolate);
1644
1645 // The infrastructure for `new Foo` calls allocates an object, which is
1646 // available here as {args.This()}. We're going to discard this object
1647 // and use {suspender} instead, but it does have the correct prototype,
1648 // which we must harvest from it. This makes a difference when the JS
1649 // constructor function wasn't {WebAssembly.Suspender} directly, but some
1650 // subclass: {suspender} has {WebAssembly.Suspender}'s prototype at this
1651 // point, so we must overwrite that with the correct prototype for {Foo}.
1652 if (!TransferPrototype(i_isolate, suspender,
1653 Utils::OpenHandle(*args.This()))) {
1654 return;
1655 }
1656 args.GetReturnValue().Set(Utils::ToLocal(suspender));
1657 }
1658
1659 namespace {
1660
GetEncodedSize(i::Handle<i::WasmTagObject> tag_object)1661 uint32_t GetEncodedSize(i::Handle<i::WasmTagObject> tag_object) {
1662 auto serialized_sig = tag_object->serialized_signature();
1663 i::wasm::WasmTagSig sig{0, static_cast<size_t>(serialized_sig.length()),
1664 reinterpret_cast<i::wasm::ValueType*>(
1665 serialized_sig.GetDataStartAddress())};
1666 i::wasm::WasmTag tag(&sig);
1667 return i::WasmExceptionPackage::GetEncodedSize(&tag);
1668 }
1669
EncodeExceptionValues(v8::Isolate * isolate,i::Handle<i::PodArray<i::wasm::ValueType>> signature,const Local<Value> & arg,ScheduledErrorThrower * thrower,i::Handle<i::FixedArray> values_out)1670 void EncodeExceptionValues(v8::Isolate* isolate,
1671 i::Handle<i::PodArray<i::wasm::ValueType>> signature,
1672 const Local<Value>& arg,
1673 ScheduledErrorThrower* thrower,
1674 i::Handle<i::FixedArray> values_out) {
1675 Local<Context> context = isolate->GetCurrentContext();
1676 uint32_t index = 0;
1677 if (!arg->IsObject()) {
1678 thrower->TypeError("Exception values must be an iterable object");
1679 return;
1680 }
1681 auto values = arg.As<Object>();
1682 for (int i = 0; i < signature->length(); ++i) {
1683 MaybeLocal<Value> maybe_value = values->Get(context, i);
1684 Local<Value> value = maybe_value.ToLocalChecked();
1685 i::wasm::ValueType type = signature->get(i);
1686 switch (type.kind()) {
1687 case i::wasm::kI32: {
1688 int32_t i32 = 0;
1689 if (!ToI32(value, context, &i32)) return;
1690 i::EncodeI32ExceptionValue(values_out, &index, i32);
1691 break;
1692 }
1693 case i::wasm::kI64: {
1694 int64_t i64 = 0;
1695 if (!ToI64(value, context, &i64)) return;
1696 i::EncodeI64ExceptionValue(values_out, &index, i64);
1697 break;
1698 }
1699 case i::wasm::kF32: {
1700 float f32 = 0;
1701 if (!ToF32(value, context, &f32)) return;
1702 int32_t i32 = bit_cast<int32_t>(f32);
1703 i::EncodeI32ExceptionValue(values_out, &index, i32);
1704 break;
1705 }
1706 case i::wasm::kF64: {
1707 double f64 = 0;
1708 if (!ToF64(value, context, &f64)) return;
1709 int64_t i64 = bit_cast<int64_t>(f64);
1710 i::EncodeI64ExceptionValue(values_out, &index, i64);
1711 break;
1712 }
1713 case i::wasm::kRef:
1714 case i::wasm::kOptRef:
1715 switch (type.heap_representation()) {
1716 case i::wasm::HeapType::kFunc:
1717 case i::wasm::HeapType::kAny:
1718 case i::wasm::HeapType::kEq:
1719 case i::wasm::HeapType::kI31:
1720 case i::wasm::HeapType::kData:
1721 case i::wasm::HeapType::kArray:
1722 values_out->set(index++, *Utils::OpenHandle(*value));
1723 break;
1724 case internal::wasm::HeapType::kBottom:
1725 UNREACHABLE();
1726 default:
1727 // TODO(7748): Add support for custom struct/array types.
1728 UNIMPLEMENTED();
1729 }
1730 break;
1731 case i::wasm::kRtt:
1732 case i::wasm::kI8:
1733 case i::wasm::kI16:
1734 case i::wasm::kVoid:
1735 case i::wasm::kBottom:
1736 case i::wasm::kS128:
1737 UNREACHABLE();
1738 }
1739 }
1740 }
1741
1742 } // namespace
1743
WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value> & args)1744 void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
1745 v8::Isolate* isolate = args.GetIsolate();
1746 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1747 HandleScope scope(isolate);
1748
1749 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception()");
1750 if (!args.IsConstructCall()) {
1751 thrower.TypeError("WebAssembly.Exception must be invoked with 'new'");
1752 return;
1753 }
1754 if (!args[0]->IsObject()) {
1755 thrower.TypeError("Argument 0 must be a WebAssembly tag");
1756 return;
1757 }
1758 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
1759 if (!i::HeapObject::cast(*arg0).IsWasmTagObject()) {
1760 thrower.TypeError("Argument 0 must be a WebAssembly tag");
1761 return;
1762 }
1763 i::Handle<i::WasmTagObject> tag_object =
1764 i::Handle<i::WasmTagObject>::cast(arg0);
1765 i::Handle<i::WasmExceptionTag> tag(
1766 i::WasmExceptionTag::cast(tag_object->tag()), i_isolate);
1767 uint32_t size = GetEncodedSize(tag_object);
1768 i::Handle<i::WasmExceptionPackage> runtime_exception =
1769 i::WasmExceptionPackage::New(i_isolate, tag, size);
1770 // The constructor above should guarantee that the cast below succeeds.
1771 i::Handle<i::FixedArray> values = i::Handle<i::FixedArray>::cast(
1772 i::WasmExceptionPackage::GetExceptionValues(i_isolate,
1773 runtime_exception));
1774 i::Handle<i::PodArray<i::wasm::ValueType>> signature(
1775 tag_object->serialized_signature(), i_isolate);
1776 EncodeExceptionValues(isolate, signature, args[1], &thrower, values);
1777 if (thrower.error()) return;
1778 args.GetReturnValue().Set(
1779 Utils::ToLocal(i::Handle<i::Object>::cast(runtime_exception)));
1780 }
1781
1782 // WebAssembly.Function
WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value> & args)1783 void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
1784 v8::Isolate* isolate = args.GetIsolate();
1785 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1786 HandleScope scope(isolate);
1787 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Function()");
1788 if (!args.IsConstructCall()) {
1789 thrower.TypeError("WebAssembly.Function must be invoked with 'new'");
1790 return;
1791 }
1792 if (!args[0]->IsObject()) {
1793 thrower.TypeError("Argument 0 must be a function type");
1794 return;
1795 }
1796 Local<Object> function_type = Local<Object>::Cast(args[0]);
1797 Local<Context> context = isolate->GetCurrentContext();
1798 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1799
1800 // Load the 'parameters' property of the function type.
1801 Local<String> parameters_key = v8_str(isolate, "parameters");
1802 v8::MaybeLocal<v8::Value> parameters_maybe =
1803 function_type->Get(context, parameters_key);
1804 v8::Local<v8::Value> parameters_value;
1805 if (!parameters_maybe.ToLocal(¶meters_value) ||
1806 !parameters_value->IsObject()) {
1807 thrower.TypeError("Argument 0 must be a function type with 'parameters'");
1808 return;
1809 }
1810 Local<Object> parameters = parameters_value.As<Object>();
1811 uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
1812 if (parameters_len == i::kMaxUInt32) {
1813 thrower.TypeError("Argument 0 contains parameters without 'length'");
1814 return;
1815 }
1816 if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
1817 thrower.TypeError("Argument 0 contains too many parameters");
1818 return;
1819 }
1820
1821 // Load the 'results' property of the function type.
1822 Local<String> results_key = v8_str(isolate, "results");
1823 v8::MaybeLocal<v8::Value> results_maybe =
1824 function_type->Get(context, results_key);
1825 v8::Local<v8::Value> results_value;
1826 if (!results_maybe.ToLocal(&results_value)) return;
1827 if (!results_value->IsObject()) {
1828 thrower.TypeError("Argument 0 must be a function type with 'results'");
1829 return;
1830 }
1831 Local<Object> results = results_value.As<Object>();
1832 uint32_t results_len = GetIterableLength(i_isolate, context, results);
1833 if (results_len == i::kMaxUInt32) {
1834 thrower.TypeError("Argument 0 contains results without 'length'");
1835 return;
1836 }
1837 if (results_len > i::wasm::kV8MaxWasmFunctionReturns) {
1838 thrower.TypeError("Argument 0 contains too many results");
1839 return;
1840 }
1841
1842 // Decode the function type and construct a signature.
1843 i::Zone zone(i_isolate->allocator(), ZONE_NAME);
1844 i::wasm::FunctionSig::Builder builder(&zone, results_len, parameters_len);
1845 for (uint32_t i = 0; i < parameters_len; ++i) {
1846 i::wasm::ValueType type;
1847 MaybeLocal<Value> maybe = parameters->Get(context, i);
1848 if (!GetValueType(isolate, maybe, context, &type, enabled_features) ||
1849 type == i::wasm::kWasmVoid) {
1850 thrower.TypeError(
1851 "Argument 0 parameter type at index #%u must be a value type", i);
1852 return;
1853 }
1854 builder.AddParam(type);
1855 }
1856 for (uint32_t i = 0; i < results_len; ++i) {
1857 i::wasm::ValueType type;
1858 MaybeLocal<Value> maybe = results->Get(context, i);
1859 if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
1860 if (type == i::wasm::kWasmVoid) {
1861 thrower.TypeError(
1862 "Argument 0 result type at index #%u must be a value type", i);
1863 return;
1864 }
1865 builder.AddReturn(type);
1866 }
1867
1868 if (!args[1]->IsFunction()) {
1869 thrower.TypeError("Argument 1 must be a function");
1870 return;
1871 }
1872 const i::wasm::FunctionSig* sig = builder.Build();
1873
1874 i::Handle<i::JSReceiver> callable =
1875 Utils::OpenHandle(*args[1].As<Function>());
1876 if (i::WasmExportedFunction::IsWasmExportedFunction(*callable)) {
1877 if (*i::Handle<i::WasmExportedFunction>::cast(callable)->sig() == *sig) {
1878 args.GetReturnValue().Set(Utils::ToLocal(callable));
1879 return;
1880 }
1881
1882 thrower.TypeError(
1883 "The signature of Argument 1 (a WebAssembly function) does "
1884 "not match the signature specified in Argument 0");
1885 return;
1886 }
1887
1888 if (i::WasmJSFunction::IsWasmJSFunction(*callable)) {
1889 if (i::Handle<i::WasmJSFunction>::cast(callable)->MatchesSignature(sig)) {
1890 args.GetReturnValue().Set(Utils::ToLocal(callable));
1891 return;
1892 }
1893
1894 thrower.TypeError(
1895 "The signature of Argument 1 (a WebAssembly function) does "
1896 "not match the signature specified in Argument 0");
1897 return;
1898 }
1899
1900 i::Handle<i::JSFunction> result = i::WasmJSFunction::New(
1901 i_isolate, sig, callable, i::Handle<i::HeapObject>());
1902 args.GetReturnValue().Set(Utils::ToLocal(result));
1903 }
1904
1905 // WebAssembly.Function.type(WebAssembly.Function) -> FunctionType
WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value> & args)1906 void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
1907 v8::Isolate* isolate = args.GetIsolate();
1908 HandleScope scope(isolate);
1909 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1910 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Function.type()");
1911
1912 const i::wasm::FunctionSig* sig;
1913 i::Zone zone(i_isolate->allocator(), ZONE_NAME);
1914 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
1915 if (i::WasmExportedFunction::IsWasmExportedFunction(*arg0)) {
1916 auto wasm_exported_function =
1917 i::Handle<i::WasmExportedFunction>::cast(arg0);
1918 auto sfi = handle(wasm_exported_function->shared(), i_isolate);
1919 i::Handle<i::WasmExportedFunctionData> data =
1920 handle(sfi->wasm_exported_function_data(), i_isolate);
1921 sig = wasm_exported_function->sig();
1922 if (!data->suspender().IsUndefined()) {
1923 // If this export is wrapped by a Suspender, the function returns a
1924 // promise as an externref instead of the original return type.
1925 size_t param_count = sig->parameter_count();
1926 i::wasm::FunctionSig::Builder builder(&zone, 1, param_count);
1927 for (size_t i = 0; i < param_count; ++i) {
1928 builder.AddParam(sig->GetParam(0));
1929 }
1930 builder.AddReturn(i::wasm::kWasmAnyRef);
1931 sig = builder.Build();
1932 }
1933 } else if (i::WasmJSFunction::IsWasmJSFunction(*arg0)) {
1934 sig = i::Handle<i::WasmJSFunction>::cast(arg0)->GetSignature(&zone);
1935 } else {
1936 thrower.TypeError("Argument 0 must be a WebAssembly.Function");
1937 return;
1938 }
1939
1940 auto type = i::wasm::GetTypeForFunction(i_isolate, sig);
1941 args.GetReturnValue().Set(Utils::ToLocal(type));
1942 }
1943
1944 constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global";
1945 constexpr const char* kName_WasmMemoryObject = "WebAssembly.Memory";
1946 constexpr const char* kName_WasmInstanceObject = "WebAssembly.Instance";
1947 constexpr const char* kName_WasmSuspenderObject = "WebAssembly.Suspender";
1948 constexpr const char* kName_WasmTableObject = "WebAssembly.Table";
1949 constexpr const char* kName_WasmTagObject = "WebAssembly.Tag";
1950 constexpr const char* kName_WasmExceptionPackage = "WebAssembly.Exception";
1951
1952 #define EXTRACT_THIS(var, WasmType) \
1953 i::Handle<i::WasmType> var; \
1954 { \
1955 i::Handle<i::Object> this_arg = Utils::OpenHandle(*args.This()); \
1956 if (!this_arg->Is##WasmType()) { \
1957 thrower.TypeError("Receiver is not a %s", kName_##WasmType); \
1958 return; \
1959 } \
1960 var = i::Handle<i::WasmType>::cast(this_arg); \
1961 }
1962
WebAssemblyInstanceGetExports(const v8::FunctionCallbackInfo<v8::Value> & args)1963 void WebAssemblyInstanceGetExports(
1964 const v8::FunctionCallbackInfo<v8::Value>& args) {
1965 v8::Isolate* isolate = args.GetIsolate();
1966 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1967 HandleScope scope(isolate);
1968 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance.exports()");
1969 EXTRACT_THIS(receiver, WasmInstanceObject);
1970 i::Handle<i::JSObject> exports_object(receiver->exports_object(), i_isolate);
1971 args.GetReturnValue().Set(Utils::ToLocal(exports_object));
1972 }
1973
WebAssemblyTableGetLength(const v8::FunctionCallbackInfo<v8::Value> & args)1974 void WebAssemblyTableGetLength(
1975 const v8::FunctionCallbackInfo<v8::Value>& args) {
1976 v8::Isolate* isolate = args.GetIsolate();
1977 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1978 HandleScope scope(isolate);
1979 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.length()");
1980 EXTRACT_THIS(receiver, WasmTableObject);
1981 args.GetReturnValue().Set(
1982 v8::Number::New(isolate, receiver->current_length()));
1983 }
1984
1985 // WebAssembly.Table.grow(num, init_value = null) -> num
WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value> & args)1986 void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1987 v8::Isolate* isolate = args.GetIsolate();
1988 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1989 HandleScope scope(isolate);
1990 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()");
1991 Local<Context> context = isolate->GetCurrentContext();
1992 EXTRACT_THIS(receiver, WasmTableObject);
1993
1994 uint32_t grow_by;
1995 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &grow_by)) {
1996 return;
1997 }
1998
1999 i::Handle<i::Object> init_value;
2000
2001 if (args.Length() >= 2 && !args[1]->IsUndefined()) {
2002 init_value = Utils::OpenHandle(*args[1]);
2003 if (!i::WasmTableObject::IsValidElement(i_isolate, receiver, init_value)) {
2004 thrower.TypeError("Argument 1 must be a valid type for the table");
2005 return;
2006 }
2007 } else {
2008 init_value = DefaultReferenceValue(i_isolate, receiver->type());
2009 }
2010
2011 // TODO(7748): Generalize this if other table types are allowed.
2012 bool has_function_type =
2013 receiver->type() == i::wasm::kWasmFuncRef || receiver->type().has_index();
2014 if (has_function_type && !init_value->IsNull()) {
2015 init_value = i::WasmInternalFunction::FromExternal(init_value, i_isolate)
2016 .ToHandleChecked();
2017 }
2018
2019 int old_size =
2020 i::WasmTableObject::Grow(i_isolate, receiver, grow_by, init_value);
2021
2022 if (old_size < 0) {
2023 thrower.RangeError("failed to grow table by %u", grow_by);
2024 return;
2025 }
2026 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
2027 return_value.Set(old_size);
2028 }
2029
2030 // WebAssembly.Table.get(num) -> any
WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value> & args)2031 void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
2032 v8::Isolate* isolate = args.GetIsolate();
2033 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2034 HandleScope scope(isolate);
2035 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.get()");
2036 Local<Context> context = isolate->GetCurrentContext();
2037 EXTRACT_THIS(receiver, WasmTableObject);
2038
2039 uint32_t index;
2040 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
2041 return;
2042 }
2043 if (!i::WasmTableObject::IsInBounds(i_isolate, receiver, index)) {
2044 thrower.RangeError("invalid index %u into function table", index);
2045 return;
2046 }
2047
2048 i::Handle<i::Object> result =
2049 i::WasmTableObject::Get(i_isolate, receiver, index);
2050 if (result->IsWasmInternalFunction()) {
2051 result =
2052 handle(i::Handle<i::WasmInternalFunction>::cast(result)->external(),
2053 i_isolate);
2054 }
2055
2056 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
2057 return_value.Set(Utils::ToLocal(result));
2058 }
2059
2060 // WebAssembly.Table.set(num, any)
WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value> & args)2061 void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
2062 v8::Isolate* isolate = args.GetIsolate();
2063 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2064 HandleScope scope(isolate);
2065 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.set()");
2066 Local<Context> context = isolate->GetCurrentContext();
2067 EXTRACT_THIS(table_object, WasmTableObject);
2068
2069 // Parameter 0.
2070 uint32_t index;
2071 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
2072 return;
2073 }
2074 if (!i::WasmTableObject::IsInBounds(i_isolate, table_object, index)) {
2075 thrower.RangeError("invalid index %u into function table", index);
2076 return;
2077 }
2078
2079 i::Handle<i::Object> element =
2080 args.Length() >= 2
2081 ? Utils::OpenHandle(*args[1])
2082 : DefaultReferenceValue(i_isolate, table_object->type());
2083
2084 if (!i::WasmTableObject::IsValidElement(i_isolate, table_object, element)) {
2085 thrower.TypeError("Argument 1 is invalid for table of type %s",
2086 table_object->type().name().c_str());
2087 return;
2088 }
2089
2090 i::Handle<i::Object> external_element;
2091 bool is_external = i::WasmInternalFunction::FromExternal(element, i_isolate)
2092 .ToHandle(&external_element);
2093
2094 i::WasmTableObject::Set(i_isolate, table_object, index,
2095 is_external ? external_element : element);
2096 }
2097
2098 // WebAssembly.Table.type() -> TableType
WebAssemblyTableType(const v8::FunctionCallbackInfo<v8::Value> & args)2099 void WebAssemblyTableType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2100 v8::Isolate* isolate = args.GetIsolate();
2101 HandleScope scope(isolate);
2102 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2103 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.type()");
2104
2105 EXTRACT_THIS(table, WasmTableObject);
2106 base::Optional<uint32_t> max_size;
2107 if (!table->maximum_length().IsUndefined()) {
2108 uint64_t max_size64 = table->maximum_length().Number();
2109 DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
2110 max_size.emplace(static_cast<uint32_t>(max_size64));
2111 }
2112 auto type = i::wasm::GetTypeForTable(i_isolate, table->type(),
2113 table->current_length(), max_size);
2114 args.GetReturnValue().Set(Utils::ToLocal(type));
2115 }
2116
2117 // WebAssembly.Memory.grow(num) -> num
WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value> & args)2118 void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
2119 v8::Isolate* isolate = args.GetIsolate();
2120 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2121 HandleScope scope(isolate);
2122 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()");
2123 Local<Context> context = isolate->GetCurrentContext();
2124 EXTRACT_THIS(receiver, WasmMemoryObject);
2125
2126 uint32_t delta_pages;
2127 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &delta_pages)) {
2128 return;
2129 }
2130
2131 i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer(), i_isolate);
2132
2133 uint64_t old_pages64 = old_buffer->byte_length() / i::wasm::kWasmPageSize;
2134 uint64_t new_pages64 = old_pages64 + static_cast<uint64_t>(delta_pages);
2135
2136 if (new_pages64 > static_cast<uint64_t>(receiver->maximum_pages())) {
2137 thrower.RangeError("Maximum memory size exceeded");
2138 return;
2139 }
2140
2141 int32_t ret = i::WasmMemoryObject::Grow(i_isolate, receiver, delta_pages);
2142 if (ret == -1) {
2143 thrower.RangeError("Unable to grow instance memory");
2144 return;
2145 }
2146 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
2147 return_value.Set(ret);
2148 }
2149
2150 // WebAssembly.Memory.buffer -> ArrayBuffer
WebAssemblyMemoryGetBuffer(const v8::FunctionCallbackInfo<v8::Value> & args)2151 void WebAssemblyMemoryGetBuffer(
2152 const v8::FunctionCallbackInfo<v8::Value>& args) {
2153 v8::Isolate* isolate = args.GetIsolate();
2154 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2155 HandleScope scope(isolate);
2156 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer");
2157 EXTRACT_THIS(receiver, WasmMemoryObject);
2158
2159 i::Handle<i::Object> buffer_obj(receiver->array_buffer(), i_isolate);
2160 DCHECK(buffer_obj->IsJSArrayBuffer());
2161 i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(*buffer_obj),
2162 i_isolate);
2163 if (buffer->is_shared()) {
2164 // TODO(gdeepti): More needed here for when cached buffer, and current
2165 // buffer are out of sync, handle that here when bounds checks, and Grow
2166 // are handled correctly.
2167 Maybe<bool> result =
2168 buffer->SetIntegrityLevel(buffer, i::FROZEN, i::kDontThrow);
2169 if (!result.FromJust()) {
2170 thrower.TypeError(
2171 "Status of setting SetIntegrityLevel of buffer is false.");
2172 }
2173 }
2174 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
2175 return_value.Set(Utils::ToLocal(buffer));
2176 }
2177
2178 // WebAssembly.Memory.type() -> MemoryType
WebAssemblyMemoryType(const v8::FunctionCallbackInfo<v8::Value> & args)2179 void WebAssemblyMemoryType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2180 v8::Isolate* isolate = args.GetIsolate();
2181 HandleScope scope(isolate);
2182 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2183 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.type()");
2184
2185 EXTRACT_THIS(memory, WasmMemoryObject);
2186 i::Handle<i::JSArrayBuffer> buffer(memory->array_buffer(), i_isolate);
2187 size_t curr_size = buffer->byte_length() / i::wasm::kWasmPageSize;
2188 DCHECK_LE(curr_size, std::numeric_limits<uint32_t>::max());
2189 uint32_t min_size = static_cast<uint32_t>(curr_size);
2190 base::Optional<uint32_t> max_size;
2191 if (memory->has_maximum_pages()) {
2192 uint64_t max_size64 = memory->maximum_pages();
2193 DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
2194 max_size.emplace(static_cast<uint32_t>(max_size64));
2195 }
2196 bool shared = buffer->is_shared();
2197 auto type = i::wasm::GetTypeForMemory(i_isolate, min_size, max_size, shared);
2198 args.GetReturnValue().Set(Utils::ToLocal(type));
2199 }
2200
2201 // WebAssembly.Tag.type() -> FunctionType
WebAssemblyTagType(const v8::FunctionCallbackInfo<v8::Value> & args)2202 void WebAssemblyTagType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2203 v8::Isolate* isolate = args.GetIsolate();
2204 HandleScope scope(isolate);
2205 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2206 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Tag.type()");
2207
2208 EXTRACT_THIS(tag, WasmTagObject);
2209 if (thrower.error()) return;
2210
2211 int n = tag->serialized_signature().length();
2212 std::vector<i::wasm::ValueType> data(n);
2213 if (n > 0) {
2214 tag->serialized_signature().copy_out(0, data.data(), n);
2215 }
2216 const i::wasm::FunctionSig sig{0, data.size(), data.data()};
2217 constexpr bool kForException = true;
2218 auto type = i::wasm::GetTypeForFunction(i_isolate, &sig, kForException);
2219 args.GetReturnValue().Set(Utils::ToLocal(type));
2220 }
2221
WebAssemblyExceptionGetArg(const v8::FunctionCallbackInfo<v8::Value> & args)2222 void WebAssemblyExceptionGetArg(
2223 const v8::FunctionCallbackInfo<v8::Value>& args) {
2224 v8::Isolate* isolate = args.GetIsolate();
2225 HandleScope scope(isolate);
2226 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2227 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception.getArg()");
2228
2229 EXTRACT_THIS(exception, WasmExceptionPackage);
2230 if (thrower.error()) return;
2231
2232 i::MaybeHandle<i::WasmTagObject> maybe_tag =
2233 GetFirstArgumentAsTag(args, &thrower);
2234 if (thrower.error()) return;
2235 auto tag = maybe_tag.ToHandleChecked();
2236 Local<Context> context = isolate->GetCurrentContext();
2237 uint32_t index;
2238 if (!EnforceUint32("Index", args[1], context, &thrower, &index)) {
2239 return;
2240 }
2241 auto maybe_values =
2242 i::WasmExceptionPackage::GetExceptionValues(i_isolate, exception);
2243
2244 auto this_tag =
2245 i::WasmExceptionPackage::GetExceptionTag(i_isolate, exception);
2246 if (this_tag->IsUndefined()) {
2247 thrower.TypeError("Expected a WebAssembly.Exception object");
2248 return;
2249 }
2250 DCHECK(this_tag->IsWasmExceptionTag());
2251 if (tag->tag() != *this_tag) {
2252 thrower.TypeError("First argument does not match the exception tag");
2253 return;
2254 }
2255
2256 DCHECK(!maybe_values->IsUndefined());
2257 auto values = i::Handle<i::FixedArray>::cast(maybe_values);
2258 auto signature = tag->serialized_signature();
2259 if (index >= static_cast<uint32_t>(signature.length())) {
2260 thrower.RangeError("Index out of range");
2261 return;
2262 }
2263 // First, find the index in the values array.
2264 uint32_t decode_index = 0;
2265 // Since the bounds check above passed, the cast to int is safe.
2266 for (int i = 0; i < static_cast<int>(index); ++i) {
2267 switch (signature.get(i).kind()) {
2268 case i::wasm::kI32:
2269 case i::wasm::kF32:
2270 decode_index += 2;
2271 break;
2272 case i::wasm::kI64:
2273 case i::wasm::kF64:
2274 decode_index += 4;
2275 break;
2276 case i::wasm::kRef:
2277 case i::wasm::kOptRef:
2278 switch (signature.get(i).heap_representation()) {
2279 case i::wasm::HeapType::kFunc:
2280 case i::wasm::HeapType::kAny:
2281 case i::wasm::HeapType::kEq:
2282 case i::wasm::HeapType::kI31:
2283 case i::wasm::HeapType::kData:
2284 case i::wasm::HeapType::kArray:
2285 decode_index++;
2286 break;
2287 case i::wasm::HeapType::kBottom:
2288 UNREACHABLE();
2289 default:
2290 // TODO(7748): Add support for custom struct/array types.
2291 UNIMPLEMENTED();
2292 }
2293 break;
2294 case i::wasm::kRtt:
2295 case i::wasm::kI8:
2296 case i::wasm::kI16:
2297 case i::wasm::kVoid:
2298 case i::wasm::kBottom:
2299 case i::wasm::kS128:
2300 UNREACHABLE();
2301 }
2302 }
2303 // Decode the value at {decode_index}.
2304 Local<Value> result;
2305 switch (signature.get(index).kind()) {
2306 case i::wasm::kI32: {
2307 uint32_t u32_bits = 0;
2308 i::DecodeI32ExceptionValue(values, &decode_index, &u32_bits);
2309 int32_t i32 = static_cast<int32_t>(u32_bits);
2310 result = v8::Integer::New(isolate, i32);
2311 break;
2312 }
2313 case i::wasm::kI64: {
2314 uint64_t u64_bits = 0;
2315 i::DecodeI64ExceptionValue(values, &decode_index, &u64_bits);
2316 int64_t i64 = static_cast<int64_t>(u64_bits);
2317 result = v8::BigInt::New(isolate, i64);
2318 break;
2319 }
2320 case i::wasm::kF32: {
2321 uint32_t f32_bits = 0;
2322 DecodeI32ExceptionValue(values, &decode_index, &f32_bits);
2323 float f32 = bit_cast<float>(f32_bits);
2324 result = v8::Number::New(isolate, f32);
2325 break;
2326 }
2327 case i::wasm::kF64: {
2328 uint64_t f64_bits = 0;
2329 DecodeI64ExceptionValue(values, &decode_index, &f64_bits);
2330 double f64 = bit_cast<double>(f64_bits);
2331 result = v8::Number::New(isolate, f64);
2332 break;
2333 }
2334 case i::wasm::kRef:
2335 case i::wasm::kOptRef:
2336 switch (signature.get(index).heap_representation()) {
2337 case i::wasm::HeapType::kFunc:
2338 case i::wasm::HeapType::kAny:
2339 case i::wasm::HeapType::kEq:
2340 case i::wasm::HeapType::kI31:
2341 case i::wasm::HeapType::kArray:
2342 case i::wasm::HeapType::kData: {
2343 auto obj = values->get(decode_index);
2344 result = Utils::ToLocal(i::Handle<i::Object>(obj, i_isolate));
2345 break;
2346 }
2347 case i::wasm::HeapType::kBottom:
2348 UNREACHABLE();
2349 default:
2350 // TODO(7748): Add support for custom struct/array types.
2351 UNIMPLEMENTED();
2352 }
2353 break;
2354 case i::wasm::kRtt:
2355 case i::wasm::kI8:
2356 case i::wasm::kI16:
2357 case i::wasm::kVoid:
2358 case i::wasm::kBottom:
2359 case i::wasm::kS128:
2360 UNREACHABLE();
2361 }
2362 args.GetReturnValue().Set(result);
2363 }
2364
WebAssemblyExceptionIs(const v8::FunctionCallbackInfo<v8::Value> & args)2365 void WebAssemblyExceptionIs(const v8::FunctionCallbackInfo<v8::Value>& args) {
2366 v8::Isolate* isolate = args.GetIsolate();
2367 HandleScope scope(isolate);
2368 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2369 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception.is()");
2370
2371 EXTRACT_THIS(exception, WasmExceptionPackage);
2372 if (thrower.error()) return;
2373
2374 auto tag = i::WasmExceptionPackage::GetExceptionTag(i_isolate, exception);
2375 if (tag->IsUndefined()) {
2376 thrower.TypeError("Expected a WebAssembly.Exception object");
2377 return;
2378 }
2379 DCHECK(tag->IsWasmExceptionTag());
2380
2381 auto maybe_tag = GetFirstArgumentAsTag(args, &thrower);
2382 if (thrower.error()) {
2383 return;
2384 }
2385 auto tag_arg = maybe_tag.ToHandleChecked();
2386 args.GetReturnValue().Set(tag_arg->tag() == *tag);
2387 }
2388
WebAssemblyGlobalGetValueCommon(const v8::FunctionCallbackInfo<v8::Value> & args,const char * name)2389 void WebAssemblyGlobalGetValueCommon(
2390 const v8::FunctionCallbackInfo<v8::Value>& args, const char* name) {
2391 v8::Isolate* isolate = args.GetIsolate();
2392 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2393 HandleScope scope(isolate);
2394 ScheduledErrorThrower thrower(i_isolate, name);
2395 EXTRACT_THIS(receiver, WasmGlobalObject);
2396
2397 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
2398
2399 switch (receiver->type().kind()) {
2400 case i::wasm::kI32:
2401 return_value.Set(receiver->GetI32());
2402 break;
2403 case i::wasm::kI64: {
2404 Local<BigInt> value = BigInt::New(isolate, receiver->GetI64());
2405 return_value.Set(value);
2406 break;
2407 }
2408 case i::wasm::kF32:
2409 return_value.Set(receiver->GetF32());
2410 break;
2411 case i::wasm::kF64:
2412 return_value.Set(receiver->GetF64());
2413 break;
2414 case i::wasm::kS128:
2415 thrower.TypeError("Can't get the value of s128 WebAssembly.Global");
2416 break;
2417 case i::wasm::kRef:
2418 case i::wasm::kOptRef:
2419 switch (receiver->type().heap_representation()) {
2420 case i::wasm::HeapType::kAny:
2421 return_value.Set(Utils::ToLocal(receiver->GetRef()));
2422 break;
2423 case i::wasm::HeapType::kFunc: {
2424 i::Handle<i::Object> result = receiver->GetRef();
2425 if (result->IsWasmInternalFunction()) {
2426 result = handle(
2427 i::Handle<i::WasmInternalFunction>::cast(result)->external(),
2428 i_isolate);
2429 }
2430 return_value.Set(Utils::ToLocal(result));
2431 break;
2432 }
2433 case i::wasm::HeapType::kBottom:
2434 UNREACHABLE();
2435 case i::wasm::HeapType::kI31:
2436 case i::wasm::HeapType::kData:
2437 case i::wasm::HeapType::kArray:
2438 case i::wasm::HeapType::kEq:
2439 default:
2440 // TODO(7748): Implement these.
2441 UNIMPLEMENTED();
2442 }
2443 break;
2444 case i::wasm::kRtt:
2445 UNIMPLEMENTED(); // TODO(7748): Implement.
2446 case i::wasm::kI8:
2447 case i::wasm::kI16:
2448 case i::wasm::kBottom:
2449 case i::wasm::kVoid:
2450 UNREACHABLE();
2451 }
2452 }
2453
2454 // WebAssembly.Global.valueOf() -> num
WebAssemblyGlobalValueOf(const v8::FunctionCallbackInfo<v8::Value> & args)2455 void WebAssemblyGlobalValueOf(const v8::FunctionCallbackInfo<v8::Value>& args) {
2456 return WebAssemblyGlobalGetValueCommon(args, "WebAssembly.Global.valueOf()");
2457 }
2458
2459 // get WebAssembly.Global.value -> num
WebAssemblyGlobalGetValue(const v8::FunctionCallbackInfo<v8::Value> & args)2460 void WebAssemblyGlobalGetValue(
2461 const v8::FunctionCallbackInfo<v8::Value>& args) {
2462 return WebAssemblyGlobalGetValueCommon(args, "get WebAssembly.Global.value");
2463 }
2464
2465 // set WebAssembly.Global.value(num)
WebAssemblyGlobalSetValue(const v8::FunctionCallbackInfo<v8::Value> & args)2466 void WebAssemblyGlobalSetValue(
2467 const v8::FunctionCallbackInfo<v8::Value>& args) {
2468 v8::Isolate* isolate = args.GetIsolate();
2469 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2470 HandleScope scope(isolate);
2471 Local<Context> context = isolate->GetCurrentContext();
2472 ScheduledErrorThrower thrower(i_isolate, "set WebAssembly.Global.value");
2473 EXTRACT_THIS(receiver, WasmGlobalObject);
2474
2475 if (!receiver->is_mutable()) {
2476 thrower.TypeError("Can't set the value of an immutable global.");
2477 return;
2478 }
2479 if (args.Length() == 0) {
2480 thrower.TypeError("Argument 0 is required");
2481 return;
2482 }
2483
2484 switch (receiver->type().kind()) {
2485 case i::wasm::kI32: {
2486 int32_t i32_value = 0;
2487 if (!args[0]->Int32Value(context).To(&i32_value)) return;
2488 receiver->SetI32(i32_value);
2489 break;
2490 }
2491 case i::wasm::kI64: {
2492 v8::Local<v8::BigInt> bigint_value;
2493 if (!args[0]->ToBigInt(context).ToLocal(&bigint_value)) return;
2494 receiver->SetI64(bigint_value->Int64Value());
2495 break;
2496 }
2497 case i::wasm::kF32: {
2498 double f64_value = 0;
2499 if (!args[0]->NumberValue(context).To(&f64_value)) return;
2500 receiver->SetF32(i::DoubleToFloat32(f64_value));
2501 break;
2502 }
2503 case i::wasm::kF64: {
2504 double f64_value = 0;
2505 if (!args[0]->NumberValue(context).To(&f64_value)) return;
2506 receiver->SetF64(f64_value);
2507 break;
2508 }
2509 case i::wasm::kS128:
2510 thrower.TypeError("Can't set the value of s128 WebAssembly.Global");
2511 break;
2512 case i::wasm::kRef:
2513 case i::wasm::kOptRef:
2514 switch (receiver->type().heap_representation()) {
2515 case i::wasm::HeapType::kAny:
2516 receiver->SetExternRef(Utils::OpenHandle(*args[0]));
2517 break;
2518 case i::wasm::HeapType::kFunc: {
2519 if (!receiver->SetFuncRef(i_isolate, Utils::OpenHandle(*args[0]))) {
2520 thrower.TypeError(
2521 "value of an funcref reference must be either null or an "
2522 "exported function");
2523 }
2524 break;
2525 }
2526 case i::wasm::HeapType::kBottom:
2527 UNREACHABLE();
2528 case i::wasm::HeapType::kI31:
2529 case i::wasm::HeapType::kData:
2530 case i::wasm::HeapType::kArray:
2531 case i::wasm::HeapType::kEq:
2532 default:
2533 // TODO(7748): Implement these.
2534 UNIMPLEMENTED();
2535 }
2536 break;
2537 case i::wasm::kRtt:
2538 // TODO(7748): Implement.
2539 UNIMPLEMENTED();
2540 case i::wasm::kI8:
2541 case i::wasm::kI16:
2542 case i::wasm::kBottom:
2543 case i::wasm::kVoid:
2544 UNREACHABLE();
2545 }
2546 }
2547
2548 // WebAssembly.Global.type() -> GlobalType
WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value> & args)2549 void WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2550 v8::Isolate* isolate = args.GetIsolate();
2551 HandleScope scope(isolate);
2552 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2553 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Global.type()");
2554
2555 EXTRACT_THIS(global, WasmGlobalObject);
2556 auto type = i::wasm::GetTypeForGlobal(i_isolate, global->is_mutable(),
2557 global->type());
2558 args.GetReturnValue().Set(Utils::ToLocal(type));
2559 }
2560
2561 // WebAssembly.Suspender.returnPromiseOnSuspend(WebAssembly.Function) ->
2562 // WebAssembly.Function
WebAssemblySuspenderReturnPromiseOnSuspend(const v8::FunctionCallbackInfo<v8::Value> & args)2563 void WebAssemblySuspenderReturnPromiseOnSuspend(
2564 const v8::FunctionCallbackInfo<v8::Value>& args) {
2565 Isolate* isolate = args.GetIsolate();
2566 HandleScope scope(isolate);
2567 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2568 ScheduledErrorThrower thrower(
2569 i_isolate, "WebAssembly.Suspender.returnPromiseOnSuspend()");
2570 if (args.Length() == 0) {
2571 thrower.TypeError("Argument 0 is required");
2572 return;
2573 }
2574 auto maybe_function = GetFirstArgumentAsJSFunction(args, &thrower);
2575 if (thrower.error()) return;
2576 i::Handle<i::JSFunction> function = maybe_function.ToHandleChecked();
2577 i::SharedFunctionInfo sfi = function->shared();
2578 if (!sfi.HasWasmExportedFunctionData()) {
2579 thrower.TypeError("Argument 0 must be a wasm function");
2580 }
2581 i::WasmExportedFunctionData data = sfi.wasm_exported_function_data();
2582 if (data.sig()->return_count() != 1) {
2583 thrower.TypeError(
2584 "Expected a WebAssembly.Function with exactly one return type");
2585 }
2586 int index = data.function_index();
2587 i::Handle<i::WasmInstanceObject> instance(
2588 i::WasmInstanceObject::cast(data.internal().ref()), i_isolate);
2589 i::Handle<i::CodeT> wrapper =
2590 BUILTIN_CODE(i_isolate, WasmReturnPromiseOnSuspend);
2591 // Upcast to JSFunction to re-use the existing ToLocal helper below.
2592 i::Handle<i::JSFunction> result =
2593 i::Handle<i::WasmExternalFunction>::cast(i::WasmExportedFunction::New(
2594 i_isolate, instance, index,
2595 static_cast<int>(data.sig()->parameter_count()), wrapper));
2596 EXTRACT_THIS(suspender, WasmSuspenderObject);
2597 auto function_data = i::WasmExportedFunctionData::cast(
2598 result->shared().function_data(kAcquireLoad));
2599 function_data.set_suspender(*suspender);
2600 args.GetReturnValue().Set(Utils::ToLocal(result));
2601 }
2602
2603 // WebAssembly.Suspender.suspendOnReturnedPromise(Function) -> Function
WebAssemblySuspenderSuspendOnReturnedPromise(const v8::FunctionCallbackInfo<v8::Value> & args)2604 void WebAssemblySuspenderSuspendOnReturnedPromise(
2605 const v8::FunctionCallbackInfo<v8::Value>& args) {
2606 v8::Isolate* isolate = args.GetIsolate();
2607 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2608 HandleScope scope(isolate);
2609 ScheduledErrorThrower thrower(
2610 i_isolate, "WebAssembly.Suspender.suspendOnReturnedPromise()");
2611 if (!args[0]->IsObject()) {
2612 thrower.TypeError("Argument 0 must be a WebAssembly.Function");
2613 return;
2614 }
2615 i::Zone zone(i_isolate->allocator(), ZONE_NAME);
2616 const i::wasm::FunctionSig* sig;
2617 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
2618
2619 if (i::WasmExportedFunction::IsWasmExportedFunction(*arg0)) {
2620 // TODO(thibaudm): Suspend on wrapped wasm-to-wasm calls too.
2621 UNIMPLEMENTED();
2622 } else if (!i::WasmJSFunction::IsWasmJSFunction(*arg0)) {
2623 thrower.TypeError("Argument 0 must be a WebAssembly.Function");
2624 return;
2625 }
2626 sig = i::Handle<i::WasmJSFunction>::cast(arg0)->GetSignature(&zone);
2627 if (sig->return_count() != 1 || sig->GetReturn(0) != i::wasm::kWasmAnyRef) {
2628 thrower.TypeError("Expected a WebAssembly.Function with return type %s",
2629 i::wasm::kWasmAnyRef.name().c_str());
2630 }
2631
2632 auto callable = handle(
2633 i::Handle<i::WasmJSFunction>::cast(arg0)->GetCallable(), i_isolate);
2634 EXTRACT_THIS(suspender, WasmSuspenderObject);
2635 i::Handle<i::JSFunction> result =
2636 i::WasmJSFunction::New(i_isolate, sig, callable, suspender);
2637 args.GetReturnValue().Set(Utils::ToLocal(result));
2638 }
2639 } // namespace
2640
2641 // TODO(titzer): we use the API to create the function template because the
2642 // internal guts are too ugly to replicate here.
NewFunctionTemplate(i::Isolate * i_isolate,FunctionCallback func,bool has_prototype,SideEffectType side_effect_type=SideEffectType::kHasSideEffect)2643 static i::Handle<i::FunctionTemplateInfo> NewFunctionTemplate(
2644 i::Isolate* i_isolate, FunctionCallback func, bool has_prototype,
2645 SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
2646 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
2647 ConstructorBehavior behavior =
2648 has_prototype ? ConstructorBehavior::kAllow : ConstructorBehavior::kThrow;
2649 Local<FunctionTemplate> templ = FunctionTemplate::New(
2650 isolate, func, {}, {}, 0, behavior, side_effect_type);
2651 if (has_prototype) templ->ReadOnlyPrototype();
2652 return v8::Utils::OpenHandle(*templ);
2653 }
2654
NewObjectTemplate(i::Isolate * i_isolate)2655 static i::Handle<i::ObjectTemplateInfo> NewObjectTemplate(
2656 i::Isolate* i_isolate) {
2657 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
2658 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2659 return v8::Utils::OpenHandle(*templ);
2660 }
2661
2662 namespace internal {
2663
CreateFunc(Isolate * isolate,Handle<String> name,FunctionCallback func,bool has_prototype,SideEffectType side_effect_type=SideEffectType::kHasSideEffect)2664 Handle<JSFunction> CreateFunc(
2665 Isolate* isolate, Handle<String> name, FunctionCallback func,
2666 bool has_prototype,
2667 SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
2668 Handle<FunctionTemplateInfo> temp =
2669 NewFunctionTemplate(isolate, func, has_prototype, side_effect_type);
2670 Handle<JSFunction> function =
2671 ApiNatives::InstantiateFunction(temp, name).ToHandleChecked();
2672 DCHECK(function->shared().HasSharedName());
2673 return function;
2674 }
2675
InstallFunc(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func,int length,bool has_prototype=false,PropertyAttributes attributes=NONE,SideEffectType side_effect_type=SideEffectType::kHasSideEffect)2676 Handle<JSFunction> InstallFunc(
2677 Isolate* isolate, Handle<JSObject> object, const char* str,
2678 FunctionCallback func, int length, bool has_prototype = false,
2679 PropertyAttributes attributes = NONE,
2680 SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
2681 Handle<String> name = v8_str(isolate, str);
2682 Handle<JSFunction> function =
2683 CreateFunc(isolate, name, func, has_prototype, side_effect_type);
2684 function->shared().set_length(length);
2685 JSObject::AddProperty(isolate, object, name, function, attributes);
2686 return function;
2687 }
2688
InstallConstructorFunc(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func)2689 Handle<JSFunction> InstallConstructorFunc(Isolate* isolate,
2690 Handle<JSObject> object,
2691 const char* str,
2692 FunctionCallback func) {
2693 return InstallFunc(isolate, object, str, func, 1, true, DONT_ENUM,
2694 SideEffectType::kHasNoSideEffect);
2695 }
2696
GetterName(Isolate * isolate,Handle<String> name)2697 Handle<String> GetterName(Isolate* isolate, Handle<String> name) {
2698 return Name::ToFunctionName(isolate, name, isolate->factory()->get_string())
2699 .ToHandleChecked();
2700 }
2701
InstallGetter(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func)2702 void InstallGetter(Isolate* isolate, Handle<JSObject> object, const char* str,
2703 FunctionCallback func) {
2704 Handle<String> name = v8_str(isolate, str);
2705 Handle<JSFunction> function =
2706 CreateFunc(isolate, GetterName(isolate, name), func, false,
2707 SideEffectType::kHasNoSideEffect);
2708
2709 Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
2710 Utils::ToLocal(function),
2711 Local<Function>(), v8::None);
2712 }
2713
SetterName(Isolate * isolate,Handle<String> name)2714 Handle<String> SetterName(Isolate* isolate, Handle<String> name) {
2715 return Name::ToFunctionName(isolate, name, isolate->factory()->set_string())
2716 .ToHandleChecked();
2717 }
2718
InstallGetterSetter(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback getter,FunctionCallback setter)2719 void InstallGetterSetter(Isolate* isolate, Handle<JSObject> object,
2720 const char* str, FunctionCallback getter,
2721 FunctionCallback setter) {
2722 Handle<String> name = v8_str(isolate, str);
2723 Handle<JSFunction> getter_func =
2724 CreateFunc(isolate, GetterName(isolate, name), getter, false,
2725 SideEffectType::kHasNoSideEffect);
2726 Handle<JSFunction> setter_func =
2727 CreateFunc(isolate, SetterName(isolate, name), setter, false);
2728 setter_func->shared().set_length(1);
2729
2730 Utils::ToLocal(object)->SetAccessorProperty(
2731 Utils::ToLocal(name), Utils::ToLocal(getter_func),
2732 Utils::ToLocal(setter_func), v8::None);
2733 }
2734
2735 // Assigns a dummy instance template to the given constructor function. Used to
2736 // make sure the implicit receivers for the constructors in this file have an
2737 // instance type different from the internal one, they allocate the resulting
2738 // object explicitly and ignore implicit receiver.
SetDummyInstanceTemplate(Isolate * isolate,Handle<JSFunction> fun)2739 void SetDummyInstanceTemplate(Isolate* isolate, Handle<JSFunction> fun) {
2740 Handle<ObjectTemplateInfo> instance_template = NewObjectTemplate(isolate);
2741 FunctionTemplateInfo::SetInstanceTemplate(
2742 isolate, handle(fun->shared().get_api_func_data(), isolate),
2743 instance_template);
2744 }
2745
SetupConstructor(Isolate * isolate,Handle<JSFunction> constructor,InstanceType instance_type,int instance_size,const char * name=nullptr)2746 Handle<JSObject> SetupConstructor(Isolate* isolate,
2747 Handle<JSFunction> constructor,
2748 InstanceType instance_type, int instance_size,
2749 const char* name = nullptr) {
2750 SetDummyInstanceTemplate(isolate, constructor);
2751 JSFunction::EnsureHasInitialMap(constructor);
2752 Handle<JSObject> proto(JSObject::cast(constructor->instance_prototype()),
2753 isolate);
2754 Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size);
2755 JSFunction::SetInitialMap(isolate, constructor, map, proto);
2756 constexpr PropertyAttributes ro_attributes =
2757 static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
2758 if (name) {
2759 JSObject::AddProperty(isolate, proto,
2760 isolate->factory()->to_string_tag_symbol(),
2761 v8_str(isolate, name), ro_attributes);
2762 }
2763 return proto;
2764 }
2765
2766 // static
Install(Isolate * isolate,bool exposed_on_global_object)2767 void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
2768 Handle<JSGlobalObject> global = isolate->global_object();
2769 Handle<Context> context(global->native_context(), isolate);
2770 // Install the JS API once only.
2771 Object prev = context->get(Context::WASM_MODULE_CONSTRUCTOR_INDEX);
2772 if (!prev.IsUndefined(isolate)) {
2773 DCHECK(prev.IsJSFunction());
2774 return;
2775 }
2776
2777 Factory* factory = isolate->factory();
2778
2779 // Setup WebAssembly
2780 Handle<String> name = v8_str(isolate, "WebAssembly");
2781 // Not supposed to be called, hence using the kIllegal builtin as code.
2782 Handle<SharedFunctionInfo> info =
2783 factory->NewSharedFunctionInfoForBuiltin(name, Builtin::kIllegal);
2784 info->set_language_mode(LanguageMode::kStrict);
2785
2786 Handle<JSFunction> cons =
2787 Factory::JSFunctionBuilder{isolate, info, context}.Build();
2788 JSFunction::SetPrototype(cons, isolate->initial_object_prototype());
2789 Handle<JSObject> webassembly =
2790 factory->NewJSObject(cons, AllocationType::kOld);
2791
2792 PropertyAttributes ro_attributes =
2793 static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
2794 JSObject::AddProperty(isolate, webassembly, factory->to_string_tag_symbol(),
2795 name, ro_attributes);
2796 InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1);
2797 InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
2798 InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1);
2799
2800 if (FLAG_wasm_test_streaming) {
2801 isolate->set_wasm_streaming_callback(WasmStreamingCallbackForTesting);
2802 }
2803
2804 if (isolate->wasm_streaming_callback() != nullptr) {
2805 InstallFunc(isolate, webassembly, "compileStreaming",
2806 WebAssemblyCompileStreaming, 1);
2807 InstallFunc(isolate, webassembly, "instantiateStreaming",
2808 WebAssemblyInstantiateStreaming, 1);
2809 }
2810
2811 // Expose the API on the global object if configured to do so.
2812 if (exposed_on_global_object) {
2813 JSObject::AddProperty(isolate, global, name, webassembly, DONT_ENUM);
2814 }
2815
2816 // Setup Module
2817 Handle<JSFunction> module_constructor =
2818 InstallConstructorFunc(isolate, webassembly, "Module", WebAssemblyModule);
2819 SetupConstructor(isolate, module_constructor, i::WASM_MODULE_OBJECT_TYPE,
2820 WasmModuleObject::kHeaderSize, "WebAssembly.Module");
2821 context->set_wasm_module_constructor(*module_constructor);
2822 InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports,
2823 1, false, NONE, SideEffectType::kHasNoSideEffect);
2824 InstallFunc(isolate, module_constructor, "exports", WebAssemblyModuleExports,
2825 1, false, NONE, SideEffectType::kHasNoSideEffect);
2826 InstallFunc(isolate, module_constructor, "customSections",
2827 WebAssemblyModuleCustomSections, 2, false, NONE,
2828 SideEffectType::kHasNoSideEffect);
2829
2830 // Setup Instance
2831 Handle<JSFunction> instance_constructor = InstallConstructorFunc(
2832 isolate, webassembly, "Instance", WebAssemblyInstance);
2833 Handle<JSObject> instance_proto = SetupConstructor(
2834 isolate, instance_constructor, i::WASM_INSTANCE_OBJECT_TYPE,
2835 WasmInstanceObject::kHeaderSize, "WebAssembly.Instance");
2836 context->set_wasm_instance_constructor(*instance_constructor);
2837 InstallGetter(isolate, instance_proto, "exports",
2838 WebAssemblyInstanceGetExports);
2839
2840 // The context is not set up completely yet. That's why we cannot use
2841 // {WasmFeatures::FromIsolate} and have to use {WasmFeatures::FromFlags}
2842 // instead.
2843 auto enabled_features = i::wasm::WasmFeatures::FromFlags();
2844
2845 // Setup Table
2846 Handle<JSFunction> table_constructor =
2847 InstallConstructorFunc(isolate, webassembly, "Table", WebAssemblyTable);
2848 Handle<JSObject> table_proto =
2849 SetupConstructor(isolate, table_constructor, i::WASM_TABLE_OBJECT_TYPE,
2850 WasmTableObject::kHeaderSize, "WebAssembly.Table");
2851 context->set_wasm_table_constructor(*table_constructor);
2852 InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
2853 InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1);
2854 InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1, false, NONE,
2855 SideEffectType::kHasNoSideEffect);
2856 InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 2);
2857 if (enabled_features.has_type_reflection()) {
2858 InstallFunc(isolate, table_proto, "type", WebAssemblyTableType, 0, false,
2859 NONE, SideEffectType::kHasNoSideEffect);
2860 }
2861
2862 // Setup Memory
2863 Handle<JSFunction> memory_constructor =
2864 InstallConstructorFunc(isolate, webassembly, "Memory", WebAssemblyMemory);
2865 Handle<JSObject> memory_proto =
2866 SetupConstructor(isolate, memory_constructor, i::WASM_MEMORY_OBJECT_TYPE,
2867 WasmMemoryObject::kHeaderSize, "WebAssembly.Memory");
2868 context->set_wasm_memory_constructor(*memory_constructor);
2869 InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1);
2870 InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
2871 if (enabled_features.has_type_reflection()) {
2872 InstallFunc(isolate, memory_proto, "type", WebAssemblyMemoryType, 0, false,
2873 NONE, SideEffectType::kHasNoSideEffect);
2874 }
2875
2876 // Setup Global
2877 Handle<JSFunction> global_constructor =
2878 InstallConstructorFunc(isolate, webassembly, "Global", WebAssemblyGlobal);
2879 Handle<JSObject> global_proto =
2880 SetupConstructor(isolate, global_constructor, i::WASM_GLOBAL_OBJECT_TYPE,
2881 WasmGlobalObject::kHeaderSize, "WebAssembly.Global");
2882 context->set_wasm_global_constructor(*global_constructor);
2883 InstallFunc(isolate, global_proto, "valueOf", WebAssemblyGlobalValueOf, 0,
2884 false, NONE, SideEffectType::kHasNoSideEffect);
2885 InstallGetterSetter(isolate, global_proto, "value", WebAssemblyGlobalGetValue,
2886 WebAssemblyGlobalSetValue);
2887 if (enabled_features.has_type_reflection()) {
2888 InstallFunc(isolate, global_proto, "type", WebAssemblyGlobalType, 0, false,
2889 NONE, SideEffectType::kHasNoSideEffect);
2890 }
2891
2892 // Setup Exception
2893 if (enabled_features.has_eh()) {
2894 Handle<JSFunction> tag_constructor =
2895 InstallConstructorFunc(isolate, webassembly, "Tag", WebAssemblyTag);
2896 Handle<JSObject> tag_proto =
2897 SetupConstructor(isolate, tag_constructor, i::WASM_TAG_OBJECT_TYPE,
2898 WasmTagObject::kHeaderSize, "WebAssembly.Tag");
2899 context->set_wasm_tag_constructor(*tag_constructor);
2900
2901 if (enabled_features.has_type_reflection()) {
2902 InstallFunc(isolate, tag_proto, "type", WebAssemblyTagType, 0);
2903 }
2904 // Set up runtime exception constructor.
2905 Handle<JSFunction> exception_constructor = InstallConstructorFunc(
2906 isolate, webassembly, "Exception", WebAssemblyException);
2907 SetDummyInstanceTemplate(isolate, exception_constructor);
2908 Handle<Map> exception_map(isolate->native_context()
2909 ->wasm_exception_error_function()
2910 .initial_map(),
2911 isolate);
2912 Handle<JSObject> exception_proto(
2913 JSObject::cast(isolate->native_context()
2914 ->wasm_exception_error_function()
2915 .instance_prototype()),
2916 isolate);
2917 InstallFunc(isolate, exception_proto, "getArg", WebAssemblyExceptionGetArg,
2918 2);
2919 InstallFunc(isolate, exception_proto, "is", WebAssemblyExceptionIs, 1);
2920 context->set_wasm_exception_constructor(*exception_constructor);
2921 JSFunction::SetInitialMap(isolate, exception_constructor, exception_map,
2922 exception_proto);
2923 }
2924
2925 // Setup Suspender.
2926 if (enabled_features.has_stack_switching()) {
2927 Handle<JSFunction> suspender_constructor = InstallConstructorFunc(
2928 isolate, webassembly, "Suspender", WebAssemblySuspender);
2929 context->set_wasm_suspender_constructor(*suspender_constructor);
2930 Handle<JSObject> suspender_proto = SetupConstructor(
2931 isolate, suspender_constructor, i::WASM_SUSPENDER_OBJECT_TYPE,
2932 WasmSuspenderObject::kHeaderSize, "WebAssembly.Suspender");
2933 InstallFunc(isolate, suspender_proto, "returnPromiseOnSuspend",
2934 WebAssemblySuspenderReturnPromiseOnSuspend, 1);
2935 InstallFunc(isolate, suspender_proto, "suspendOnReturnedPromise",
2936 WebAssemblySuspenderSuspendOnReturnedPromise, 1);
2937 }
2938
2939 // Setup Function
2940 if (enabled_features.has_type_reflection()) {
2941 Handle<JSFunction> function_constructor = InstallConstructorFunc(
2942 isolate, webassembly, "Function", WebAssemblyFunction);
2943 SetDummyInstanceTemplate(isolate, function_constructor);
2944 JSFunction::EnsureHasInitialMap(function_constructor);
2945 Handle<JSObject> function_proto(
2946 JSObject::cast(function_constructor->instance_prototype()), isolate);
2947 Handle<Map> function_map = isolate->factory()->CreateSloppyFunctionMap(
2948 FUNCTION_WITHOUT_PROTOTYPE, MaybeHandle<JSFunction>());
2949 CHECK(JSObject::SetPrototype(
2950 isolate, function_proto,
2951 handle(context->function_function().prototype(), isolate), false,
2952 kDontThrow)
2953 .FromJust());
2954 JSFunction::SetInitialMap(isolate, function_constructor, function_map,
2955 function_proto);
2956 InstallFunc(isolate, function_constructor, "type", WebAssemblyFunctionType,
2957 1);
2958 // Make all exported functions an instance of {WebAssembly.Function}.
2959 context->set_wasm_exported_function_map(*function_map);
2960 } else {
2961 // Make all exported functions an instance of {Function}.
2962 Handle<Map> function_map = isolate->sloppy_function_without_prototype_map();
2963 context->set_wasm_exported_function_map(*function_map);
2964 }
2965
2966 // Setup errors
2967 Handle<JSFunction> compile_error(
2968 isolate->native_context()->wasm_compile_error_function(), isolate);
2969 JSObject::AddProperty(isolate, webassembly,
2970 isolate->factory()->CompileError_string(),
2971 compile_error, DONT_ENUM);
2972 Handle<JSFunction> link_error(
2973 isolate->native_context()->wasm_link_error_function(), isolate);
2974 JSObject::AddProperty(isolate, webassembly,
2975 isolate->factory()->LinkError_string(), link_error,
2976 DONT_ENUM);
2977 Handle<JSFunction> runtime_error(
2978 isolate->native_context()->wasm_runtime_error_function(), isolate);
2979 JSObject::AddProperty(isolate, webassembly,
2980 isolate->factory()->RuntimeError_string(),
2981 runtime_error, DONT_ENUM);
2982 }
2983
2984 // static
InstallConditionalFeatures(Isolate * isolate,Handle<Context> context)2985 void WasmJs::InstallConditionalFeatures(Isolate* isolate,
2986 Handle<Context> context) {
2987 // Exception handling may have been enabled by an origin trial. If so, make
2988 // sure that the {WebAssembly.Tag} constructor is set up.
2989 auto enabled_features = i::wasm::WasmFeatures::FromContext(isolate, context);
2990 if (enabled_features.has_eh()) {
2991 Handle<JSGlobalObject> global = handle(context->global_object(), isolate);
2992 MaybeHandle<Object> maybe_webassembly =
2993 JSObject::GetProperty(isolate, global, "WebAssembly");
2994 Handle<Object> webassembly_obj;
2995 if (!maybe_webassembly.ToHandle(&webassembly_obj) ||
2996 !webassembly_obj->IsJSObject()) {
2997 // There is no {WebAssembly} object, or it's not what we expect.
2998 // Just return without adding the {Tag} constructor.
2999 return;
3000 }
3001 Handle<JSObject> webassembly = Handle<JSObject>::cast(webassembly_obj);
3002 // Setup Tag.
3003 Handle<String> tag_name = v8_str(isolate, "Tag");
3004 // The {WebAssembly} object may already have been modified. The following
3005 // code is designed to:
3006 // - check for existing {Tag} properties on the object itself, and avoid
3007 // overwriting them or adding duplicate properties
3008 // - disregard any setters or read-only properties on the prototype chain
3009 // - only make objects accessible to user code after all internal setup
3010 // has been completed.
3011 if (JSObject::HasOwnProperty(isolate, webassembly, tag_name)
3012 .FromMaybe(true)) {
3013 // Existing property, or exception.
3014 return;
3015 }
3016
3017 bool has_prototype = true;
3018 Handle<JSFunction> tag_constructor =
3019 CreateFunc(isolate, tag_name, WebAssemblyTag, has_prototype,
3020 SideEffectType::kHasNoSideEffect);
3021 tag_constructor->shared().set_length(1);
3022 context->set_wasm_tag_constructor(*tag_constructor);
3023 Handle<JSObject> tag_proto =
3024 SetupConstructor(isolate, tag_constructor, i::WASM_TAG_OBJECT_TYPE,
3025 WasmTagObject::kHeaderSize, "WebAssembly.Tag");
3026 if (enabled_features.has_type_reflection()) {
3027 InstallFunc(isolate, tag_proto, "type", WebAssemblyTagType, 0);
3028 }
3029 LookupIterator it(isolate, webassembly, tag_name, LookupIterator::OWN);
3030 Maybe<bool> result = JSObject::DefineOwnPropertyIgnoreAttributes(
3031 &it, tag_constructor, DONT_ENUM, Just(kDontThrow));
3032 // This could still fail if the object was non-extensible, but now we
3033 // return anyway so there's no need to even check.
3034 USE(result);
3035 }
3036 }
3037 #undef ASSIGN
3038 #undef EXTRACT_THIS
3039
3040 } // namespace internal
3041 } // namespace v8
3042