Bug 1253137 - Baldr: use length+bytes instead of c-strings (r=sunfish)
authorLuke Wagner <luke@mozilla.com>
Sun, 06 Mar 2016 17:46:23 -0600
changeset 287004 0d9926dd10ea11482e186c39a3c6ab4d66f55093
parent 287003 5eabc3a7368a09eb317dabd86b598e4f32ff6088
child 287005 6acd12ff78fd490ac5cdd833a305023c1516a333
push id18032
push usercbook@mozilla.com
push dateMon, 07 Mar 2016 10:38:51 +0000
treeherderfx-team@087905ffec78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1253137
milestone47.0a1
Bug 1253137 - Baldr: use length+bytes instead of c-strings (r=sunfish) MozReview-Commit-ID: 1GYHSyxx6n1
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmBinary.h
js/src/asmjs/WasmText.cpp
js/src/jit-test/tests/wasm/binary.js
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -242,27 +242,29 @@ DecodeCallIndirect(FunctionDecoder& f, E
         return false;
 
     return DecodeCallWithSig(f, f.mg().sig(sigIndex), type);
 }
 
 static bool
 DecodeConstI32(FunctionDecoder& f, ExprType* type)
 {
-    if (!f.d().readVarU32())
+    uint32_t _;
+    if (!f.d().readVarU32(&_))
         return f.fail("unable to read i32.const immediate");
 
     *type = ExprType::I32;
     return true;
 }
 
 static bool
 DecodeConstI64(FunctionDecoder& f, ExprType* type)
 {
-    if (!f.d().readVarU64())
+    uint64_t _;
+    if (!f.d().readVarU64(&_))
         return f.fail("unable to read i64.const immediate");
 
     *type = ExprType::I64;
     return true;
 }
 
 static bool
 DecodeConstF32(FunctionDecoder& f, ExprType* type)
@@ -829,34 +831,16 @@ DecodeExpr(FunctionDecoder& f, ExprType*
         // can return Expr values for which there is no enumerator.
         break;
     }
 
     return f.fail("bad expression code");
 }
 
 /*****************************************************************************/
-// dynamic link data
-
-struct ImportName
-{
-    UniqueChars module;
-    UniqueChars func;
-
-    ImportName(UniqueChars module, UniqueChars func)
-      : module(Move(module)), func(Move(func))
-    {}
-    ImportName(ImportName&& rhs)
-      : module(Move(rhs.module)), func(Move(rhs.func))
-    {}
-};
-
-typedef Vector<ImportName, 0, SystemAllocPolicy> ImportNameVector;
-
-/*****************************************************************************/
 // wasm decoding and generation
 
 typedef HashSet<const DeclaredSig*, SigHashPolicy> SigSet;
 
 static bool
 DecodeSignatures(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
     uint32_t sectionStart;
@@ -1037,38 +1021,53 @@ CheckTypeForJS(JSContext* cx, Decoder& d
     }
 
     if (sig.ret() == ExprType::I64)
         return Fail(cx, d, "cannot import/export i64 return type");
 
     return true;
 }
 
+struct ImportName
+{
+    Bytes module;
+    Bytes func;
+
+    ImportName(Bytes&& module, Bytes&& func)
+      : module(Move(module)), func(Move(func))
+    {}
+    ImportName(ImportName&& rhs)
+      : module(Move(rhs.module)), func(Move(rhs.func))
+    {}
+};
+
+typedef Vector<ImportName, 0, SystemAllocPolicy> ImportNameVector;
+
 static bool
 DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
 {
     const DeclaredSig* sig;
     if (!DecodeSignatureIndex(cx, d, *init, &sig))
         return false;
 
     if (!init->imports.emplaceBack(sig))
         return false;
 
     if (!CheckTypeForJS(cx, d, *sig))
         return false;
 
-    UniqueChars moduleName = d.readCString();
-    if (!moduleName)
+    Bytes moduleName;
+    if (!d.readBytes(&moduleName))
         return Fail(cx, d, "expected import module name");
 
-    if (!*moduleName.get())
+    if (moduleName.empty())
         return Fail(cx, d, "module name cannot be empty");
 
-    UniqueChars funcName = d.readCString();
-    if (!funcName)
+    Bytes funcName;
+    if (!d.readBytes(&funcName))
         return Fail(cx, d, "expected import func name");
 
     return importNames->emplaceBack(Move(moduleName), Move(funcName));
 }
 
 static bool
 DecodeImportTable(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
 {
@@ -1143,24 +1142,36 @@ DecodeMemory(JSContext* cx, Decoder& d, 
 
     mg.initHeapUsage(HeapUsage::Unshared);
     return true;
 }
 
 typedef HashSet<const char*, CStringHasher> CStringSet;
 
 static UniqueChars
-DecodeFieldName(JSContext* cx, Decoder& d, CStringSet* dupSet)
+DecodeExportName(JSContext* cx, Decoder& d, CStringSet* dupSet)
 {
-    UniqueChars fieldName = d.readCString();
-    if (!fieldName) {
-        Fail(cx, d, "expected export external name string");
+    Bytes fieldBytes;
+    if (!d.readBytes(&fieldBytes)) {
+        Fail(cx, d, "expected export name");
         return nullptr;
     }
 
+    if (memchr(fieldBytes.begin(), 0, fieldBytes.length())) {
+        Fail(cx, d, "null in export names not yet supported");
+        return nullptr;
+    }
+
+    if (!fieldBytes.append(0))
+        return nullptr;
+
+    UniqueChars fieldName((char*)fieldBytes.extractRawBuffer());
+    if (!fieldName)
+        return nullptr;
+
     CStringSet::AddPtr p = dupSet->lookupForAdd(fieldName.get());
     if (p) {
         Fail(cx, d, "duplicate export");
         return nullptr;
     }
 
     if (!dupSet->add(p, fieldName.get()))
         return nullptr;
@@ -1176,17 +1187,17 @@ DecodeFunctionExport(JSContext* cx, Deco
         return Fail(cx, d, "expected export internal index");
 
     if (funcIndex >= mg.numFuncSigs())
         return Fail(cx, d, "export function index out of range");
 
     if (!CheckTypeForJS(cx, d, mg.funcSig(funcIndex)))
         return false;
 
-    UniqueChars fieldName = DecodeFieldName(cx, d, dupSet);
+    UniqueChars fieldName = DecodeExportName(cx, d, dupSet);
     if (!fieldName)
         return false;
 
     return mg.declareExport(Move(fieldName), funcIndex);
 }
 
 static bool
 DecodeExportTable(JSContext* cx, Decoder& d, ModuleGenerator& mg)
@@ -1335,17 +1346,17 @@ DecodeDataSegments(JSContext* cx, Decode
         uint32_t numBytes;
         if (!d.readVarU32(&numBytes))
             return Fail(cx, d, "expected segment size");
 
         if (dstOffset > heapLength || heapLength - dstOffset < numBytes)
             return Fail(cx, d, "data segment does not fit in memory");
 
         const uint8_t* src;
-        if (!d.readRawData(numBytes, &src))
+        if (!d.readBytesRaw(numBytes, &src))
             return Fail(cx, d, "data segment shorter than declared");
 
         memcpy(heapBase + dstOffset, src, numBytes);
         prevEnd = dstOffset + numBytes;
     }
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "data section byte size mismatch");
@@ -1447,44 +1458,44 @@ CheckCompilerSupport(JSContext* cx)
 #endif
         JS_ReportError(cx, "WebAssembly is not supported on the current device.");
         return false;
     }
     return true;
 }
 
 static bool
-GetProperty(JSContext* cx, HandleObject obj, const char* utf8Chars, MutableHandleValue v)
+GetProperty(JSContext* cx, HandleObject obj, const Bytes& bytes, MutableHandleValue v)
 {
-    JSAtom* atom = AtomizeUTF8Chars(cx, utf8Chars, strlen(utf8Chars));
+    JSAtom* atom = AtomizeUTF8Chars(cx, (char*)bytes.begin(), bytes.length());
     if (!atom)
         return false;
 
     RootedId id(cx, AtomToId(atom));
     return GetProperty(cx, obj, obj, id, v);
 }
 
 static bool
 ImportFunctions(JSContext* cx, HandleObject importObj, const ImportNameVector& importNames,
                 MutableHandle<FunctionVector> imports)
 {
     if (!importNames.empty() && !importObj)
         return Fail(cx, "no import object given");
 
     for (const ImportName& name : importNames) {
         RootedValue v(cx);
-        if (!GetProperty(cx, importObj, name.module.get(), &v))
+        if (!GetProperty(cx, importObj, name.module, &v))
             return false;
 
-        if (*name.func.get()) {
+        if (!name.func.empty()) {
             if (!v.isObject())
                 return Fail(cx, "import object field is not an Object");
 
             RootedObject obj(cx, &v.toObject());
-            if (!GetProperty(cx, obj, name.func.get(), &v))
+            if (!GetProperty(cx, obj, name.func, &v))
                 return false;
         }
 
         if (!IsFunctionObject(v))
             return Fail(cx, "import object field is not a Function");
 
         if (!imports.append(&v.toObject().as<JSFunction>()))
             return false;
@@ -1519,16 +1530,17 @@ wasm::Eval(JSContext* cx, Handle<TypedAr
     UniqueChars file;
     if (!DescribeScriptedCaller(cx, &file))
         return false;
 
     ImportNameVector importNames;
     UniqueExportMap exportMap;
     Rooted<ArrayBufferObject*> heap(cx);
     Rooted<WasmModuleObject*> moduleObj(cx);
+
     if (!DecodeModule(cx, Move(file), bytes, length, &importNames, &exportMap, &heap, &moduleObj)) {
         if (!cx->isExceptionPending())
             ReportOutOfMemory(cx);
         return false;
     }
 
     Rooted<FunctionVector> imports(cx, FunctionVector(cx));
     if (!ImportFunctions(cx, importObj, importNames, &imports))
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -376,30 +376,27 @@ class Encoder
     }
 
     uint32_t varU32ByteLength(size_t offset) const {
         size_t start = offset;
         while (bytes_[offset] & 0x80)
             offset++;
         return offset - start + 1;
     }
-    static const uint32_t EnumSentinel = 0x3fff;
 
     template <class T>
     MOZ_WARN_UNUSED_RESULT bool writePatchableEnum(size_t* offset) {
-        static_assert(uint32_t(T::Limit) <= EnumSentinel, "reserve enough bits");
         *offset = bytes_.length();
-        return writeVarU32(EnumSentinel);
+        return writeVarU32(uint32_t(T::Limit));
     }
 
     template <class T>
     void patchEnum(size_t offset, T v) {
-        static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits");
         MOZ_ASSERT(uint32_t(v) < uint32_t(T::Limit));
-        return patchVarU32(offset, uint32_t(v), EnumSentinel);
+        return patchVarU32(offset, uint32_t(v), uint32_t(T::Limit));
     }
 
   public:
     explicit Encoder(Bytes& bytes)
       : bytes_(bytes)
     {
         MOZ_ASSERT(empty());
     }
@@ -465,39 +462,37 @@ class Encoder
 
     MOZ_WARN_UNUSED_RESULT bool writePatchableExpr(size_t* offset) {
         return writePatchableEnum<Expr>(offset);
     }
     void patchExpr(size_t offset, Expr expr) {
         patchEnum(offset, expr);
     }
 
-    // C-strings are written in UTF8 and null-terminated while raw data can
-    // contain nulls and instead has an explicit byte length.
+    // Byte ranges start with an LEB128 length followed by an arbitrary sequence
+    // of bytes. When used for strings, bytes are to be interpreted as utf8.
 
-    MOZ_WARN_UNUSED_RESULT bool writeCString(const char* cstr) {
-        return bytes_.append(reinterpret_cast<const uint8_t*>(cstr), strlen(cstr) + 1);
-    }
-    MOZ_WARN_UNUSED_RESULT bool writeRawData(const uint8_t* bytes, uint32_t numBytes) {
-        return bytes_.append(bytes, numBytes);
+    MOZ_WARN_UNUSED_RESULT bool writeBytes(const void* bytes, uint32_t numBytes) {
+        return writeVarU32(numBytes) &&
+               bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
     }
 
     // A "section" is a contiguous range of bytes that stores its own size so
     // that it may be trivially skipped without examining the contents. Sections
     // require backpatching since the size of the section is only known at the
-    // end while the size's uint32 must be stored at the beginning. Immediately
+    // end while the size's varU32 must be stored at the beginning. Immediately
     // after the section length is the string id of the section.
 
     template <size_t IdSizeWith0>
     MOZ_WARN_UNUSED_RESULT bool startSection(const char (&id)[IdSizeWith0], size_t* offset) {
         static const size_t IdSize = IdSizeWith0 - 1;
         MOZ_ASSERT(id[IdSize] == '\0');
         return writePatchableVarU32(offset) &&
                writeVarU32(IdSize) &&
-               writeRawData(reinterpret_cast<const uint8_t*>(id), IdSize);
+               bytes_.append(reinterpret_cast<const uint8_t*>(id), IdSize);
     }
     void finishSection(size_t offset) {
         return patchVarU32(offset, bytes_.length() - offset - varU32ByteLength(offset));
     }
 
     // Temporary encoding forms which should be removed as part of the
     // conversion to wasm:
 
@@ -523,30 +518,28 @@ class Decoder
     const uint8_t* const beg_;
     const uint8_t* const end_;
     const uint8_t* cur_;
 
     template <class T>
     MOZ_WARN_UNUSED_RESULT bool read(T* out) {
         if (bytesRemain() < sizeof(T))
             return false;
-        if (out)
-            memcpy((void*)out, cur_, sizeof(T));
+        memcpy((void*)out, cur_, sizeof(T));
         cur_ += sizeof(T);
         return true;
     }
 
     template <class T>
     MOZ_WARN_UNUSED_RESULT bool readEnum(T* out) {
         static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits");
         uint32_t u32;
         if (!readVarU32(&u32) || u32 >= uint32_t(T::Limit))
             return false;
-        if (out)
-            *out = T(u32);
+        *out = T(u32);
         return true;
     }
 
     template <class T>
     T uncheckedRead() {
         MOZ_ASSERT(bytesRemain() >= sizeof(T));
         T ret;
         memcpy(&ret, cur_, sizeof(T));
@@ -556,38 +549,36 @@ class Decoder
 
     template <class T>
     T uncheckedReadEnum() {
         static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits");
         return (T)uncheckedReadVarU32();
     }
 
     template <typename UInt>
-    MOZ_WARN_UNUSED_RESULT bool readVarU(UInt* out = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readVarU(UInt* out) {
         const unsigned numBits = sizeof(UInt) * CHAR_BIT;
         const unsigned remainderBits = numBits % 7;
         const unsigned numBitsInSevens = numBits - remainderBits;
         UInt u = 0;
         uint8_t byte;
         UInt shift = 0;
         do {
             if (!readFixedU8(&byte))
                 return false;
             if (!(byte & 0x80)) {
-                if (out)
-                    *out = u | UInt(byte) << shift;
+                *out = u | UInt(byte) << shift;
                 return true;
             }
             u |= UInt(byte & 0x7F) << shift;
             shift += 7;
         } while (shift != numBitsInSevens);
         if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits)))
             return false;
-        if (out)
-            *out = u | UInt(byte) << numBitsInSevens;
+        *out = u | UInt(byte) << numBitsInSevens;
         return true;
     }
 
   public:
     Decoder(const uint8_t* begin, const uint8_t* end)
       : beg_(begin),
         end_(end),
         cur_(begin)
@@ -614,64 +605,65 @@ class Decoder
     }
     size_t currentOffset() const {
         return cur_ - beg_;
     }
 
     // Fixed-size encoding operations simply copy the literal bytes (without
     // attempting to align).
 
-    MOZ_WARN_UNUSED_RESULT bool readFixedU32(uint32_t* u = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readFixedU32(uint32_t* u) {
         return read<uint32_t>(u);
     }
-    MOZ_WARN_UNUSED_RESULT bool readFixedF32(float* f = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readFixedF32(float* f) {
         return read<float>(f);
     }
-    MOZ_WARN_UNUSED_RESULT bool readFixedF64(double* d = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readFixedF64(double* d) {
         return read<double>(d);
     }
-    MOZ_WARN_UNUSED_RESULT bool readFixedI32x4(I32x4* i32x4 = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readFixedI32x4(I32x4* i32x4) {
         return read<I32x4>(i32x4);
     }
-    MOZ_WARN_UNUSED_RESULT bool readFixedF32x4(F32x4* f32x4 = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readFixedF32x4(F32x4* f32x4) {
         return read<F32x4>(f32x4);
     }
 
     // Variable-length encodings that all use LEB128.
 
-    MOZ_WARN_UNUSED_RESULT bool readVarU32(uint32_t* out = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readVarU32(uint32_t* out) {
         return readVarU<uint32_t>(out);
     }
-    MOZ_WARN_UNUSED_RESULT bool readVarU64(uint64_t* out = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readVarU64(uint64_t* out) {
         return readVarU<uint64_t>(out);
     }
-    MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr) {
         return readEnum(expr);
     }
     MOZ_WARN_UNUSED_RESULT bool readValType(ValType* type) {
         return readEnum(type);
     }
     MOZ_WARN_UNUSED_RESULT bool readExprType(ExprType* type) {
         return readEnum(type);
     }
 
-    // C-strings are written in UTF8 and null-terminated while raw data can
-    // contain nulls and instead has an explicit byte length.
+    // See writeBytes comment.
 
-    MOZ_WARN_UNUSED_RESULT UniqueChars readCString() {
-        const char* begin = reinterpret_cast<const char*>(cur_);
-        for (; cur_ != end_; cur_++) {
-            if (!*cur_) {
-                cur_++;
-                return UniqueChars(DuplicateString(begin));
-            }
-        }
-        return nullptr;
+    MOZ_WARN_UNUSED_RESULT bool readBytes(Bytes* bytes) {
+        uint32_t numBytes;
+        if (!readVarU32(&numBytes))
+            return false;
+        if (bytesRemain() < numBytes)
+            return false;
+        if (!bytes->resize(numBytes))
+            return false;
+        memcpy(bytes->begin(), cur_, numBytes);
+        cur_ += numBytes;
+        return true;
     }
-    MOZ_WARN_UNUSED_RESULT bool readRawData(uint32_t numBytes, const uint8_t** bytes = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readBytesRaw(uint32_t numBytes, const uint8_t** bytes) {
         if (bytes)
             *bytes = cur_;
         if (bytesRemain() < numBytes)
             return false;
         cur_ += numBytes;
         return true;
     }
 
@@ -774,17 +766,17 @@ class Decoder
     }
     ValType uncheckedReadValType() {
         return uncheckedReadEnum<ValType>();
     }
 
     // Temporary encoding forms which should be removed as part of the
     // conversion to wasm:
 
-    MOZ_WARN_UNUSED_RESULT bool readFixedU8(uint8_t* i = nullptr) {
+    MOZ_WARN_UNUSED_RESULT bool readFixedU8(uint8_t* i) {
         return read<uint8_t>(i);
     }
     uint8_t uncheckedReadFixedU8() {
         return uncheckedRead<uint8_t>();
     }
 };
 
 // Reusable macro encoding/decoding functions reused by both the two
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -3777,33 +3777,33 @@ EncodeFunctionSignatures(Encoder& e, Was
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeCString(Encoder& e, WasmName wasmName)
+EncodeBytes(Encoder& e, WasmName wasmName)
 {
     TwoByteChars range(wasmName.begin(), wasmName.length());
     UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str());
-    return utf8 && e.writeCString(utf8.get());
+    return utf8 && e.writeBytes(utf8.get(), strlen(utf8.get()));
 }
 
 static bool
 EncodeImport(Encoder& e, WasmAstImport& imp)
 {
     if (!e.writeVarU32(imp.sigIndex()))
         return false;
 
-    if (!EncodeCString(e, imp.module()))
+    if (!EncodeBytes(e, imp.module()))
         return false;
 
-    if (!EncodeCString(e, imp.func()))
+    if (!EncodeBytes(e, imp.func()))
         return false;
 
     return true;
 }
 
 static bool
 EncodeImportTable(Encoder& e, WasmAstModule& module)
 {
@@ -3861,17 +3861,17 @@ EncodeMemory(Encoder& e, WasmAstModule& 
 }
 
 static bool
 EncodeFunctionExport(Encoder& e, WasmAstExport& exp)
 {
     if (!e.writeVarU32(exp.func().index()))
         return false;
 
-    if (!EncodeCString(e, exp.name()))
+    if (!EncodeBytes(e, exp.name()))
         return false;
 
     return true;
 }
 
 static bool
 EncodeExportTable(Encoder& e, WasmAstModule& module)
 {
@@ -3986,20 +3986,17 @@ EncodeDataSegment(Encoder& e, WasmAstSeg
     const char16_t* cur = text.begin();
     const char16_t* end = text.end();
     while (cur != end) {
         uint8_t byte;
         MOZ_ALWAYS_TRUE(ConsumeTextByte(&cur, end, &byte));
         bytes.infallibleAppend(byte);
     }
 
-    if (!e.writeVarU32(bytes.length()))
-        return false;
-
-    if (!e.writeRawData(bytes.begin(), bytes.length()))
+    if (!e.writeBytes(bytes.begin(), bytes.length()))
         return false;
 
     return true;
 }
 
 static bool
 EncodeDataSegments(Encoder& e, WasmAstModule& module)
 {
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -74,26 +74,30 @@ assertErrorMessage(() => wasmEval(toU8(m
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 1))), TypeError, sectionError);
 assertErrorMessage(() => wasmEval(toU8(moduleHeaderThen(0, 0))), TypeError, unknownSectionError);
 
 function cstring(name) {
     return (name + '\0').split('').map(c => c.charCodeAt(0));
 }
 
 function string(name) {
-    return name.split('').map(c => c.charCodeAt(0));
+    var nameBytes = name.split('').map(c => {
+        var code = c.charCodeAt(0);
+        assertEq(code < 128, true); // TODO
+        return code
+    });
+    return varU32(nameBytes.length).concat(nameBytes);
 }
 
 function moduleWithSections(sectionArray) {
     var bytes = moduleHeaderThen();
     for (let section of sectionArray) {
-        var nameLength = varU32(section.name.length);
-        bytes.push(...varU32(nameLength.length + section.name.length + section.body.length));
-        bytes.push(...nameLength);
-        bytes.push(...string(section.name));
+        var sectionName = string(section.name);
+        bytes.push(...varU32(sectionName.length + section.body.length));
+        bytes.push(...sectionName);
         bytes.push(...section.body);
     }
     return toU8(bytes);
 }
 
 function sigSection(sigs) {
     var body = [];
     body.push(...varU32(sigs.length));
@@ -128,18 +132,18 @@ function bodySection(bodies) {
     return { name: functionBodiesId, body };
 }
 
 function importSection(imports) {
     var body = [];
     body.push(...varU32(imports.length));
     for (let imp of imports) {
         body.push(...varU32(imp.sigIndex));
-        body.push(...cstring(imp.module));
-        body.push(...cstring(imp.func));
+        body.push(...string(imp.module));
+        body.push(...string(imp.func));
     }
     return { name: importId, body };
 }
 
 function tableSection(elems) {
     var body = [];
     body.push(...varU32(elems.length));
     for (let i of elems)