Bug 1308510 - Default to non-experimental text format. r?luke draft
authorYury Delendik <ydelendik@mozilla.com>
Thu, 13 Oct 2016 14:30:17 -0500
changeset 424916 dcca7cce521fd7eaecc63d9b53b8a906c19f39a7
parent 424914 7452437b3ab571b1d60aed4e973d82a1471f72b2
child 424917 377d60dcf09bc18fa0d5652fa53477b2346a13f9
push id32288
push userydelendik@mozilla.com
push dateThu, 13 Oct 2016 19:47:11 +0000
reviewersluke
bugs1308510
milestone52.0a1
Bug 1308510 - Default to non-experimental text format. r?luke MozReview-Commit-ID: 8IZIGP1hN5J
js/src/asmjs/WasmBinaryToExperimentalText.cpp
js/src/asmjs/WasmBinaryToExperimentalText.h
js/src/asmjs/WasmBinaryToText.cpp
js/src/asmjs/WasmBinaryToText.h
js/src/asmjs/WasmCode.cpp
js/src/asmjs/WasmCode.h
js/src/asmjs/WasmGeneratedSourceMap.h
js/src/jit-test/tests/debug/wasm-05.js
--- a/js/src/asmjs/WasmBinaryToExperimentalText.cpp
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.cpp
@@ -54,74 +54,16 @@ enum PrintOperatorPrecedence
     NegatePrecedence = 12,
     EqzPrecedence = 12,
     OperatorPrecedence = 15,
     LoadOperatorPrecedence = 15,
     CallPrecedence = 15,
     GroupPrecedence = 16,
 };
 
-// StringBuffer wrapper to track the position (line and column) within the generated
-// source.
-class WasmPrintBuffer
-{
-    StringBuffer& stringBuffer_;
-    uint32_t lineno_;
-    uint32_t column_;
-
-  public:
-    explicit WasmPrintBuffer(StringBuffer& stringBuffer) : stringBuffer_(stringBuffer), lineno_(1), column_(1) {}
-    inline char processChar(char ch) {
-        if (ch == '\n') {
-            lineno_++; column_ = 1;
-        } else
-            column_++;
-        return ch;
-    }
-    inline char16_t processChar(char16_t ch) {
-        if (ch == '\n') {
-            lineno_++; column_ = 1;
-        } else
-            column_++;
-        return ch;
-    }
-    bool append(const char ch) {
-        return stringBuffer_.append(processChar(ch));
-    }
-    bool append(const char16_t ch) {
-        return stringBuffer_.append(processChar(ch));
-    }
-    bool append(const char* str, size_t length) {
-        for (size_t i = 0; i < length; i++)
-            processChar(str[i]);
-        return stringBuffer_.append(str, length);
-    }
-    bool append(const char16_t* begin, const char16_t* end) {
-        for (const char16_t* p = begin; p != end; p++)
-            processChar(*p);
-        return stringBuffer_.append(begin, end);
-    }
-    bool append(const char16_t* str, size_t length) {
-        return append(str, str + length);
-    }
-    template <size_t ArrayLength>
-    bool append(const char (&array)[ArrayLength]) {
-        return append(array, ArrayLength - 1);
-    }
-    char16_t getChar(size_t index) {
-        return stringBuffer_.getChar(index);
-    }
-    size_t length() {
-        return stringBuffer_.length();
-    }
-    StringBuffer& stringBuffer() { return stringBuffer_; }
-    uint32_t lineno() { return lineno_; }
-    uint32_t column() { return column_; }
-};
-
 struct WasmPrintContext
 {
     JSContext* cx;
     AstModule* module;
     WasmPrintBuffer& buffer;
     const ExperimentalTextFormatting& f;
     GeneratedSourceMap* maybeSourceMap;
     uint32_t indent;
--- a/js/src/asmjs/WasmBinaryToExperimentalText.h
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.h
@@ -14,20 +14,19 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_binary_to_experimental_text_h
 #define wasm_binary_to_experimental_text_h
 
-#include "mozilla/Vector.h"
-
 #include "NamespaceImports.h"
 
+#include "asmjs/WasmGeneratedSourceMap.h"
 #include "gc/Rooting.h"
 #include "js/Class.h"
 
 namespace js {
 
 class StringBuffer;
 
 namespace wasm {
@@ -40,69 +39,16 @@ struct ExperimentalTextFormatting
 
     ExperimentalTextFormatting()
      : allowAsciiOperators(true),
        reduceParens(true),
        groupBlocks(true)
     {}
 };
 
-// The generated source location for the AST node/expression. The offset field refers
-// an offset in an binary format file.
-struct ExprLoc
-{
-    uint32_t lineno;
-    uint32_t column;
-    uint32_t offset;
-    ExprLoc() : lineno(0), column(0), offset(0) {}
-    ExprLoc(uint32_t lineno_, uint32_t column_, uint32_t offset_) : lineno(lineno_), column(column_), offset(offset_) {}
-};
-
-typedef mozilla::Vector<ExprLoc, 0, TempAllocPolicy> ExprLocVector;
-
-// The generated source WebAssembly function lines and expressions ranges.
-struct FunctionLoc
-{
-    size_t startExprsIndex;
-    size_t endExprsIndex;
-    uint32_t startLineno;
-    uint32_t endLineno;
-    FunctionLoc(size_t startExprsIndex_, size_t endExprsIndex_, uint32_t startLineno_, uint32_t endLineno_)
-      : startExprsIndex(startExprsIndex_),
-        endExprsIndex(endExprsIndex_),
-        startLineno(startLineno_),
-        endLineno(endLineno_)
-    {}
-};
-
-typedef mozilla::Vector<FunctionLoc, 0, TempAllocPolicy> FunctionLocVector;
-
-// The generated source map for WebAssembly binary file. This map is generated during
-// building the text buffer (see BinaryToExperimentalText).
-class GeneratedSourceMap
-{
-    ExprLocVector exprlocs_;
-    FunctionLocVector functionlocs_;
-    uint32_t totalLines_;
-
-  public:
-    explicit GeneratedSourceMap(JSContext* cx)
-     : exprlocs_(cx),
-       functionlocs_(cx),
-       totalLines_(0)
-    {}
-    ExprLocVector& exprlocs() { return exprlocs_; }
-    FunctionLocVector& functionlocs() { return functionlocs_; }
-
-    uint32_t totalLines() { return totalLines_; }
-    void setTotalLines(uint32_t val) { totalLines_ = val; }
-};
-
-typedef UniquePtr<GeneratedSourceMap> UniqueGeneratedSourceMap;
-
 // Translate the given binary representation of a wasm module into the module's textual
 // representation.
 
 MOZ_MUST_USE bool
 BinaryToExperimentalText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
                          const ExperimentalTextFormatting& formatting = ExperimentalTextFormatting(),
                          GeneratedSourceMap* sourceMap = nullptr);
 
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -34,24 +34,27 @@ using namespace js::wasm;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 
 struct WasmRenderContext
 {
     JSContext* cx;
     AstModule* module;
-    StringBuffer& buffer;
+    WasmPrintBuffer& buffer;
+    GeneratedSourceMap* maybeSourceMap;
     uint32_t indent;
 
     uint32_t currentFuncIndex;
 
-    WasmRenderContext(JSContext* cx, AstModule* module, StringBuffer& buffer)
-      : cx(cx), module(module), buffer(buffer), indent(0), currentFuncIndex(0)
+    WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer, GeneratedSourceMap* sourceMap)
+      : cx(cx), module(module), buffer(buffer), maybeSourceMap(sourceMap), indent(0), currentFuncIndex(0)
     {}
+
+    StringBuffer& sb() { return buffer.stringBuffer(); }
 };
 
 /*****************************************************************************/
 // utilities
 
 static bool
 RenderIndent(WasmRenderContext& c)
 {
@@ -60,51 +63,51 @@ RenderIndent(WasmRenderContext& c)
             return false;
     }
     return true;
 }
 
 static bool
 RenderInt32(WasmRenderContext& c, int32_t num)
 {
-    return NumberValueToStringBuffer(c.cx, Int32Value(num), c.buffer);
+    return NumberValueToStringBuffer(c.cx, Int32Value(num), c.sb());
 }
 
 static bool
 RenderInt64(WasmRenderContext& c, int64_t num)
 {
     if (num < 0 && !c.buffer.append("-"))
         return false;
     if (!num)
         return c.buffer.append("0");
-    return RenderInBase<10>(c.buffer, mozilla::Abs(num));
+    return RenderInBase<10>(c.sb(), mozilla::Abs(num));
 }
 
 static bool
 RenderDouble(WasmRenderContext& c, RawF64 num)
 {
     double d = num.fp();
     if (IsNaN(d))
-        return RenderNaN(c.buffer, num);
+        return RenderNaN(c.sb(), num);
     if (IsNegativeZero(d))
         return c.buffer.append("-0");
     if (IsInfinite(d)) {
         if (d > 0)
             return c.buffer.append("infinity");
         return c.buffer.append("-infinity");
     }
-    return NumberValueToStringBuffer(c.cx, DoubleValue(d), c.buffer);
+    return NumberValueToStringBuffer(c.cx, DoubleValue(d), c.sb());
 }
 
 static bool
 RenderFloat32(WasmRenderContext& c, RawF32 num)
 {
     float f = num.fp();
     if (IsNaN(f))
-        return RenderNaN(c.buffer, num);
+        return RenderNaN(c.sb(), num);
     return RenderDouble(c, RawF64(double(f)));
 }
 
 static bool
 RenderEscapedString(WasmRenderContext& c, const AstName& s)
 {
     size_t length = s.length();
     const char16_t* p = s.begin();
@@ -218,45 +221,56 @@ RenderBlockNameAndSignature(WasmRenderCo
     }
 
     return true;
 }
 
 static bool
 RenderExpr(WasmRenderContext& c, AstExpr& expr);
 
+#define MAP_AST_EXPR(c, expr)                                                         \
+    if (c.maybeSourceMap) {                                                           \
+        uint32_t lineno = c.buffer.lineno();                                          \
+        uint32_t column = c.buffer.column();                                          \
+        if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset())) \
+            return false;                                                             \
+    }
+
 /*****************************************************************************/
 // binary format parsing and rendering
 
 static bool
-RenderNop(WasmRenderContext& c)
+RenderNop(WasmRenderContext& c, AstNop& nop)
 {
     if (!RenderIndent(c))
         return false;
+    MAP_AST_EXPR(c, nop);
     return c.buffer.append("nop\n");
 }
 
 static bool
 RenderDrop(WasmRenderContext& c, AstDrop& drop)
 {
     if (!RenderExpr(c, drop.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
+    MAP_AST_EXPR(c, drop);
     if (!c.buffer.append("drop\n"))
         return false;
     return true;
 }
 
 static bool
-RenderUnreachable(WasmRenderContext& c)
+RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable)
 {
     if (!RenderIndent(c))
         return false;
+    MAP_AST_EXPR(c, unreachable);
     return c.buffer.append("unreachable\n");
 }
 
 static bool
 RenderCallArgs(WasmRenderContext& c, const AstExprVector& args)
 {
     for (uint32_t i = 0; i < args.length(); i++) {
         if (!RenderExpr(c, *args[i]))
@@ -270,16 +284,17 @@ static bool
 RenderCall(WasmRenderContext& c, AstCall& call)
 {
     if (!RenderCallArgs(c, call.args()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, call);
     if (call.expr() == Expr::Call) {
         if (!c.buffer.append("call "))
             return false;
     } else if (call.expr() == Expr::CallImport) {
         if (!c.buffer.append("call_import "))
             return false;
     } else {
         return false;
@@ -301,16 +316,17 @@ RenderCallIndirect(WasmRenderContext& c,
         return false;
 
     if (!RenderExpr(c, *call.index()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, call);
     if (!c.buffer.append("call_indirect "))
         return false;
     if (!RenderRef(c, call.sig()))
         return false;
 
     if (!c.buffer.append('\n'))
         return false;
 
@@ -318,16 +334,17 @@ RenderCallIndirect(WasmRenderContext& c,
 }
 
 static bool
 RenderConst(WasmRenderContext& c, AstConst& cst)
 {
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, cst);
     if (!RenderValType(c, cst.val().type()))
         return false;
     if (!c.buffer.append(".const "))
         return false;
 
     switch (ToExprType(cst.val().type())) {
       case ExprType::I32:
         if (!RenderInt32(c, (uint32_t)cst.val().i32()))
@@ -355,16 +372,17 @@ RenderConst(WasmRenderContext& c, AstCon
 }
 
 static bool
 RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl)
 {
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, gl);
     if (!c.buffer.append("get_local "))
         return false;
     if (!RenderRef(c, gl.local()))
         return false;
     if (!c.buffer.append('\n'))
         return false;
     return true;
 }
@@ -373,16 +391,17 @@ static bool
 RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl)
  {
     if (!RenderExpr(c, sl.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, sl);
     if (!c.buffer.append("set_local "))
         return false;
     if (!RenderRef(c, sl.local()))
         return false;
 
     if (!c.buffer.append('\n'))
         return false;
     return true;
@@ -392,16 +411,17 @@ static bool
 RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl)
 {
     if (!RenderExpr(c, tl.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, tl);
     if (!c.buffer.append("tee_local "))
         return false;
     if (!RenderRef(c, tl.local()))
         return false;
 
     if (!c.buffer.append('\n'))
         return false;
     return true;
@@ -418,16 +438,17 @@ RenderExprList(WasmRenderContext& c, con
 }
 
 static bool
 RenderBlock(WasmRenderContext& c, AstBlock& block)
 {
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, block);
     if (block.expr() == Expr::Block) {
         if (!c.buffer.append("block"))
             return false;
     } else if (block.expr() == Expr::Loop) {
         if (!c.buffer.append("loop"))
             return false;
     } else
         return false;
@@ -480,16 +501,17 @@ static bool
 RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& op)
 {
     if (!RenderExpr(c, *op.op()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, op);
     const char* opStr;
     switch (op.expr()) {
       case Expr::I32Eqz:     opStr = "i32.eqz"; break;
       case Expr::I32Clz:     opStr = "i32.clz"; break;
       case Expr::I32Ctz:     opStr = "i32.ctz"; break;
       case Expr::I32Popcnt:  opStr = "i32.popcnt"; break;
       case Expr::I64Clz:     opStr = "i64.clz"; break;
       case Expr::I64Ctz:     opStr = "i64.ctz"; break;
@@ -521,16 +543,17 @@ RenderBinaryOperator(WasmRenderContext& 
     if (!RenderExpr(c, *op.lhs()))
         return false;
     if (!RenderExpr(c, *op.rhs()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, op);
     const char* opStr;
     switch (op.expr()) {
       case Expr::I32Add:      opStr = "i32.add"; break;
       case Expr::I32Sub:      opStr = "i32.sub"; break;
       case Expr::I32Mul:      opStr = "i32.mul"; break;
       case Expr::I32DivS:     opStr = "i32.div_s"; break;
       case Expr::I32DivU:     opStr = "i32.div_u"; break;
       case Expr::I32RemS:     opStr = "i32.rem_s"; break;
@@ -586,16 +609,17 @@ RenderTernaryOperator(WasmRenderContext&
     if (!RenderExpr(c, *op.op1()))
         return false;
     if (!RenderExpr(c, *op.op2()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, op);
     const char* opStr;
     switch (op.expr()) {
       case Expr::Select: opStr = "select"; break;
       default: return false;
     }
     if (!c.buffer.append(opStr, strlen(opStr)))
         return false;
 
@@ -611,16 +635,17 @@ RenderComparisonOperator(WasmRenderConte
     if (!RenderExpr(c, *op.lhs()))
         return false;
     if (!RenderExpr(c, *op.rhs()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, op);
     const char* opStr;
     switch (op.expr()) {
       case Expr::I32Eq:  opStr = "i32.eq"; break;
       case Expr::I32Ne:  opStr = "i32.ne"; break;
       case Expr::I32LtS: opStr = "i32.lt_s"; break;
       case Expr::I32LtU: opStr = "i32.lt_u"; break;
       case Expr::I32LeS: opStr = "i32.le_s"; break;
       case Expr::I32LeU: opStr = "i32.le_u"; break;
@@ -665,16 +690,17 @@ static bool
 RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& op)
 {
     if (!RenderExpr(c, *op.op()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, op);
     const char* opStr;
     switch (op.expr()) {
       case Expr::I32WrapI64:        opStr = "i32.wrap/i64"; break;
       case Expr::I32TruncSF32:      opStr = "i32.trunc_s/f32"; break;
       case Expr::I32TruncUF32:      opStr = "i32.trunc_u/f32"; break;
       case Expr::I32ReinterpretF32: opStr = "i32.reinterpret/f32"; break;
       case Expr::I32TruncSF64:      opStr = "i32.trunc_s/f64"; break;
       case Expr::I32TruncUF64:      opStr = "i32.trunc_u/f64"; break;
@@ -711,16 +737,17 @@ static bool
 RenderIf(WasmRenderContext& c, AstIf& if_)
 {
     if (!RenderExpr(c, if_.cond()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, if_);
     if (!c.buffer.append("if"))
         return false;
 
     if (!RenderBlockNameAndSignature(c, if_.name(), if_.type()))
         return false;
 
     if (!if_.name().empty()) {
         if (!c.buffer.append(' '))
@@ -791,16 +818,17 @@ static bool
 RenderLoad(WasmRenderContext& c, AstLoad& load)
 {
     if (!RenderLoadStoreBase(c, load.address()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, load);
     uint32_t defaultAlignLog2;
     switch (load.expr()) {
       case Expr::I32Load8S:
         if (!c.buffer.append("i32.load8_s"))
             return false;
         defaultAlignLog2 = 0;
         break;
       case Expr::I64Load8S:
@@ -885,16 +913,17 @@ RenderStore(WasmRenderContext& c, AstSto
         return false;
 
     if (!RenderExpr(c, store.value()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, store);
     uint32_t defaultAlignLog2;
     switch (store.expr()) {
       case Expr::I32Store8:
         if (!c.buffer.append("i32.store8"))
             return false;
         defaultAlignLog2 = 0;
         break;
       case Expr::I64Store8:
@@ -961,16 +990,17 @@ RenderBranch(WasmRenderContext& c, AstBr
     if (branch.maybeValue()) {
         if (!RenderExpr(c, *(branch.maybeValue())))
             return false;
     }
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, branch);
     if (expr == Expr::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br "))
         return false;
 
     if (!RenderRef(c, branch.target()))
         return false;
 
     return c.buffer.append('\n');
 }
@@ -985,16 +1015,17 @@ RenderBrTable(WasmRenderContext& c, AstB
 
     // Index
     if (!RenderExpr(c, table.index()))
         return false;
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, table);
     if (!c.buffer.append("br_table "))
         return false;
 
     uint32_t tableLength = table.table().length();
     for (uint32_t i = 0; i < tableLength; i++) {
         if (!RenderRef(c, table.table()[i]))
             return false;
 
@@ -1014,32 +1045,33 @@ RenderReturn(WasmRenderContext& c, AstRe
     if (ret.maybeExpr()) {
         if (!RenderExpr(c, *(ret.maybeExpr())))
             return false;
     }
 
     if (!RenderIndent(c))
         return false;
 
+    MAP_AST_EXPR(c, ret);
     if (!c.buffer.append("return"))
         return false;
 
     return c.buffer.append('\n');
 }
 
 static bool
 RenderExpr(WasmRenderContext& c, AstExpr& expr)
 {
     switch (expr.kind()) {
       case AstExprKind::Drop:
         return RenderDrop(c, expr.as<AstDrop>());
       case AstExprKind::Nop:
-        return RenderNop(c);
+        return RenderNop(c, expr.as<AstNop>());
       case AstExprKind::Unreachable:
-        return RenderUnreachable(c);
+        return RenderUnreachable(c, expr.as<AstUnreachable>());
       case AstExprKind::Call:
         return RenderCall(c, expr.as<AstCall>());
       case AstExprKind::CallIndirect:
         return RenderCallIndirect(c, expr.as<AstCallIndirect>());
       case AstExprKind::Const:
         return RenderConst(c, expr.as<AstConst>());
       case AstExprKind::GetLocal:
         return RenderGetLocal(c, expr.as<AstGetLocal>());
@@ -1292,16 +1324,19 @@ RenderExportSection(WasmRenderContext& c
     return true;
 }
 
 static bool
 RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::SigVector& sigs)
 {
     const AstSig* sig = sigs[func.sig().index()];
 
+    size_t startExprIndex = c.maybeSourceMap ? c.maybeSourceMap->exprlocs().length() : 0;
+    uint32_t startLineno = c.buffer.lineno();
+
     uint32_t argsNum = sig->args().length();
     uint32_t localsNum = func.vars().length();
     if (localsNum > 0) {
         if (!RenderIndent(c))
             return false;
         for (uint32_t i = 0; i < localsNum; i++) {
             if (!c.buffer.append("(local "))
                 return false;
@@ -1324,16 +1359,24 @@ RenderFunctionBody(WasmRenderContext& c,
 
 
     uint32_t exprsNum = func.body().length();
     for (uint32_t i = 0; i < exprsNum; i++) {
       if (!RenderExpr(c, *func.body()[i]))
           return false;
     }
 
+    size_t endExprIndex = c.maybeSourceMap ? c.maybeSourceMap->exprlocs().length() : 0;
+    uint32_t endLineno = c.buffer.lineno();
+
+    if (c.maybeSourceMap) {
+        if (!c.maybeSourceMap->functionlocs().emplaceBack(startExprIndex, endExprIndex, startLineno, endLineno))
+            return false;
+    }
+
     return true;
 }
 
 static bool
 RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs, const AstModule::SigVector& sigs)
 {
     uint32_t numFuncBodies = funcs.length();
     for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
@@ -1456,30 +1499,33 @@ RenderModule(WasmRenderContext& c, AstMo
     c.indent--;
 
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
 
+#undef MAP_AST_EXPR
+
 /*****************************************************************************/
 // Top-level functions
 
 bool
-wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer)
+wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer, GeneratedSourceMap* sourceMap)
 {
 
     LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
 
     AstModule* module;
     if (!BinaryToAst(cx, bytes, length, lifo, &module))
         return false;
 
-    WasmRenderContext c(cx, module, buffer);
+    WasmPrintBuffer buf(buffer);
+    WasmRenderContext c(cx, module, buf, sourceMap);
 
     if (!RenderModule(c, *module)) {
         if (!cx->isExceptionPending())
             ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
--- a/js/src/asmjs/WasmBinaryToText.h
+++ b/js/src/asmjs/WasmBinaryToText.h
@@ -16,28 +16,30 @@
  * limitations under the License.
  */
 
 #ifndef wasm_binary_to_text_h
 #define wasm_binary_to_text_h
 
 #include "NamespaceImports.h"
 
+#include "asmjs/WasmGeneratedSourceMap.h"
 #include "gc/Rooting.h"
 #include "js/Class.h"
 
 namespace js {
 
 class StringBuffer;
 
 namespace wasm {
 
 // Translate the given binary representation of a wasm module into the module's textual
 // representation.
 
 MOZ_MUST_USE bool
-BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer);
+BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
+             GeneratedSourceMap* sourceMap = nullptr);
 
 }  // namespace wasm
 
 }  // namespace js
 
 #endif // namespace wasm_binary_to_text_h
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -19,16 +19,17 @@
 #include "asmjs/WasmCode.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/EnumeratedRange.h"
 
 #include "jsprf.h"
 
+#include "asmjs/WasmBinaryToText.h"
 #include "asmjs/WasmModule.h"
 #include "asmjs/WasmSerialize.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/MacroAssembler.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "vm/StringBuffer.h"
@@ -709,20 +710,18 @@ Code::createText(JSContext* cx)
         const Bytes& bytes = maybeBytecode_->bytes;
         if (!buffer.append(experimentalWarning))
             return nullptr;
 
         maybeSourceMap_.reset(cx->runtime()->new_<GeneratedSourceMap>(cx));
         if (!maybeSourceMap_)
             return nullptr;
 
-        if (!BinaryToExperimentalText(cx, bytes.begin(), bytes.length(), buffer,
-                                      ExperimentalTextFormatting(), maybeSourceMap_.get())) {
+        if (!BinaryToText(cx, bytes.begin(), bytes.length(), buffer, maybeSourceMap_.get()))
             return nullptr;
-        }
 
 #if DEBUG
         // Checking source map invariant: expression and function locations must be sorted
         // by line number.
         uint32_t lastLineno = 0;
         for (const ExprLoc& loc : maybeSourceMap_->exprlocs()) {
             MOZ_ASSERT(lastLineno <= loc.lineno);
             lastLineno = loc.lineno;
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -14,17 +14,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_code_h
 #define wasm_code_h
 
-#include "asmjs/WasmBinaryToExperimentalText.h"
+#include "asmjs/WasmGeneratedSourceMap.h"
 #include "asmjs/WasmTypes.h"
 
 namespace js {
 
 struct AsmJSMetadata;
 
 namespace wasm {
 
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmGeneratedSourceMap.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_generated_source_map_h
+#define wasm_generated_source_map_h
+
+#include "mozilla/Vector.h"
+
+#include "vm/StringBuffer.h"
+
+namespace js {
+
+namespace wasm {
+
+// The generated source location for the AST node/expression. The offset field refers
+// an offset in an binary format file.
+struct ExprLoc
+{
+    uint32_t lineno;
+    uint32_t column;
+    uint32_t offset;
+    ExprLoc() : lineno(0), column(0), offset(0) {}
+    ExprLoc(uint32_t lineno_, uint32_t column_, uint32_t offset_) : lineno(lineno_), column(column_), offset(offset_) {}
+};
+
+typedef mozilla::Vector<ExprLoc, 0, TempAllocPolicy> ExprLocVector;
+
+// The generated source WebAssembly function lines and expressions ranges.
+struct FunctionLoc
+{
+    size_t startExprsIndex;
+    size_t endExprsIndex;
+    uint32_t startLineno;
+    uint32_t endLineno;
+    FunctionLoc(size_t startExprsIndex_, size_t endExprsIndex_, uint32_t startLineno_, uint32_t endLineno_)
+      : startExprsIndex(startExprsIndex_),
+        endExprsIndex(endExprsIndex_),
+        startLineno(startLineno_),
+        endLineno(endLineno_)
+    {}
+};
+
+typedef mozilla::Vector<FunctionLoc, 0, TempAllocPolicy> FunctionLocVector;
+
+// The generated source map for WebAssembly binary file. This map is generated during
+// building the text buffer (see BinaryToExperimentalText).
+class GeneratedSourceMap
+{
+    ExprLocVector exprlocs_;
+    FunctionLocVector functionlocs_;
+    uint32_t totalLines_;
+
+  public:
+    explicit GeneratedSourceMap(JSContext* cx)
+     : exprlocs_(cx),
+       functionlocs_(cx),
+       totalLines_(0)
+    {}
+    ExprLocVector& exprlocs() { return exprlocs_; }
+    FunctionLocVector& functionlocs() { return functionlocs_; }
+
+    uint32_t totalLines() { return totalLines_; }
+    void setTotalLines(uint32_t val) { totalLines_ = val; }
+};
+
+typedef UniquePtr<GeneratedSourceMap> UniqueGeneratedSourceMap;
+
+// Helper class, StringBuffer wrapper, to track the position (line and column)
+// within the generated source.
+class WasmPrintBuffer
+{
+    StringBuffer& stringBuffer_;
+    uint32_t lineno_;
+    uint32_t column_;
+
+  public:
+    explicit WasmPrintBuffer(StringBuffer& stringBuffer) : stringBuffer_(stringBuffer), lineno_(1), column_(1) {}
+    inline char processChar(char ch) {
+        if (ch == '\n') {
+            lineno_++; column_ = 1;
+        } else
+            column_++;
+        return ch;
+    }
+    inline char16_t processChar(char16_t ch) {
+        if (ch == '\n') {
+            lineno_++; column_ = 1;
+        } else
+            column_++;
+        return ch;
+    }
+    bool append(const char ch) {
+        return stringBuffer_.append(processChar(ch));
+    }
+    bool append(const char16_t ch) {
+        return stringBuffer_.append(processChar(ch));
+    }
+    bool append(const char* str, size_t length) {
+        for (size_t i = 0; i < length; i++)
+            processChar(str[i]);
+        return stringBuffer_.append(str, length);
+    }
+    bool append(const char16_t* begin, const char16_t* end) {
+        for (const char16_t* p = begin; p != end; p++)
+            processChar(*p);
+        return stringBuffer_.append(begin, end);
+    }
+    bool append(const char16_t* str, size_t length) {
+        return append(str, str + length);
+    }
+    template <size_t ArrayLength>
+    bool append(const char (&array)[ArrayLength]) {
+        return append(array, ArrayLength - 1);
+    }
+    char16_t getChar(size_t index) {
+        return stringBuffer_.getChar(index);
+    }
+    size_t length() {
+        return stringBuffer_.length();
+    }
+    StringBuffer& stringBuffer() { return stringBuffer_; }
+    uint32_t lineno() { return lineno_; }
+    uint32_t column() { return column_; }
+};
+
+}  // namespace wasm
+
+}  // namespace js
+
+#endif // namespace wasm_generated_source_map_h
--- a/js/src/jit-test/tests/debug/wasm-05.js
+++ b/js/src/jit-test/tests/debug/wasm-05.js
@@ -28,22 +28,11 @@ var result1 = getAllOffsets('(module \
 )');
 
 var nopLine = result1.filter(i => i.str.indexOf('nop') >= 0);
 assertEq(nopLine.length, 1);
 // The nopLine shall have single offset.
 assertEq(nopLine[0].offsets.length, 1);
 assertEq(nopLine[0].offsets[0] > 0, true);
 
-var sqrtLine = result1.filter(i => i.str.indexOf('sqrt') >= 0);
-assertEq(sqrtLine.length, 1);
-// The sqrtLine shall have 5 offsets but they were produced from AST postorder decoding:
-//   drop(f32.sqrt(1.0 + 2.0)) ~~> 88,87,86,76,81
-assertEq(sqrtLine[0].offsets.length, 5);
-assertEq(sqrtLine[0].offsets[3] > 0, true);
-assertEq(sqrtLine[0].offsets[3] < sqrtLine[0].offsets[4], true);
-assertEq(sqrtLine[0].offsets[2] > sqrtLine[0].offsets[3], true);
-assertEq(sqrtLine[0].offsets[1] > sqrtLine[0].offsets[2], true);
-assertEq(sqrtLine[0].offsets[0] > sqrtLine[0].offsets[1], true);
-
-var noOffsetLines = result1.filter(i => i.str.indexOf('nop') < 0 && i.str.indexOf('sqrt') < 0);
-// Other lines will not have offsets.
-assertEq(noOffsetLines.every(i => i.offsets.length == 0), true);
+var singleOffsetLines = result1.filter(i => i.offsets.length === 1);
+// There shall be total 6 lines with single offset.
+assertEq(singleOffsetLines.length === 6, true);