025031fde813bbae447604e96a34ec390fc317d9
[WebKit.git] / Source / WebCore / inspector / front-end / CompilerSourceMapping.js
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  */
34 WebInspector.CompilerSourceMapping = function()
35 {
36 }
37
38 WebInspector.CompilerSourceMapping.prototype = {
39     compiledLocationToSourceLocation: function(lineNumber, columnNumber)
40     {
41         // Should be implemented by subclasses.
42     },
43
44     sourceLocationToCompiledLocation: function(sourceURL, lineNumber, columnNumber)
45     {
46         // Should be implemented by subclasses.
47     },
48
49     sources: function()
50     {
51         // Should be implemented by subclasses.
52     }
53 }
54
55 /**
56  * Implements Source Map V3 consumer. See http://code.google.com/p/closure-compiler/wiki/SourceMaps
57  * for format description.
58  * @extends {WebInspector.CompilerSourceMapping}
59  * @constructor
60  */
61 WebInspector.ClosureCompilerSourceMapping = function(payload)
62 {
63     if (!WebInspector.ClosureCompilerSourceMapping.prototype._base64Map) {
64         base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
65         WebInspector.ClosureCompilerSourceMapping.prototype._base64Map = {};
66         for (var i = 0; i < base64Digits.length; ++i)
67             WebInspector.ClosureCompilerSourceMapping.prototype._base64Map[base64Digits.charAt(i)] = i;
68     }
69
70     this._sources = payload.sources;
71     this._mappings = this._parsePayload(payload);
72 }
73
74 WebInspector.ClosureCompilerSourceMapping.prototype = {
75     compiledLocationToSourceLocation: function(lineNumber, columnNumber)
76     {
77         var mapping = this._findMapping(lineNumber, columnNumber);
78         var sourceURL = this._sources[mapping[2]];
79         return { sourceURL: sourceURL, lineNumber: mapping[3], columnNumber: mapping[4] };
80     },
81
82     sourceLocationToCompiledLocation: function(sourceURL, lineNumber, columnNumber)
83     {
84     },
85
86     sources: function()
87     {
88         return this._sources;
89     },
90
91     _findMapping: function(lineNumber, columnNumber)
92     {
93         var first = 0;
94         var count = this._mappings.length;
95         while (count > 1) {
96           var step = count >> 1;
97           var middle = first + step;
98           var mapping = this._mappings[middle];
99           if (lineNumber < mapping[0] || (lineNumber == mapping[0] && columnNumber < mapping[1]))
100               count = step;
101           else {
102               first = middle;
103               count -= step;
104           }
105         }
106         return this._mappings[first];
107     },
108
109     _parsePayload: function(payload)
110     {
111         var mappings = [];
112         var stringCharIterator = new WebInspector.ClosureCompilerSourceMapping.StringCharIterator(payload.mappings);
113
114         var lineNumber = 0;
115         var columnNumber = 0;
116         var sourceIndex = 0;
117         var sourceLineNumber = 0;
118         var sourceColumnNumber = 0;
119         var nameIndex = 0;
120         do {
121             columnNumber += this._decodeVLQ(stringCharIterator);
122             if (this._isSeparator(stringCharIterator.peek()))
123                 continue;
124             sourceIndex += this._decodeVLQ(stringCharIterator);
125             sourceLineNumber += this._decodeVLQ(stringCharIterator);
126             sourceColumnNumber += this._decodeVLQ(stringCharIterator);
127             var mapping = [lineNumber, columnNumber, sourceIndex, sourceLineNumber, sourceColumnNumber];
128             if (!this._isSeparator(stringCharIterator.peek())) {
129                 nameIndex += this._decodeVLQ(stringCharIterator);
130                 mapping.push(nameIndex);
131             }
132             mappings.push(mapping);
133             if (stringCharIterator.next() === ";") {
134                 lineNumber += 1;
135                 columnNumber = 0;
136             }
137         } while(stringCharIterator.hasNext());
138         return mappings;
139     },
140
141     _isSeparator: function(char)
142     {
143         return char === "," || char === ";";
144     },
145
146     _decodeVLQ: function(stringCharIterator)
147     {
148         // Read unsigned value.
149         var result = 0;
150         var shift = 0;
151         do {
152             var digit = this._base64Map[stringCharIterator.next()];
153             result += (digit & this._VLQ_BASE_MASK) << shift;
154             shift += this._VLQ_BASE_SHIFT;
155         } while (digit & this._VLQ_CONTINUATION_MASK);
156
157         // Fix the sign.
158         var negative = result & 1;
159         result >>= 1;
160         return negative ? -result : result;
161     },
162
163     _VLQ_BASE_SHIFT: 5,
164     _VLQ_BASE_MASK: (1 << 5) - 1,
165     _VLQ_CONTINUATION_MASK: 1 << 5
166 }
167
168 /**
169  * @constructor
170  */
171 WebInspector.ClosureCompilerSourceMapping.StringCharIterator = function(string)
172 {
173     this._string = string;
174     this._position = 0;
175 }
176
177 WebInspector.ClosureCompilerSourceMapping.StringCharIterator.prototype = {
178     next: function()
179     {
180         return this._string.charAt(this._position++);
181     },
182
183     peek: function()
184     {
185         return this._string.charAt(this._position);
186     },
187
188     hasNext: function()
189     {
190         return this._position < this._string.length;
191     }
192 }