Diff
Modified: trunk/JSTests/ChangeLog (209650 => 209651)
--- trunk/JSTests/ChangeLog 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/JSTests/ChangeLog 2016-12-10 07:08:31 UTC (rev 209651)
@@ -1,5 +1,26 @@
2016-12-09 JF Bastien <[email protected]>
+ WebAssembly: implement data section
+ https://bugs.webkit.org/show_bug.cgi?id=165696
+
+ Reviewed by Keith Miller.
+
+ As specified in https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#data-section
+ Note that some of the interesting corner cases are ill-defined by the spec: https://github.com/WebAssembly/design/issues/897
+
+ * wasm/Builder.js: create a data section from _javascript_
+ * wasm/Builder_WebAssemblyBinary.js: assemble the data section into the proper binary encoding
+ (const.emitters.Data):
+ * wasm/js-api/test_Data.js: Added.
+ (DataSection):
+ (DataSectionOffTheEnd):
+ (DataSectionPartlyOffTheEnd):
+ (DataSectionEmptyOffTheEnd):
+ (DataSectionSeenByStart):
+ * wasm/self-test/test_BuilderJSON.js: make sure the JSON structure is fine (this sanity checks before going to binary)
+
+2016-12-09 JF Bastien <[email protected]>
+
WebAssembly JS API: implement start function
https://bugs.webkit.org/show_bug.cgi?id=165150
Modified: trunk/JSTests/wasm/Builder.js (209650 => 209651)
--- trunk/JSTests/wasm/Builder.js 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/JSTests/wasm/Builder.js 2016-12-10 07:08:31 UTC (rev 209651)
@@ -523,8 +523,38 @@
break;
case "Data":
- // FIXME implement data https://bugs.webkit.org/show_bug.cgi?id=161709
- this[section] = () => { throw new Error(`Unimplemented: section type "${section}"`); };
+ this[section] = function() {
+ const s = this._addSection(section);
+ const dataBuilder = {
+ End: () => this,
+ Segment: data ="" {
+ assert.isArray(data);
+ for (const datum of data) {
+ assert.isNumber(datum);
+ assert.ge(datum, 0);
+ assert.le(datum, 0xff);
+ }
+ s.data.push({ data: data, index: 0, offset: 0 });
+ let thisSegment = s.data[s.data.length - 1];
+ const segmentBuilder = {
+ End: () => dataBuilder,
+ Index: index => {
+ assert.eq(index, 0); // Linear memory index must be zero in MVP.
+ thisSegment.index = index;
+ return segmentBuilder;
+ },
+ Offset: offset => {
+ // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
+ assert.isNumber(offset);
+ thisSegment.offset = offset;
+ return segmentBuilder;
+ },
+ };
+ return segmentBuilder;
+ },
+ };
+ return dataBuilder;
+ };
break;
default:
Modified: trunk/JSTests/wasm/Builder_WebAssemblyBinary.js (209650 => 209651)
--- trunk/JSTests/wasm/Builder_WebAssemblyBinary.js 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/JSTests/wasm/Builder_WebAssemblyBinary.js 2016-12-10 07:08:31 UTC (rev 209651)
@@ -156,7 +156,20 @@
}
},
- Data: (section, bin) => { throw new Error(`Not yet implemented`); },
+ Data: (section, bin) => {
+ put(bin, "varuint32", section.data.length);
+ for (const datum of section.data) {
+ put(bin, "varuint32", datum.index);
+ // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
+ // For now we only handle i32.const as offset.
+ put(bin, "uint8", WASM.description.opcode["i32.const"].value);
+ put(bin, WASM.description.opcode["i32.const"].immediate[0].type, datum.offset);
+ put(bin, "uint8", WASM.description.opcode["end"].value);
+ put(bin, "varuint32", datum.data.length);
+ for (const byte of datum.data)
+ put(bin, "uint8", byte);
+ }
+ },
};
export const Binary = (preamble, sections) => {
Added: trunk/JSTests/wasm/js-api/test_Data.js (0 => 209651)
--- trunk/JSTests/wasm/js-api/test_Data.js (rev 0)
+++ trunk/JSTests/wasm/js-api/test_Data.js 2016-12-10 07:08:31 UTC (rev 209651)
@@ -0,0 +1,135 @@
+import * as assert from '../assert.js';
+import Builder from '../Builder.js';
+
+const memSizeInPages = 1;
+const pageSizeInBytes = 64 * 1024;
+const memoryDescription = { initial: memSizeInPages, maximum: memSizeInPages };
+
+// FIXME Some corner cases are ill-specified: https://github.com/WebAssembly/design/issues/897
+
+(function DataSection() {
+ const builder = (new Builder())
+ .Type().End()
+ .Import().Memory("imp", "memory", memoryDescription).End()
+ .Data()
+ .Segment([0xff, 0x2a]).Offset(4).End()
+ .Segment([0xde, 0xad, 0xbe, 0xef]).Offset(24).End()
+ .Segment([0xca, 0xfe]).Offset(25).End() // Overwrite.
+ .Segment([]).Offset(4).End() // Empty.
+ .End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const memory = new WebAssembly.Memory(memoryDescription);
+ const instance = new WebAssembly.Instance(module, { imp: { memory: memory } });
+ const buffer = new Uint8Array(memory.buffer);
+ for (let idx = 0; idx < memSizeInPages * pageSizeInBytes; ++idx) {
+ const value = buffer[idx];
+ switch (idx) {
+ case 4: assert.eq(value, 0xff); break;
+ case 5: assert.eq(value, 0x2a); break;
+ case 24: assert.eq(value, 0xde); break;
+ case 25: assert.eq(value, 0xca); break;
+ case 26: assert.eq(value, 0xfe); break;
+ case 27: assert.eq(value, 0xef); break;
+ default: assert.eq(value, 0x00); break;
+ }
+ }
+})();
+
+(function DataSectionOffTheEnd() {
+ const builder = (new Builder())
+ .Type().End()
+ .Import().Memory("imp", "memory", memoryDescription).End()
+ .Data()
+ .Segment([0xff]).Offset(memSizeInPages * pageSizeInBytes).End()
+ .End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const memory = new WebAssembly.Memory(memoryDescription);
+ assert.throws(() => new WebAssembly.Instance(module, { imp: { memory: memory } }), RangeError, `Data segment initializes memory out of range`);
+ const buffer = new Uint8Array(memory.buffer);
+ for (let idx = 0; idx < memSizeInPages * pageSizeInBytes; ++idx) {
+ const value = buffer[idx];
+ assert.eq(value, 0x00);
+ }
+})();
+
+(function DataSectionPartlyOffTheEnd() {
+ const builder = (new Builder())
+ .Type().End()
+ .Import().Memory("imp", "memory", memoryDescription).End()
+ .Data()
+ .Segment([0xff, 0xff]).Offset(memSizeInPages * pageSizeInBytes - 1).End()
+ .End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const memory = new WebAssembly.Memory(memoryDescription);
+ assert.throws(() => new WebAssembly.Instance(module, { imp: { memory: memory } }), RangeError, `Data segment initializes memory out of range`);
+ const buffer = new Uint8Array(memory.buffer);
+ for (let idx = 0; idx < memSizeInPages * pageSizeInBytes; ++idx) {
+ const value = buffer[idx];
+ assert.eq(value, 0x00);
+ }
+})();
+
+(function DataSectionEmptyOffTheEnd() {
+ const builder = (new Builder())
+ .Type().End()
+ .Import().Memory("imp", "memory", memoryDescription).End()
+ .Data()
+ .Segment([]).Offset(memSizeInPages * pageSizeInBytes).End()
+ .End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const memory = new WebAssembly.Memory(memoryDescription);
+ const instance = new WebAssembly.Instance(module, { imp: { memory: memory } });
+ const buffer = new Uint8Array(memory.buffer);
+ for (let idx = 0; idx < memSizeInPages * pageSizeInBytes; ++idx) {
+ const value = buffer[idx];
+ assert.eq(value, 0x00);
+ }
+})();
+
+(function DataSectionSeenByStart() {
+ const offset = 1024;
+ const builder = (new Builder())
+ .Type().End()
+ .Import()
+ .Memory("imp", "memory", memoryDescription)
+ .Function("imp", "func", { params: ["i32"] })
+ .End()
+ .Function().End()
+ .Start("foo").End()
+ .Code()
+ .Function("foo", { params: [] })
+ .I32Const(offset)
+ .I32Load8U(2, 0)
+ .Call(0) // Calls func((i8.load(offset), align=2, offset=0). This should observe 0xff as set by the data section.
+ .End()
+ .End()
+ .Data()
+ .Segment([0xff]).Offset(offset).End()
+ .End();
+ const bin = builder.WebAssembly().get();
+ const module = new WebAssembly.Module(bin);
+ const memory = new WebAssembly.Memory(memoryDescription);
+ let value = 0;
+ const setter = v => value = v;
+ const instance = new WebAssembly.Instance(
+ module,
+ {
+ imp: {
+ memory: memory,
+ func: setter
+ }
+ });
+ assert.eq(value, 0xff);
+ const buffer = new Uint8Array(memory.buffer);
+ for (let idx = 0; idx < memSizeInPages * pageSizeInBytes; ++idx) {
+ const value = buffer[idx];
+ if (idx == offset)
+ assert.eq(value, 0xff);
+ else
+ assert.eq(value, 0x00);
+ }
+})();
Modified: trunk/JSTests/wasm/self-test/test_BuilderJSON.js (209650 => 209651)
--- trunk/JSTests/wasm/self-test/test_BuilderJSON.js 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/JSTests/wasm/self-test/test_BuilderJSON.js 2016-12-10 07:08:31 UTC (rev 209651)
@@ -619,6 +619,7 @@
assert.eq(j.section[1].data[0].code.length, 6);
assert.eq(j.section[1].data[0].code[3].name, "select");
})();
+// FIXME test type mismatch with select. https://bugs.webkit.org/show_bug.cgi?id=163267
(function MemoryImport() {
const builder = (new Builder())
@@ -639,4 +640,27 @@
assert.eq(json.section[1].data[0].memoryDescription.maximum, 31);
})();
-// FIXME test type mismatch with select. https://bugs.webkit.org/show_bug.cgi?id=163267
+(function DataSection() {
+ const builder = (new Builder())
+ .Memory().InitialMaxPages(64, 64).End()
+ .Data()
+ .Segment([0xff, 0x2a]).Offset(4).End()
+ .Segment([0xde, 0xad, 0xbe, 0xef]).Index(0).Offset(24).End()
+ .End();
+ const json = JSON.parse(builder.json());
+ assert.eq(json.section.length, 2);
+ assert.eq(json.section[1].name, "Data");
+ assert.eq(json.section[1].data.length, 2);
+ assert.eq(json.section[1].data[0].index, 0);
+ assert.eq(json.section[1].data[0].offset, 4);
+ assert.eq(json.section[1].data[0].data.length, 2);
+ assert.eq(json.section[1].data[0].data[0], 0xff);
+ assert.eq(json.section[1].data[0].data[1], 0x2a);
+ assert.eq(json.section[1].data[1].index, 0);
+ assert.eq(json.section[1].data[1].offset, 24);
+ assert.eq(json.section[1].data[1].data.length, 4);
+ assert.eq(json.section[1].data[1].data[0], 0xde);
+ assert.eq(json.section[1].data[1].data[1], 0xad);
+ assert.eq(json.section[1].data[1].data[2], 0xbe);
+ assert.eq(json.section[1].data[1].data[3], 0xef);
+})();
Modified: trunk/Source/_javascript_Core/ChangeLog (209650 => 209651)
--- trunk/Source/_javascript_Core/ChangeLog 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-12-10 07:08:31 UTC (rev 209651)
@@ -1,3 +1,28 @@
+2016-12-09 JF Bastien <[email protected]>
+
+ WebAssembly: implement data section
+ https://bugs.webkit.org/show_bug.cgi?id=165696
+
+ Reviewed by Keith Miller.
+
+ As specified in https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#data-section
+ Note that some of the interesting corner cases are ill-defined by the spec: https://github.com/WebAssembly/design/issues/897
+
+ * wasm/WasmFormat.h: segments are what represent sections of memory to initialize (similar to ELF's non-zero intializer data / rodata)
+ (JSC::Wasm::Segment::make):
+ (JSC::Wasm::Segment::destroy):
+ (JSC::Wasm::Segment::byte):
+ (JSC::Wasm::Segment::makePtr):
+ * wasm/WasmModuleParser.cpp: parse the data section, and prevent a few overflows if a user passes in UINT_MAX (the loops would overflow)
+ (JSC::Wasm::ModuleParser::parseType):
+ (JSC::Wasm::ModuleParser::parseImport):
+ (JSC::Wasm::ModuleParser::parseFunction):
+ (JSC::Wasm::ModuleParser::parseExport):
+ (JSC::Wasm::ModuleParser::parseCode):
+ (JSC::Wasm::ModuleParser::parseData):
+ * wasm/js/WebAssemblyModuleRecord.cpp:
+ (JSC::WebAssemblyModuleRecord::evaluate): the only sensible time to initialize the data section is after linking, but before calling start, I test for this but the spec isn't clear it's correct yet
+
2016-12-09 Karim H <[email protected]>
It is okay to turn undefined into null because we are producing values for a
Modified: trunk/Source/_javascript_Core/wasm/WasmFormat.h (209650 => 209651)
--- trunk/Source/_javascript_Core/wasm/WasmFormat.h 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/Source/_javascript_Core/wasm/WasmFormat.h 2016-12-10 07:08:31 UTC (rev 209651)
@@ -35,6 +35,8 @@
#include "WasmMemoryInformation.h"
#include "WasmOps.h"
#include "WasmPageCount.h"
+#include <memory>
+#include <wtf/FastMalloc.h>
#include <wtf/Optional.h>
#include <wtf/Vector.h>
@@ -114,6 +116,35 @@
size_t end;
};
+struct Segment {
+ uint32_t offset;
+ uint32_t sizeInBytes;
+ // Bytes are allocated at the end.
+ static Segment* make(uint32_t offset, uint32_t sizeInBytes)
+ {
+ auto allocated = tryFastCalloc(sizeof(Segment) + sizeInBytes, 1);
+ Segment* segment;
+ if (!allocated.getValue(segment))
+ return nullptr;
+ segment->offset = offset;
+ segment->sizeInBytes = sizeInBytes;
+ return segment;
+ }
+ static void destroy(Segment *segment)
+ {
+ fastFree(segment);
+ }
+ uint8_t& byte(uint32_t pos)
+ {
+ ASSERT(pos < sizeInBytes);
+ return *reinterpret_cast<uint8_t*>(reinterpret_cast<char*>(this) + sizeof(offset) + sizeof(sizeInBytes) + pos);
+ }
+ typedef std::unique_ptr<Segment, decltype(&Segment::destroy)> Ptr;
+ static Ptr makePtr(Segment* segment)
+ {
+ return Ptr(segment, &Segment::destroy);
+ }
+};
struct ModuleInformation {
Vector<Signature> signatures;
@@ -125,6 +156,7 @@
MemoryInformation memory;
Vector<Export> exports;
std::optional<uint32_t> startFunctionIndexSpace;
+ Vector<Segment::Ptr> data;
~ModuleInformation();
};
Modified: trunk/Source/_javascript_Core/wasm/WasmModuleParser.cpp (209650 => 209651)
--- trunk/Source/_javascript_Core/wasm/WasmModuleParser.cpp 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/Source/_javascript_Core/wasm/WasmModuleParser.cpp 2016-12-10 07:08:31 UTC (rev 209651)
@@ -157,12 +157,12 @@
bool ModuleParser::parseType()
{
uint32_t count;
- if (!parseVarUInt32(count))
+ if (!parseVarUInt32(count)
+ || count == std::numeric_limits<uint32_t>::max()
+ || !m_module->signatures.tryReserveCapacity(count))
return false;
if (verbose)
- dataLogLn("count: ", count);
- if (!m_module->signatures.tryReserveCapacity(count))
- return false;
+ dataLogLn(" count: ", count);
for (uint32_t i = 0; i < count; ++i) {
int8_t type;
@@ -175,17 +175,15 @@
dataLogLn("Got function type.");
uint32_t argumentCount;
- if (!parseVarUInt32(argumentCount))
+ Vector<Type> argumentTypes;
+ if (!parseVarUInt32(argumentCount)
+ || argumentCount == std::numeric_limits<uint32_t>::max()
+ || !argumentTypes.tryReserveCapacity(argumentCount))
return false;
-
if (verbose)
- dataLogLn("argumentCount: ", argumentCount);
+ dataLogLn(" argument count: ", argumentCount);
- Vector<Type> argumentTypes;
- if (!argumentTypes.tryReserveCapacity(argumentCount))
- return false;
-
- for (unsigned i = 0; i != argumentCount; ++i) {
+ for (unsigned i = 0; i < argumentCount; ++i) {
Type argumentType;
if (!parseResultType(argumentType))
return false;
@@ -216,14 +214,14 @@
bool ModuleParser::parseImport()
{
uint32_t importCount;
- if (!parseVarUInt32(importCount))
- return false;
- if (!m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
+ if (!parseVarUInt32(importCount)
+ || importCount == std::numeric_limits<uint32_t>::max()
+ || !m_module->imports.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
|| !m_module->importFunctions.tryReserveCapacity(importCount) // FIXME this over-allocates when we fix the FIXMEs below.
|| !m_functionIndexSpace.tryReserveCapacity(importCount)) // FIXME this over-allocates when we fix the FIXMEs below. We'll allocate some more here when we know how many functions to expect.
return false;
- for (uint32_t importNumber = 0; importNumber != importCount; ++importNumber) {
+ for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) {
Import imp;
uint32_t moduleLen;
uint32_t fieldLen;
@@ -278,12 +276,13 @@
{
uint32_t count;
if (!parseVarUInt32(count)
+ || count == std::numeric_limits<uint32_t>::max()
|| !m_module->internalFunctionSignatures.tryReserveCapacity(count)
|| !m_functionLocationInBinary.tryReserveCapacity(count)
|| !m_functionIndexSpace.tryReserveCapacity(m_functionIndexSpace.size() + count))
return false;
- for (uint32_t i = 0; i != count; ++i) {
+ for (uint32_t i = 0; i < count; ++i) {
uint32_t typeNumber;
if (!parseVarUInt32(typeNumber)
|| typeNumber >= m_module->signatures.size())
@@ -374,10 +373,11 @@
{
uint32_t exportCount;
if (!parseVarUInt32(exportCount)
+ || exportCount == std::numeric_limits<uint32_t>::max()
|| !m_module->exports.tryReserveCapacity(exportCount))
return false;
- for (uint32_t exportNumber = 0; exportNumber != exportCount; ++exportNumber) {
+ for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) {
Export exp;
uint32_t fieldLen;
String fieldString;
@@ -385,8 +385,10 @@
|| !consumeUTF8String(fieldString, fieldLen))
return false;
exp.field = Identifier::fromString(m_vm, fieldString);
+
if (!parseExternalKind(exp.kind))
return false;
+
switch (exp.kind) {
case External::Function: {
if (!parseVarUInt32(exp.functionIndex)
@@ -440,10 +442,11 @@
{
uint32_t count;
if (!parseVarUInt32(count)
+ || count == std::numeric_limits<uint32_t>::max()
|| count != m_functionLocationInBinary.size())
return false;
- for (uint32_t i = 0; i != count; ++i) {
+ for (uint32_t i = 0; i < count; ++i) {
uint32_t functionSize;
if (!parseVarUInt32(functionSize)
|| functionSize > length()
@@ -460,8 +463,56 @@
bool ModuleParser::parseData()
{
- // FIXME https://bugs.webkit.org/show_bug.cgi?id=161709
- RELEASE_ASSERT_NOT_REACHED();
+ uint32_t segmentCount;
+ if (!parseVarUInt32(segmentCount)
+ || segmentCount == std::numeric_limits<uint32_t>::max()
+ || !m_module->data.tryReserveCapacity(segmentCount))
+ return false;
+ if (verbose)
+ dataLogLn(" segments: ", segmentCount);
+
+ for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) {
+ if (verbose)
+ dataLogLn(" segment #", segmentNumber);
+ uint32_t index;
+ uint8_t opcode;
+ uint32_t offset;
+ uint8_t endOpcode;
+ uint32_t dataByteLength;
+ if (!parseVarUInt32(index)
+ || index)
+ return false;
+
+ // FIXME allow complex init_expr here. https://bugs.webkit.org/show_bug.cgi?id=165700
+ // For now we only handle i32.const as offset.
+ if (!parseUInt8(opcode)
+ || opcode != Wasm::I32Const
+ || !parseVarUInt32(offset)
+ || !parseUInt8(endOpcode)
+ || endOpcode != Wasm::End)
+ return false;
+ if (verbose)
+ dataLogLn(" offset: ", offset);
+
+ if (!parseVarUInt32(dataByteLength)
+ || dataByteLength == std::numeric_limits<uint32_t>::max())
+ return false;
+ if (verbose)
+ dataLogLn(" data bytes: ", dataByteLength);
+
+ Segment* segment = Segment::make(offset, dataByteLength);
+ if (!segment)
+ return false;
+ m_module->data.uncheckedAppend(Segment::makePtr(segment));
+ for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) {
+ uint8_t byte;
+ if (!parseUInt8(byte))
+ return false;
+ segment->byte(dataByte) = byte;
+ if (verbose)
+ dataLogLn(" [", dataByte, "] = ", segment->byte(dataByte));
+ }
+ }
return true;
}
Modified: trunk/Source/_javascript_Core/wasm/js/WebAssemblyModuleRecord.cpp (209650 => 209651)
--- trunk/Source/_javascript_Core/wasm/js/WebAssemblyModuleRecord.cpp 2016-12-10 06:04:28 UTC (rev 209650)
+++ trunk/Source/_javascript_Core/wasm/js/WebAssemblyModuleRecord.cpp 2016-12-10 07:08:31 UTC (rev 209651)
@@ -187,9 +187,27 @@
JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
{
+ VM& vm = state->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (JSWebAssemblyMemory* jsMemory = m_instance->memory()) {
+ uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory());
+ auto sizeInBytes = jsMemory->memory()->size();
+ if (memory) {
+ const Vector<Wasm::Segment::Ptr>& data = ""
+ for (auto& segment : data) {
+ if (segment->sizeInBytes) {
+ if (sizeInBytes < segment->sizeInBytes
+ || segment->offset > sizeInBytes
+ || segment->offset > sizeInBytes - segment->sizeInBytes)
+ return throwException(state, scope, createRangeError(state, ASCIILiteral("Data segment initializes memory out of range")));
+ memcpy(memory + segment->offset, &segment->byte(0), segment->sizeInBytes);
+ }
+ }
+ }
+ }
+
if (WebAssemblyFunction* startFunction = m_startFunction.get()) {
- VM& vm = state->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
ProtoCallFrame protoCallFrame;
protoCallFrame.init(nullptr, startFunction, JSValue(), 1, nullptr);
startFunction->call(vm, &protoCallFrame);