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/signature.h"
6
7 #include "src/handles.h"
8 #include "src/objects-inl.h"
9 #include "src/v8.h"
10 #include "src/zone/zone-containers.h"
11
12 #include "src/wasm/function-body-decoder.h"
13 #include "src/wasm/leb-helper.h"
14 #include "src/wasm/wasm-constants.h"
15 #include "src/wasm/wasm-module-builder.h"
16 #include "src/wasm/wasm-module.h"
17 #include "src/wasm/wasm-opcodes.h"
18
19 #include "src/v8memory.h"
20
21 namespace v8 {
22 namespace internal {
23 namespace wasm {
24
25 namespace {
26
27 // Emit a section code and the size as a padded varint that can be patched
28 // later.
EmitSection(SectionCode code,ZoneBuffer & buffer)29 size_t EmitSection(SectionCode code, ZoneBuffer& buffer) {
30 // Emit the section code.
31 buffer.write_u8(code);
32
33 // Emit a placeholder for the length.
34 return buffer.reserve_u32v();
35 }
36
37 // Patch the size of a section after it's finished.
FixupSection(ZoneBuffer & buffer,size_t start)38 void FixupSection(ZoneBuffer& buffer, size_t start) {
39 buffer.patch_u32v(start, static_cast<uint32_t>(buffer.offset() - start -
40 kPaddedVarInt32Size));
41 }
42
43 } // namespace
44
WasmFunctionBuilder(WasmModuleBuilder * builder)45 WasmFunctionBuilder::WasmFunctionBuilder(WasmModuleBuilder* builder)
46 : builder_(builder),
47 locals_(builder->zone()),
48 signature_index_(0),
49 func_index_(static_cast<uint32_t>(builder->functions_.size())),
50 body_(builder->zone(), 256),
51 i32_temps_(builder->zone()),
52 i64_temps_(builder->zone()),
53 f32_temps_(builder->zone()),
54 f64_temps_(builder->zone()),
55 direct_calls_(builder->zone()),
56 asm_offsets_(builder->zone(), 8) {}
57
EmitI32V(int32_t val)58 void WasmFunctionBuilder::EmitI32V(int32_t val) { body_.write_i32v(val); }
59
EmitU32V(uint32_t val)60 void WasmFunctionBuilder::EmitU32V(uint32_t val) { body_.write_u32v(val); }
61
SetSignature(FunctionSig * sig)62 void WasmFunctionBuilder::SetSignature(FunctionSig* sig) {
63 DCHECK(!locals_.has_sig());
64 locals_.set_sig(sig);
65 signature_index_ = builder_->AddSignature(sig);
66 }
67
AddLocal(ValueType type)68 uint32_t WasmFunctionBuilder::AddLocal(ValueType type) {
69 DCHECK(locals_.has_sig());
70 return locals_.AddLocals(1, type);
71 }
72
EmitGetLocal(uint32_t local_index)73 void WasmFunctionBuilder::EmitGetLocal(uint32_t local_index) {
74 EmitWithU32V(kExprGetLocal, local_index);
75 }
76
EmitSetLocal(uint32_t local_index)77 void WasmFunctionBuilder::EmitSetLocal(uint32_t local_index) {
78 EmitWithU32V(kExprSetLocal, local_index);
79 }
80
EmitTeeLocal(uint32_t local_index)81 void WasmFunctionBuilder::EmitTeeLocal(uint32_t local_index) {
82 EmitWithU32V(kExprTeeLocal, local_index);
83 }
84
EmitCode(const byte * code,uint32_t code_size)85 void WasmFunctionBuilder::EmitCode(const byte* code, uint32_t code_size) {
86 body_.write(code, code_size);
87 }
88
Emit(WasmOpcode opcode)89 void WasmFunctionBuilder::Emit(WasmOpcode opcode) { body_.write_u8(opcode); }
90
EmitWithU8(WasmOpcode opcode,const byte immediate)91 void WasmFunctionBuilder::EmitWithU8(WasmOpcode opcode, const byte immediate) {
92 body_.write_u8(opcode);
93 body_.write_u8(immediate);
94 }
95
EmitWithU8U8(WasmOpcode opcode,const byte imm1,const byte imm2)96 void WasmFunctionBuilder::EmitWithU8U8(WasmOpcode opcode, const byte imm1,
97 const byte imm2) {
98 body_.write_u8(opcode);
99 body_.write_u8(imm1);
100 body_.write_u8(imm2);
101 }
102
EmitWithI32V(WasmOpcode opcode,int32_t immediate)103 void WasmFunctionBuilder::EmitWithI32V(WasmOpcode opcode, int32_t immediate) {
104 body_.write_u8(opcode);
105 body_.write_i32v(immediate);
106 }
107
EmitWithU32V(WasmOpcode opcode,uint32_t immediate)108 void WasmFunctionBuilder::EmitWithU32V(WasmOpcode opcode, uint32_t immediate) {
109 body_.write_u8(opcode);
110 body_.write_u32v(immediate);
111 }
112
EmitI32Const(int32_t value)113 void WasmFunctionBuilder::EmitI32Const(int32_t value) {
114 EmitWithI32V(kExprI32Const, value);
115 }
116
EmitI64Const(int64_t value)117 void WasmFunctionBuilder::EmitI64Const(int64_t value) {
118 body_.write_u8(kExprI64Const);
119 body_.write_i64v(value);
120 }
121
EmitF32Const(float value)122 void WasmFunctionBuilder::EmitF32Const(float value) {
123 body_.write_u8(kExprF32Const);
124 body_.write_f32(value);
125 }
126
EmitF64Const(double value)127 void WasmFunctionBuilder::EmitF64Const(double value) {
128 body_.write_u8(kExprF64Const);
129 body_.write_f64(value);
130 }
131
EmitDirectCallIndex(uint32_t index)132 void WasmFunctionBuilder::EmitDirectCallIndex(uint32_t index) {
133 DirectCallIndex call;
134 call.offset = body_.size();
135 call.direct_index = index;
136 direct_calls_.push_back(call);
137 byte placeholder_bytes[kMaxVarInt32Size] = {0};
138 EmitCode(placeholder_bytes, arraysize(placeholder_bytes));
139 }
140
SetName(Vector<const char> name)141 void WasmFunctionBuilder::SetName(Vector<const char> name) { name_ = name; }
142
AddAsmWasmOffset(size_t call_position,size_t to_number_position)143 void WasmFunctionBuilder::AddAsmWasmOffset(size_t call_position,
144 size_t to_number_position) {
145 // We only want to emit one mapping per byte offset.
146 DCHECK(asm_offsets_.size() == 0 || body_.size() > last_asm_byte_offset_);
147
148 DCHECK_LE(body_.size(), kMaxUInt32);
149 uint32_t byte_offset = static_cast<uint32_t>(body_.size());
150 asm_offsets_.write_u32v(byte_offset - last_asm_byte_offset_);
151 last_asm_byte_offset_ = byte_offset;
152
153 DCHECK_GE(std::numeric_limits<uint32_t>::max(), call_position);
154 uint32_t call_position_u32 = static_cast<uint32_t>(call_position);
155 asm_offsets_.write_i32v(call_position_u32 - last_asm_source_position_);
156
157 DCHECK_GE(std::numeric_limits<uint32_t>::max(), to_number_position);
158 uint32_t to_number_position_u32 = static_cast<uint32_t>(to_number_position);
159 asm_offsets_.write_i32v(to_number_position_u32 - call_position_u32);
160 last_asm_source_position_ = to_number_position_u32;
161 }
162
SetAsmFunctionStartPosition(size_t function_position)163 void WasmFunctionBuilder::SetAsmFunctionStartPosition(
164 size_t function_position) {
165 DCHECK_EQ(0, asm_func_start_source_position_);
166 DCHECK_GE(std::numeric_limits<uint32_t>::max(), function_position);
167 uint32_t function_position_u32 = static_cast<uint32_t>(function_position);
168 // Must be called before emitting any asm.js source position.
169 DCHECK_EQ(0, asm_offsets_.size());
170 asm_func_start_source_position_ = function_position_u32;
171 last_asm_source_position_ = function_position_u32;
172 }
173
DeleteCodeAfter(size_t position)174 void WasmFunctionBuilder::DeleteCodeAfter(size_t position) {
175 DCHECK_LE(position, body_.size());
176 body_.Truncate(position);
177 }
178
WriteSignature(ZoneBuffer & buffer) const179 void WasmFunctionBuilder::WriteSignature(ZoneBuffer& buffer) const {
180 buffer.write_u32v(signature_index_);
181 }
182
WriteBody(ZoneBuffer & buffer) const183 void WasmFunctionBuilder::WriteBody(ZoneBuffer& buffer) const {
184 size_t locals_size = locals_.Size();
185 buffer.write_size(locals_size + body_.size());
186 buffer.EnsureSpace(locals_size);
187 byte** ptr = buffer.pos_ptr();
188 locals_.Emit(*ptr);
189 (*ptr) += locals_size; // UGLY: manual bump of position pointer
190 if (body_.size() > 0) {
191 size_t base = buffer.offset();
192 buffer.write(body_.begin(), body_.size());
193 for (DirectCallIndex call : direct_calls_) {
194 buffer.patch_u32v(
195 base + call.offset,
196 call.direct_index +
197 static_cast<uint32_t>(builder_->function_imports_.size()));
198 }
199 }
200 }
201
WriteAsmWasmOffsetTable(ZoneBuffer & buffer) const202 void WasmFunctionBuilder::WriteAsmWasmOffsetTable(ZoneBuffer& buffer) const {
203 if (asm_func_start_source_position_ == 0 && asm_offsets_.size() == 0) {
204 buffer.write_size(0);
205 return;
206 }
207 size_t locals_enc_size = LEBHelper::sizeof_u32v(locals_.Size());
208 size_t func_start_size =
209 LEBHelper::sizeof_u32v(asm_func_start_source_position_);
210 buffer.write_size(asm_offsets_.size() + locals_enc_size + func_start_size);
211 // Offset of the recorded byte offsets.
212 DCHECK_GE(kMaxUInt32, locals_.Size());
213 buffer.write_u32v(static_cast<uint32_t>(locals_.Size()));
214 // Start position of the function.
215 buffer.write_u32v(asm_func_start_source_position_);
216 buffer.write(asm_offsets_.begin(), asm_offsets_.size());
217 }
218
WasmModuleBuilder(Zone * zone)219 WasmModuleBuilder::WasmModuleBuilder(Zone* zone)
220 : zone_(zone),
221 signatures_(zone),
222 function_imports_(zone),
223 function_exports_(zone),
224 global_imports_(zone),
225 functions_(zone),
226 data_segments_(zone),
227 indirect_functions_(zone),
228 globals_(zone),
229 signature_map_(zone),
230 start_function_index_(-1),
231 min_memory_size_(16),
232 max_memory_size_(0),
233 has_max_memory_size_(false),
234 has_shared_memory_(false) {}
235
AddFunction(FunctionSig * sig)236 WasmFunctionBuilder* WasmModuleBuilder::AddFunction(FunctionSig* sig) {
237 functions_.push_back(new (zone_) WasmFunctionBuilder(this));
238 // Add the signature if one was provided here.
239 if (sig) functions_.back()->SetSignature(sig);
240 return functions_.back();
241 }
242
AddDataSegment(const byte * data,uint32_t size,uint32_t dest)243 void WasmModuleBuilder::AddDataSegment(const byte* data, uint32_t size,
244 uint32_t dest) {
245 data_segments_.push_back({ZoneVector<byte>(zone()), dest});
246 ZoneVector<byte>& vec = data_segments_.back().data;
247 for (uint32_t i = 0; i < size; i++) {
248 vec.push_back(data[i]);
249 }
250 }
251
AddSignature(FunctionSig * sig)252 uint32_t WasmModuleBuilder::AddSignature(FunctionSig* sig) {
253 auto sig_entry = signature_map_.find(*sig);
254 if (sig_entry != signature_map_.end()) return sig_entry->second;
255 uint32_t index = static_cast<uint32_t>(signatures_.size());
256 signature_map_.emplace(*sig, index);
257 signatures_.push_back(sig);
258 return index;
259 }
260
AllocateIndirectFunctions(uint32_t count)261 uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) {
262 uint32_t index = static_cast<uint32_t>(indirect_functions_.size());
263 DCHECK_GE(FLAG_wasm_max_table_size, index);
264 if (count > FLAG_wasm_max_table_size - index) {
265 return std::numeric_limits<uint32_t>::max();
266 }
267 indirect_functions_.resize(indirect_functions_.size() + count);
268 return index;
269 }
270
SetIndirectFunction(uint32_t indirect,uint32_t direct)271 void WasmModuleBuilder::SetIndirectFunction(uint32_t indirect,
272 uint32_t direct) {
273 indirect_functions_[indirect] = direct;
274 }
275
AddImport(Vector<const char> name,FunctionSig * sig)276 uint32_t WasmModuleBuilder::AddImport(Vector<const char> name,
277 FunctionSig* sig) {
278 function_imports_.push_back({name, AddSignature(sig)});
279 return static_cast<uint32_t>(function_imports_.size() - 1);
280 }
281
AddGlobalImport(Vector<const char> name,ValueType type)282 uint32_t WasmModuleBuilder::AddGlobalImport(Vector<const char> name,
283 ValueType type) {
284 global_imports_.push_back({name, ValueTypes::ValueTypeCodeFor(type)});
285 return static_cast<uint32_t>(global_imports_.size() - 1);
286 }
287
MarkStartFunction(WasmFunctionBuilder * function)288 void WasmModuleBuilder::MarkStartFunction(WasmFunctionBuilder* function) {
289 start_function_index_ = function->func_index();
290 }
291
AddExport(Vector<const char> name,WasmFunctionBuilder * function)292 void WasmModuleBuilder::AddExport(Vector<const char> name,
293 WasmFunctionBuilder* function) {
294 function_exports_.push_back({name, function->func_index()});
295 }
296
AddGlobal(ValueType type,bool exported,bool mutability,const WasmInitExpr & init)297 uint32_t WasmModuleBuilder::AddGlobal(ValueType type, bool exported,
298 bool mutability,
299 const WasmInitExpr& init) {
300 globals_.push_back({type, exported, mutability, init});
301 return static_cast<uint32_t>(globals_.size() - 1);
302 }
303
SetMinMemorySize(uint32_t value)304 void WasmModuleBuilder::SetMinMemorySize(uint32_t value) {
305 min_memory_size_ = value;
306 }
307
SetMaxMemorySize(uint32_t value)308 void WasmModuleBuilder::SetMaxMemorySize(uint32_t value) {
309 has_max_memory_size_ = true;
310 max_memory_size_ = value;
311 }
312
SetHasSharedMemory()313 void WasmModuleBuilder::SetHasSharedMemory() { has_shared_memory_ = true; }
314
WriteTo(ZoneBuffer & buffer) const315 void WasmModuleBuilder::WriteTo(ZoneBuffer& buffer) const {
316 // == Emit magic =============================================================
317 buffer.write_u32(kWasmMagic);
318 buffer.write_u32(kWasmVersion);
319
320 // == Emit signatures ========================================================
321 if (signatures_.size() > 0) {
322 size_t start = EmitSection(kTypeSectionCode, buffer);
323 buffer.write_size(signatures_.size());
324
325 for (FunctionSig* sig : signatures_) {
326 buffer.write_u8(kWasmFunctionTypeCode);
327 buffer.write_size(sig->parameter_count());
328 for (auto param : sig->parameters()) {
329 buffer.write_u8(ValueTypes::ValueTypeCodeFor(param));
330 }
331 buffer.write_size(sig->return_count());
332 for (auto ret : sig->returns()) {
333 buffer.write_u8(ValueTypes::ValueTypeCodeFor(ret));
334 }
335 }
336 FixupSection(buffer, start);
337 }
338
339 // == Emit imports ===========================================================
340 if (global_imports_.size() + function_imports_.size() > 0) {
341 size_t start = EmitSection(kImportSectionCode, buffer);
342 buffer.write_size(global_imports_.size() + function_imports_.size());
343 for (auto import : global_imports_) {
344 buffer.write_u32v(0); // module name (length)
345 buffer.write_string(import.name); // field name
346 buffer.write_u8(kExternalGlobal);
347 buffer.write_u8(import.type_code);
348 buffer.write_u8(0); // immutable
349 }
350 for (auto import : function_imports_) {
351 buffer.write_u32v(0); // module name (length)
352 buffer.write_string(import.name); // field name
353 buffer.write_u8(kExternalFunction);
354 buffer.write_u32v(import.sig_index);
355 }
356 FixupSection(buffer, start);
357 }
358
359 // == Emit function signatures ===============================================
360 uint32_t num_function_names = 0;
361 if (functions_.size() > 0) {
362 size_t start = EmitSection(kFunctionSectionCode, buffer);
363 buffer.write_size(functions_.size());
364 for (auto function : functions_) {
365 function->WriteSignature(buffer);
366 if (!function->name_.is_empty()) ++num_function_names;
367 }
368 FixupSection(buffer, start);
369 }
370
371 // == emit function table ====================================================
372 if (indirect_functions_.size() > 0) {
373 size_t start = EmitSection(kTableSectionCode, buffer);
374 buffer.write_u8(1); // table count
375 buffer.write_u8(kLocalAnyFunc);
376 buffer.write_u8(kHasMaximumFlag);
377 buffer.write_size(indirect_functions_.size());
378 buffer.write_size(indirect_functions_.size());
379 FixupSection(buffer, start);
380 }
381
382 // == emit memory declaration ================================================
383 {
384 size_t start = EmitSection(kMemorySectionCode, buffer);
385 buffer.write_u8(1); // memory count
386 if (has_shared_memory_) {
387 buffer.write_u8(has_max_memory_size_ ? MemoryFlags::kSharedAndMaximum
388 : MemoryFlags::kSharedNoMaximum);
389 } else {
390 buffer.write_u8(has_max_memory_size_ ? MemoryFlags::kMaximum
391 : MemoryFlags::kNoMaximum);
392 }
393 buffer.write_u32v(min_memory_size_);
394 if (has_max_memory_size_) {
395 buffer.write_u32v(max_memory_size_);
396 }
397 FixupSection(buffer, start);
398 }
399
400 // == Emit globals ===========================================================
401 if (globals_.size() > 0) {
402 size_t start = EmitSection(kGlobalSectionCode, buffer);
403 buffer.write_size(globals_.size());
404
405 for (auto global : globals_) {
406 buffer.write_u8(ValueTypes::ValueTypeCodeFor(global.type));
407 buffer.write_u8(global.mutability ? 1 : 0);
408 switch (global.init.kind) {
409 case WasmInitExpr::kI32Const:
410 DCHECK_EQ(kWasmI32, global.type);
411 buffer.write_u8(kExprI32Const);
412 buffer.write_i32v(global.init.val.i32_const);
413 break;
414 case WasmInitExpr::kI64Const:
415 DCHECK_EQ(kWasmI64, global.type);
416 buffer.write_u8(kExprI64Const);
417 buffer.write_i64v(global.init.val.i64_const);
418 break;
419 case WasmInitExpr::kF32Const:
420 DCHECK_EQ(kWasmF32, global.type);
421 buffer.write_u8(kExprF32Const);
422 buffer.write_f32(global.init.val.f32_const);
423 break;
424 case WasmInitExpr::kF64Const:
425 DCHECK_EQ(kWasmF64, global.type);
426 buffer.write_u8(kExprF64Const);
427 buffer.write_f64(global.init.val.f64_const);
428 break;
429 case WasmInitExpr::kGlobalIndex:
430 buffer.write_u8(kExprGetGlobal);
431 buffer.write_u32v(global.init.val.global_index);
432 break;
433 default: {
434 // No initializer, emit a default value.
435 switch (global.type) {
436 case kWasmI32:
437 buffer.write_u8(kExprI32Const);
438 // LEB encoding of 0.
439 buffer.write_u8(0);
440 break;
441 case kWasmI64:
442 buffer.write_u8(kExprI64Const);
443 // LEB encoding of 0.
444 buffer.write_u8(0);
445 break;
446 case kWasmF32:
447 buffer.write_u8(kExprF32Const);
448 buffer.write_f32(0.f);
449 break;
450 case kWasmF64:
451 buffer.write_u8(kExprF64Const);
452 buffer.write_f64(0.);
453 break;
454 default:
455 UNREACHABLE();
456 }
457 }
458 }
459 buffer.write_u8(kExprEnd);
460 }
461 FixupSection(buffer, start);
462 }
463
464 // == emit exports ===========================================================
465 if (!function_exports_.empty()) {
466 size_t start = EmitSection(kExportSectionCode, buffer);
467 buffer.write_size(function_exports_.size());
468 for (auto function_export : function_exports_) {
469 buffer.write_string(function_export.name);
470 buffer.write_u8(kExternalFunction);
471 buffer.write_size(function_export.function_index +
472 function_imports_.size());
473 }
474 FixupSection(buffer, start);
475 }
476
477 // == emit start function index ==============================================
478 if (start_function_index_ >= 0) {
479 size_t start = EmitSection(kStartSectionCode, buffer);
480 buffer.write_size(start_function_index_ + function_imports_.size());
481 FixupSection(buffer, start);
482 }
483
484 // == emit function table elements ===========================================
485 if (indirect_functions_.size() > 0) {
486 size_t start = EmitSection(kElementSectionCode, buffer);
487 buffer.write_u8(1); // count of entries
488 buffer.write_u8(0); // table index
489 buffer.write_u8(kExprI32Const); // offset
490 buffer.write_u32v(0);
491 buffer.write_u8(kExprEnd);
492 buffer.write_size(indirect_functions_.size()); // element count
493
494 for (auto index : indirect_functions_) {
495 buffer.write_size(index + function_imports_.size());
496 }
497
498 FixupSection(buffer, start);
499 }
500
501 // == emit code ==============================================================
502 if (functions_.size() > 0) {
503 size_t start = EmitSection(kCodeSectionCode, buffer);
504 buffer.write_size(functions_.size());
505 for (auto function : functions_) {
506 function->WriteBody(buffer);
507 }
508 FixupSection(buffer, start);
509 }
510
511 // == emit data segments =====================================================
512 if (data_segments_.size() > 0) {
513 size_t start = EmitSection(kDataSectionCode, buffer);
514 buffer.write_size(data_segments_.size());
515
516 for (auto segment : data_segments_) {
517 buffer.write_u8(0); // linear memory segment
518 buffer.write_u8(kExprI32Const); // initializer expression for dest
519 buffer.write_u32v(segment.dest);
520 buffer.write_u8(kExprEnd);
521 buffer.write_u32v(static_cast<uint32_t>(segment.data.size()));
522 buffer.write(&segment.data[0], segment.data.size());
523 }
524 FixupSection(buffer, start);
525 }
526
527 // == Emit names =============================================================
528 if (num_function_names > 0 || !function_imports_.empty()) {
529 // Emit the section code.
530 buffer.write_u8(kUnknownSectionCode);
531 // Emit a placeholder for the length.
532 size_t start = buffer.reserve_u32v();
533 // Emit the section string.
534 buffer.write_size(4);
535 buffer.write(reinterpret_cast<const byte*>("name"), 4);
536 // Emit a subsection for the function names.
537 buffer.write_u8(NameSectionKindCode::kFunction);
538 // Emit a placeholder for the subsection length.
539 size_t functions_start = buffer.reserve_u32v();
540 // Emit the function names.
541 // Imports are always named.
542 uint32_t num_imports = static_cast<uint32_t>(function_imports_.size());
543 buffer.write_size(num_imports + num_function_names);
544 uint32_t function_index = 0;
545 for (; function_index < num_imports; ++function_index) {
546 const WasmFunctionImport* import = &function_imports_[function_index];
547 DCHECK(!import->name.is_empty());
548 buffer.write_u32v(function_index);
549 buffer.write_string(import->name);
550 }
551 if (num_function_names > 0) {
552 for (auto function : functions_) {
553 DCHECK_EQ(function_index,
554 function->func_index() + function_imports_.size());
555 if (!function->name_.is_empty()) {
556 buffer.write_u32v(function_index);
557 buffer.write_string(function->name_);
558 }
559 ++function_index;
560 }
561 }
562 FixupSection(buffer, functions_start);
563 FixupSection(buffer, start);
564 }
565 }
566
WriteAsmJsOffsetTable(ZoneBuffer & buffer) const567 void WasmModuleBuilder::WriteAsmJsOffsetTable(ZoneBuffer& buffer) const {
568 // == Emit asm.js offset table ===============================================
569 buffer.write_size(functions_.size());
570 // Emit the offset table per function.
571 for (auto function : functions_) {
572 function->WriteAsmWasmOffsetTable(buffer);
573 }
574 // Append a 0 to indicate that this is an encoded table.
575 buffer.write_u8(0);
576 }
577 } // namespace wasm
578 } // namespace internal
579 } // namespace v8
580