blob: f4360188337b053baee4b5aeae36f21ddcb7b992 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29// -------------------------------------------------------------------
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +000030//
31// Matches Script::Type from objects.h
32var TYPE_NATIVE = 0;
33var TYPE_EXTENSION = 1;
34var TYPE_NORMAL = 2;
35
36// Matches Script::CompilationType from objects.h
37var COMPILATION_TYPE_HOST = 0;
38var COMPILATION_TYPE_EVAL = 1;
39var COMPILATION_TYPE_JSON = 2;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000040
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +000041// Matches Messages::kNoLineNumberInfo from v8.h
42var kNoLineNumberInfo = 0;
43
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +000044// If this object gets passed to an error constructor the error will
45// get an accessor for .message that constructs a descriptive error
46// message on access.
47var kAddMessageAccessorsMarker = { };
48
ager@chromium.orgadd848f2009-08-13 12:44:13 +000049var kMessages = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
ricow@chromium.org83aa5492011-02-07 12:42:56 +000051var kReplacementMarkers = [ "%0", "%1", "%2", "%3" ];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000052
kmillikin@chromium.org31b12772011-02-02 16:08:26 +000053function FormatString(format, message) {
54 var args = %MessageGetArguments(message);
55 var result = "";
56 var arg_num = 0;
57 for (var i = 0; i < format.length; i++) {
58 var str = format[i];
59 for (arg_num = 0; arg_num < kReplacementMarkers.length; arg_num++) {
60 if (format[i] !== kReplacementMarkers[arg_num]) continue;
61 try {
62 str = ToDetailString(args[arg_num]);
63 } catch (e) {
64 str = "#<error>";
65 }
ager@chromium.org378b34e2011-01-28 08:04:38 +000066 }
kmillikin@chromium.org31b12772011-02-02 16:08:26 +000067 result += str;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000068 }
69 return result;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000070}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000071
72
ager@chromium.org0ee099b2011-01-25 14:06:47 +000073// To check if something is a native error we need to check the
74// concrete native error types. It is not enough to check "obj
75// instanceof $Error" because user code can replace
76// NativeError.prototype.__proto__. User code cannot replace
77// NativeError.prototype though and therefore this is a safe test.
78function IsNativeErrorObject(obj) {
79 return (obj instanceof $Error) ||
80 (obj instanceof $EvalError) ||
81 (obj instanceof $RangeError) ||
82 (obj instanceof $ReferenceError) ||
83 (obj instanceof $SyntaxError) ||
84 (obj instanceof $TypeError) ||
85 (obj instanceof $URIError);
86}
87
88
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +000089// When formatting internally created error messages, do not
90// invoke overwritten error toString methods but explicitly use
91// the error to string method. This is to avoid leaking error
92// objects between script tags in a browser setting.
93function ToStringCheckErrorObject(obj) {
ager@chromium.org0ee099b2011-01-25 14:06:47 +000094 if (IsNativeErrorObject(obj)) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000095 return %_CallFunction(obj, errorToString);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000096 } else {
97 return ToString(obj);
98 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +000099}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000100
101
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000102function ToDetailString(obj) {
103 if (obj != null && IS_OBJECT(obj) && obj.toString === $Object.prototype.toString) {
104 var constructor = obj.constructor;
105 if (!constructor) return ToStringCheckErrorObject(obj);
106 var constructorName = constructor.name;
ager@chromium.org378b34e2011-01-28 08:04:38 +0000107 if (!constructorName || !IS_STRING(constructorName)) {
108 return ToStringCheckErrorObject(obj);
109 }
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000110 return "#<" + constructorName + ">";
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000111 } else {
112 return ToStringCheckErrorObject(obj);
113 }
114}
115
116
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000117function MakeGenericError(constructor, type, args) {
ager@chromium.org3e875802009-06-29 08:26:34 +0000118 if (IS_UNDEFINED(args)) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000119 args = [];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000120 }
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000121 var e = new constructor(kAddMessageAccessorsMarker);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000122 e.type = type;
123 e.arguments = args;
124 return e;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000125}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000126
127
128/**
129 * Setup the Script function and constructor.
130 */
131%FunctionSetInstanceClassName(Script, 'Script');
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000132%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000133%SetCode(Script, function(x) {
134 // Script objects can only be created by the VM.
135 throw new $Error("Not supported");
136});
137
138
139// Helper functions; called from the runtime system.
140function FormatMessage(message) {
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000141 if (kMessages === 0) {
142 kMessages = {
143 // Error
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000144 cyclic_proto: ["Cyclic __proto__ value"],
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +0000145 code_gen_from_strings: ["Code generation from strings disallowed for this context"],
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000146 // TypeError
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000147 unexpected_token: ["Unexpected token ", "%0"],
148 unexpected_token_number: ["Unexpected number"],
149 unexpected_token_string: ["Unexpected string"],
150 unexpected_token_identifier: ["Unexpected identifier"],
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000151 unexpected_strict_reserved: ["Unexpected strict mode reserved word"],
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000152 unexpected_eos: ["Unexpected end of input"],
153 malformed_regexp: ["Invalid regular expression: /", "%0", "/: ", "%1"],
154 unterminated_regexp: ["Invalid regular expression: missing /"],
155 regexp_flags: ["Cannot supply flags when constructing one RegExp from another"],
156 incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"],
157 invalid_lhs_in_assignment: ["Invalid left-hand side in assignment"],
158 invalid_lhs_in_for_in: ["Invalid left-hand side in for-in"],
159 invalid_lhs_in_postfix_op: ["Invalid left-hand side expression in postfix operation"],
160 invalid_lhs_in_prefix_op: ["Invalid left-hand side expression in prefix operation"],
161 multiple_defaults_in_switch: ["More than one default clause in switch statement"],
162 newline_after_throw: ["Illegal newline after throw"],
163 redeclaration: ["%0", " '", "%1", "' has already been declared"],
164 no_catch_or_finally: ["Missing catch or finally after try"],
165 unknown_label: ["Undefined label '", "%0", "'"],
166 uncaught_exception: ["Uncaught ", "%0"],
167 stack_trace: ["Stack Trace:\n", "%0"],
168 called_non_callable: ["%0", " is not a function"],
169 undefined_method: ["Object ", "%1", " has no method '", "%0", "'"],
170 property_not_function: ["Property '", "%0", "' of object ", "%1", " is not a function"],
171 cannot_convert_to_primitive: ["Cannot convert object to primitive value"],
172 not_constructor: ["%0", " is not a constructor"],
173 not_defined: ["%0", " is not defined"],
174 non_object_property_load: ["Cannot read property '", "%0", "' of ", "%1"],
175 non_object_property_store: ["Cannot set property '", "%0", "' of ", "%1"],
176 non_object_property_call: ["Cannot call method '", "%0", "' of ", "%1"],
177 with_expression: ["%0", " has no properties"],
178 illegal_invocation: ["Illegal invocation"],
179 no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
180 apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
181 apply_wrong_args: ["Function.prototype.apply: Arguments list has wrong type"],
182 invalid_in_operator_use: ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
183 instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"],
184 instanceof_nonobject_proto: ["Function has non-object prototype '", "%0", "' in instanceof check"],
185 null_to_object: ["Cannot convert null to object"],
186 reduce_no_initial: ["Reduce of empty array with no initial value"],
187 getter_must_be_callable: ["Getter must be a function: ", "%0"],
188 setter_must_be_callable: ["Setter must be a function: ", "%0"],
189 value_and_accessor: ["Invalid property. A property cannot both have accessors and be writable or have a value: ", "%0"],
190 proto_object_or_null: ["Object prototype may only be an Object or null"],
191 property_desc_object: ["Property description must be an object: ", "%0"],
192 redefine_disallowed: ["Cannot redefine property: ", "%0"],
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000193 define_disallowed: ["Cannot define property:", "%0", ", object is not extensible."],
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000194 non_extensible_proto: ["%0", " is not extensible"],
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000195 handler_non_object: ["Proxy.", "%0", " called with non-object as handler"],
kmillikin@chromium.orgc53e10d2011-05-18 09:12:58 +0000196 handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000197 proxy_prop_not_configurable: ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"],
198 proxy_non_object_prop_names: ["Trap ", "%1", " returned non-object ", "%0"],
199 proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated property name ", "%2"],
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000200 // RangeError
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000201 invalid_array_length: ["Invalid array length"],
202 stack_overflow: ["Maximum call stack size exceeded"],
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000203 // SyntaxError
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000204 unable_to_parse: ["Parse error"],
205 duplicate_regexp_flag: ["Duplicate RegExp flag ", "%0"],
206 invalid_regexp: ["Invalid RegExp pattern /", "%0", "/"],
207 illegal_break: ["Illegal break statement"],
208 illegal_continue: ["Illegal continue statement"],
209 illegal_return: ["Illegal return statement"],
210 error_loading_debugger: ["Error loading debugger"],
211 no_input_to_regexp: ["No input to ", "%0"],
212 invalid_json: ["String '", "%0", "' is not valid JSON"],
213 circular_structure: ["Converting circular structure to JSON"],
214 obj_ctor_property_non_object: ["Object.", "%0", " called on non-object"],
lrn@chromium.org1c092762011-05-09 09:42:16 +0000215 called_on_null_or_undefined: ["%0", " called on null or undefined"],
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000216 array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
217 object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
218 illegal_access: ["Illegal access"],
219 invalid_preparser_data: ["Invalid preparser data for function ", "%0"],
220 strict_mode_with: ["Strict mode code may not include a with statement"],
221 strict_catch_variable: ["Catch variable may not be eval or arguments in strict mode"],
jkummerow@chromium.orge297f592011-06-08 10:05:15 +0000222 too_many_arguments: ["Too many arguments in function call (only 32766 allowed)"],
vegorov@chromium.org7304bca2011-05-16 12:14:13 +0000223 too_many_parameters: ["Too many parameters in function definition (only 32766 allowed)"],
224 too_many_variables: ["Too many variables declared (only 32767 allowed)"],
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000225 strict_param_name: ["Parameter name eval or arguments is not allowed in strict mode"],
226 strict_param_dupe: ["Strict mode function may not have duplicate parameter names"],
227 strict_var_name: ["Variable name may not be eval or arguments in strict mode"],
228 strict_function_name: ["Function name may not be eval or arguments in strict mode"],
229 strict_octal_literal: ["Octal literals are not allowed in strict mode."],
230 strict_duplicate_property: ["Duplicate data property in object literal not allowed in strict mode"],
231 accessor_data_property: ["Object literal may not have data and accessor property with the same name"],
232 accessor_get_set: ["Object literal may not have multiple get/set accessors with the same name"],
233 strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"],
234 strict_lhs_postfix: ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
235 strict_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000236 strict_reserved_word: ["Use of future reserved word in strict mode"],
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000237 strict_delete: ["Delete of an unqualified identifier in strict mode."],
238 strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"],
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000239 strict_const: ["Use of const in strict mode."],
240 strict_function: ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
241 strict_read_only_property: ["Cannot assign to read only property '", "%0", "' of ", "%1"],
242 strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"],
danno@chromium.org40cb8782011-05-25 07:58:50 +0000243 strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000244 strict_caller: ["Illegal access to a strict mode caller function."],
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000245 };
246 }
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000247 var message_type = %MessageGetType(message);
248 var format = kMessages[message_type];
249 if (!format) return "<unknown message " + message_type + ">";
250 return FormatString(format, message);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000251}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000252
253
254function GetLineNumber(message) {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000255 var start_position = %MessageGetStartPosition(message);
256 if (start_position == -1) return kNoLineNumberInfo;
257 var script = %MessageGetScript(message);
258 var location = script.locationFromPosition(start_position, true);
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000259 if (location == null) return kNoLineNumberInfo;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000260 return location.line + 1;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000261}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262
263
264// Returns the source code line containing the given source
265// position, or the empty string if the position is invalid.
266function GetSourceLine(message) {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000267 var script = %MessageGetScript(message);
268 var start_position = %MessageGetStartPosition(message);
269 var location = script.locationFromPosition(start_position, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270 if (location == null) return "";
271 location.restrict();
272 return location.sourceText();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000273}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000274
275
276function MakeTypeError(type, args) {
277 return MakeGenericError($TypeError, type, args);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000278}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279
280
281function MakeRangeError(type, args) {
282 return MakeGenericError($RangeError, type, args);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000283}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284
285
286function MakeSyntaxError(type, args) {
287 return MakeGenericError($SyntaxError, type, args);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000288}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000289
290
291function MakeReferenceError(type, args) {
292 return MakeGenericError($ReferenceError, type, args);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000293}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000294
295
296function MakeEvalError(type, args) {
297 return MakeGenericError($EvalError, type, args);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000298}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000299
300
301function MakeError(type, args) {
302 return MakeGenericError($Error, type, args);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000303}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000304
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000305/**
306 * Find a line number given a specific source position.
307 * @param {number} position The source position.
308 * @return {number} 0 if input too small, -1 if input too large,
309 else the line number.
310 */
311Script.prototype.lineFromPosition = function(position) {
312 var lower = 0;
313 var upper = this.lineCount() - 1;
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000314 var line_ends = this.line_ends;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000315
316 // We'll never find invalid positions so bail right away.
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000317 if (position > line_ends[upper]) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000318 return -1;
319 }
320
321 // This means we don't have to safe-guard indexing line_ends[i - 1].
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000322 if (position <= line_ends[0]) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000323 return 0;
324 }
325
326 // Binary search to find line # from position range.
327 while (upper >= 1) {
328 var i = (lower + upper) >> 1;
329
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000330 if (position > line_ends[i]) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000331 lower = i + 1;
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000332 } else if (position <= line_ends[i - 1]) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000333 upper = i - 1;
334 } else {
335 return i;
336 }
337 }
erik.corry@gmail.comd91075f2011-02-10 07:45:38 +0000338
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000339 return -1;
340}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000341
342/**
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000343 * Get information on a specific source position.
344 * @param {number} position The source position
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000345 * @param {boolean} include_resource_offset Set to true to have the resource
346 * offset added to the location
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000347 * @return {SourceLocation}
348 * If line is negative or not in the source null is returned.
349 */
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000350Script.prototype.locationFromPosition = function (position,
351 include_resource_offset) {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000352 var line = this.lineFromPosition(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000353 if (line == -1) return null;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000354
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000355 // Determine start, end and column.
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000356 var line_ends = this.line_ends;
357 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
358 var end = line_ends[line];
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000359 if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') end--;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000360 var column = position - start;
361
362 // Adjust according to the offset within the resource.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000363 if (include_resource_offset) {
364 line += this.line_offset;
365 if (line == this.line_offset) {
366 column += this.column_offset;
367 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000368 }
369
370 return new SourceLocation(this, position, line, column, start, end);
371};
372
373
374/**
375 * Get information on a specific source line and column possibly offset by a
376 * fixed source position. This function is used to find a source position from
377 * a line and column position. The fixed source position offset is typically
378 * used to find a source position in a function based on a line and column in
379 * the source for the function alone. The offset passed will then be the
380 * start position of the source for the function within the full script source.
381 * @param {number} opt_line The line within the source. Default value is 0
382 * @param {number} opt_column The column in within the line. Default value is 0
383 * @param {number} opt_offset_position The offset from the begining of the
384 * source from where the line and column calculation starts. Default value is 0
385 * @return {SourceLocation}
386 * If line is negative or not in the source null is returned.
387 */
388Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000389 // Default is the first line in the script. Lines in the script is relative
390 // to the offset within the resource.
391 var line = 0;
392 if (!IS_UNDEFINED(opt_line)) {
393 line = opt_line - this.line_offset;
394 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000395
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396 // Default is first column. If on the first line add the offset within the
397 // resource.
398 var column = opt_column || 0;
399 if (line == 0) {
400 column -= this.column_offset
401 }
402
403 var offset_position = opt_offset_position || 0;
404 if (line < 0 || column < 0 || offset_position < 0) return null;
405 if (line == 0) {
ager@chromium.org3a6061e2009-03-12 14:24:36 +0000406 return this.locationFromPosition(offset_position + column, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407 } else {
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000408 // Find the line where the offset position is located.
409 var offset_line = this.lineFromPosition(offset_position);
410
411 if (offset_line == -1 || offset_line + line >= this.lineCount()) {
412 return null;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000413 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000414
iposva@chromium.org245aa852009-02-10 00:49:54 +0000415 return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000416 }
417}
418
419
420/**
421 * Get a slice of source code from the script. The boundaries for the slice is
422 * specified in lines.
423 * @param {number} opt_from_line The first line (zero bound) in the slice.
424 * Default is 0
425 * @param {number} opt_to_column The last line (zero bound) in the slice (non
426 * inclusive). Default is the number of lines in the script
427 * @return {SourceSlice} The source slice or null of the parameters where
428 * invalid
429 */
430Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000431 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line;
432 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line
433
434 // Adjust according to the offset within the resource.
435 from_line -= this.line_offset;
436 to_line -= this.line_offset;
437 if (from_line < 0) from_line = 0;
438 if (to_line > this.lineCount()) to_line = this.lineCount();
439
iposva@chromium.org245aa852009-02-10 00:49:54 +0000440 // Check parameters.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000441 if (from_line >= this.lineCount() ||
442 to_line < 0 ||
443 from_line > to_line) {
444 return null;
445 }
446
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000447 var line_ends = this.line_ends;
448 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
449 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000450
451 // Return a source slice with line numbers re-adjusted to the resource.
452 return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset,
453 from_position, to_position);
454}
455
456
457Script.prototype.sourceLine = function (opt_line) {
458 // Default is the first line in the script. Lines in the script are relative
459 // to the offset within the resource.
460 var line = 0;
461 if (!IS_UNDEFINED(opt_line)) {
462 line = opt_line - this.line_offset;
463 }
iposva@chromium.org245aa852009-02-10 00:49:54 +0000464
465 // Check parameter.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000466 if (line < 0 || this.lineCount() <= line) {
467 return null;
468 }
469
iposva@chromium.org245aa852009-02-10 00:49:54 +0000470 // Return the source line.
sgjesse@chromium.org499aaa52009-11-30 08:07:20 +0000471 var line_ends = this.line_ends;
472 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
473 var end = line_ends[line];
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000474 return %_CallFunction(this.source, start, end, StringSubstring);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000475}
476
477
478/**
479 * Returns the number of source lines.
480 * @return {number}
481 * Number of source lines.
482 */
483Script.prototype.lineCount = function() {
iposva@chromium.org245aa852009-02-10 00:49:54 +0000484 // Return number of source lines.
485 return this.line_ends.length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000486};
487
488
489/**
lrn@chromium.org25156de2010-04-06 13:10:27 +0000490 * Returns the name of script if available, contents of sourceURL comment
vegorov@chromium.org42841962010-10-18 11:18:59 +0000491 * otherwise. See
lrn@chromium.org25156de2010-04-06 13:10:27 +0000492 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
493 * for details on using //@ sourceURL comment to identify scritps that don't
494 * have name.
vegorov@chromium.org42841962010-10-18 11:18:59 +0000495 *
lrn@chromium.org25156de2010-04-06 13:10:27 +0000496 * @return {?string} script name if present, value for //@ sourceURL comment
497 * otherwise.
498 */
499Script.prototype.nameOrSourceURL = function() {
500 if (this.name)
501 return this.name;
vegorov@chromium.org42841962010-10-18 11:18:59 +0000502 // TODO(608): the spaces in a regexp below had to be escaped as \040
lrn@chromium.org25156de2010-04-06 13:10:27 +0000503 // because this file is being processed by js2c whose handling of spaces
504 // in regexps is broken. Also, ['"] are excluded from allowed URLs to
505 // avoid matches against sources that invoke evals with sourceURL.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000506 // A better solution would be to detect these special comments in
507 // the scanner/parser.
508 var source = ToString(this.source);
509 var sourceUrlPos = %StringIndexOf(source, "sourceURL=", 0);
510 if (sourceUrlPos > 4) {
511 var sourceUrlPattern =
512 /\/\/@[\040\t]sourceURL=[\040\t]*([^\s\'\"]*)[\040\t]*$/gm;
513 // Don't reuse lastMatchInfo here, so we create a new array with room
514 // for four captures (array with length one longer than the index
515 // of the fourth capture, where the numbering is zero-based).
516 var matchInfo = new InternalArray(CAPTURE(3) + 1);
517 var match =
518 %_RegExpExec(sourceUrlPattern, source, sourceUrlPos - 4, matchInfo);
519 if (match) {
520 return SubString(source, matchInfo[CAPTURE(2)], matchInfo[CAPTURE(3)]);
521 }
522 }
523 return this.name;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000524}
525
526
527/**
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000528 * Class for source location. A source location is a position within some
529 * source with the following properties:
530 * script : script object for the source
531 * line : source line number
532 * column : source column within the line
533 * position : position within the source
534 * start : position of start of source context (inclusive)
535 * end : position of end of source context (not inclusive)
536 * Source text for the source context is the character interval [start, end[. In
537 * most cases end will point to a newline character. It might point just past
538 * the final position of the source if the last source line does not end with a
iposva@chromium.org245aa852009-02-10 00:49:54 +0000539 * newline character.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000540 * @param {Script} script The Script object for which this is a location
541 * @param {number} position Source position for the location
542 * @param {number} line The line number for the location
543 * @param {number} column The column within the line for the location
544 * @param {number} start Source position for start of source context
545 * @param {number} end Source position for end of source context
546 * @constructor
547 */
548function SourceLocation(script, position, line, column, start, end) {
549 this.script = script;
550 this.position = position;
551 this.line = line;
552 this.column = column;
553 this.start = start;
554 this.end = end;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000555}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000556
557
558const kLineLengthLimit = 78;
559
560/**
561 * Restrict source location start and end positions to make the source slice
562 * no more that a certain number of characters wide.
563 * @param {number} opt_limit The with limit of the source text with a default
564 * of 78
565 * @param {number} opt_before The number of characters to prefer before the
566 * position with a default value of 10 less that the limit
567 */
568SourceLocation.prototype.restrict = function (opt_limit, opt_before) {
569 // Find the actual limit to use.
570 var limit;
571 var before;
572 if (!IS_UNDEFINED(opt_limit)) {
573 limit = opt_limit;
574 } else {
575 limit = kLineLengthLimit;
576 }
577 if (!IS_UNDEFINED(opt_before)) {
578 before = opt_before;
579 } else {
580 // If no before is specified center for small limits and perfer more source
581 // before the the position that after for longer limits.
582 if (limit <= 20) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000583 before = $floor(limit / 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584 } else {
585 before = limit - 10;
586 }
587 }
588 if (before >= limit) {
589 before = limit - 1;
590 }
591
592 // If the [start, end[ interval is too big we restrict
593 // it in one or both ends. We make sure to always produce
594 // restricted intervals of maximum allowed size.
595 if (this.end - this.start > limit) {
596 var start_limit = this.position - before;
597 var end_limit = this.position + limit - before;
598 if (this.start < start_limit && end_limit < this.end) {
599 this.start = start_limit;
600 this.end = end_limit;
601 } else if (this.start < start_limit) {
602 this.start = this.end - limit;
603 } else {
604 this.end = this.start + limit;
605 }
606 }
607};
608
609
610/**
611 * Get the source text for a SourceLocation
612 * @return {String}
613 * Source text for this location.
614 */
615SourceLocation.prototype.sourceText = function () {
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000616 return %_CallFunction(this.script.source, this.start, this.end, StringSubstring);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000617};
618
619
620/**
621 * Class for a source slice. A source slice is a part of a script source with
622 * the following properties:
623 * script : script object for the source
624 * from_line : line number for the first line in the slice
625 * to_line : source line number for the last line in the slice
626 * from_position : position of the first character in the slice
627 * to_position : position of the last character in the slice
628 * The to_line and to_position are not included in the slice, that is the lines
629 * in the slice are [from_line, to_line[. Likewise the characters in the slice
630 * are [from_position, to_position[.
631 * @param {Script} script The Script object for the source slice
632 * @param {number} from_line
633 * @param {number} to_line
634 * @param {number} from_position
635 * @param {number} to_position
636 * @constructor
637 */
638function SourceSlice(script, from_line, to_line, from_position, to_position) {
639 this.script = script;
640 this.from_line = from_line;
641 this.to_line = to_line;
642 this.from_position = from_position;
643 this.to_position = to_position;
644}
645
646
647/**
648 * Get the source text for a SourceSlice
649 * @return {String} Source text for this slice. The last line will include
650 * the line terminating characters (if any)
651 */
652SourceSlice.prototype.sourceText = function () {
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000653 return %_CallFunction(this.script.source,
654 this.from_position,
655 this.to_position,
656 StringSubstring);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000657};
658
659
660// Returns the offset of the given position within the containing
661// line.
662function GetPositionInLine(message) {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000663 var script = %MessageGetScript(message);
664 var start_position = %MessageGetStartPosition(message);
665 var location = script.locationFromPosition(start_position, false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000666 if (location == null) return -1;
667 location.restrict();
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000668 return start_position - location.start;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000669}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000670
671
672function GetStackTraceLine(recv, fun, pos, isGlobal) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000673 return FormatSourcePosition(new CallSite(recv, fun, pos));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000674}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000675
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000676// ----------------------------------------------------------------------------
677// Error implementation
678
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000679// Defines accessors for a property that is calculated the first time
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000680// the property is read.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000681function DefineOneShotAccessor(obj, name, fun) {
682 // Note that the accessors consistently operate on 'obj', not 'this'.
683 // Since the object may occur in someone else's prototype chain we
684 // can't rely on 'this' being the same as 'obj'.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000685 var hasBeenSet = false;
686 var value;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000687 obj.__defineGetter__(name, function () {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000688 if (hasBeenSet) {
689 return value;
690 }
691 hasBeenSet = true;
692 value = fun(obj);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000693 return value;
694 });
695 obj.__defineSetter__(name, function (v) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000696 hasBeenSet = true;
697 value = v;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000698 });
699}
700
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000701function CallSite(receiver, fun, pos) {
702 this.receiver = receiver;
703 this.fun = fun;
704 this.pos = pos;
705}
706
707CallSite.prototype.getThis = function () {
708 return this.receiver;
709};
710
711CallSite.prototype.getTypeName = function () {
712 var constructor = this.receiver.constructor;
713 if (!constructor)
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000714 return %_CallFunction(this.receiver, ObjectToString);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000715 var constructorName = constructor.name;
716 if (!constructorName)
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000717 return %_CallFunction(this.receiver, ObjectToString);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000718 return constructorName;
719};
720
721CallSite.prototype.isToplevel = function () {
722 if (this.receiver == null)
723 return true;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000724 return IS_GLOBAL(this.receiver);
725};
726
727CallSite.prototype.isEval = function () {
728 var script = %FunctionGetScript(this.fun);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000729 return script && script.compilation_type == COMPILATION_TYPE_EVAL;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000730};
731
732CallSite.prototype.getEvalOrigin = function () {
733 var script = %FunctionGetScript(this.fun);
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000734 return FormatEvalOrigin(script);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000735};
736
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000737CallSite.prototype.getScriptNameOrSourceURL = function () {
738 var script = %FunctionGetScript(this.fun);
739 return script ? script.nameOrSourceURL() : null;
740};
741
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000742CallSite.prototype.getFunction = function () {
743 return this.fun;
744};
745
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000746CallSite.prototype.getFunctionName = function () {
747 // See if the function knows its own name
748 var name = this.fun.name;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000749 if (name) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000750 return name;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000751 } else {
752 return %FunctionGetInferredName(this.fun);
753 }
754 // Maybe this is an evaluation?
755 var script = %FunctionGetScript(this.fun);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000756 if (script && script.compilation_type == COMPILATION_TYPE_EVAL)
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000757 return "eval";
758 return null;
759};
760
761CallSite.prototype.getMethodName = function () {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000762 // See if we can find a unique property on the receiver that holds
763 // this function.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000764 var ownName = this.fun.name;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000765 if (ownName && this.receiver &&
whesse@chromium.org7a392b32011-01-31 11:30:36 +0000766 (%_CallFunction(this.receiver, ownName, ObjectLookupGetter) === this.fun ||
767 %_CallFunction(this.receiver, ownName, ObjectLookupSetter) === this.fun ||
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000768 this.receiver[ownName] === this.fun)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000769 // To handle DontEnum properties we guess that the method has
770 // the same name as the function.
771 return ownName;
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000772 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000773 var name = null;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000774 for (var prop in this.receiver) {
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000775 if (this.receiver.__lookupGetter__(prop) === this.fun ||
776 this.receiver.__lookupSetter__(prop) === this.fun ||
777 (!this.receiver.__lookupGetter__(prop) && this.receiver[prop] === this.fun)) {
778 // If we find more than one match bail out to avoid confusion.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000779 if (name)
780 return null;
781 name = prop;
782 }
783 }
784 if (name)
785 return name;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000786 return null;
787};
788
789CallSite.prototype.getFileName = function () {
790 var script = %FunctionGetScript(this.fun);
791 return script ? script.name : null;
792};
793
794CallSite.prototype.getLineNumber = function () {
795 if (this.pos == -1)
796 return null;
797 var script = %FunctionGetScript(this.fun);
798 var location = null;
799 if (script) {
800 location = script.locationFromPosition(this.pos, true);
801 }
802 return location ? location.line + 1 : null;
803};
804
805CallSite.prototype.getColumnNumber = function () {
806 if (this.pos == -1)
807 return null;
808 var script = %FunctionGetScript(this.fun);
809 var location = null;
810 if (script) {
811 location = script.locationFromPosition(this.pos, true);
812 }
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000813 return location ? location.column + 1: null;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000814};
815
816CallSite.prototype.isNative = function () {
817 var script = %FunctionGetScript(this.fun);
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000818 return script ? (script.type == TYPE_NATIVE) : false;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000819};
820
821CallSite.prototype.getPosition = function () {
822 return this.pos;
823};
824
825CallSite.prototype.isConstructor = function () {
826 var constructor = this.receiver ? this.receiver.constructor : null;
827 if (!constructor)
828 return false;
829 return this.fun === constructor;
830};
831
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000832function FormatEvalOrigin(script) {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000833 var sourceURL = script.nameOrSourceURL();
834 if (sourceURL)
835 return sourceURL;
836
837 var eval_origin = "eval at ";
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000838 if (script.eval_from_function_name) {
839 eval_origin += script.eval_from_function_name;
840 } else {
841 eval_origin += "<anonymous>";
842 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000843
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000844 var eval_from_script = script.eval_from_script;
845 if (eval_from_script) {
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000846 if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000847 // eval script originated from another eval.
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000848 eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000849 } else {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000850 // eval script originated from "real" source.
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000851 if (eval_from_script.name) {
852 eval_origin += " (" + eval_from_script.name;
853 var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true);
854 if (location) {
855 eval_origin += ":" + (location.line + 1);
856 eval_origin += ":" + (location.column + 1);
857 }
858 eval_origin += ")"
859 } else {
860 eval_origin += " (unknown source)";
861 }
862 }
863 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000864
sgjesse@chromium.org98180592009-12-02 08:17:28 +0000865 return eval_origin;
866};
867
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000868function FormatSourcePosition(frame) {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000869 var fileName;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000870 var fileLocation = "";
871 if (frame.isNative()) {
872 fileLocation = "native";
873 } else if (frame.isEval()) {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000874 fileName = frame.getScriptNameOrSourceURL();
875 if (!fileName)
876 fileLocation = frame.getEvalOrigin();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000877 } else {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000878 fileName = frame.getFileName();
879 }
880
881 if (fileName) {
882 fileLocation += fileName;
883 var lineNumber = frame.getLineNumber();
884 if (lineNumber != null) {
885 fileLocation += ":" + lineNumber;
886 var columnNumber = frame.getColumnNumber();
887 if (columnNumber) {
888 fileLocation += ":" + columnNumber;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000889 }
890 }
891 }
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000892
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000893 if (!fileLocation) {
894 fileLocation = "unknown source";
895 }
896 var line = "";
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000897 var functionName = frame.getFunction().name;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000898 var addPrefix = true;
899 var isConstructor = frame.isConstructor();
900 var isMethodCall = !(frame.isToplevel() || isConstructor);
901 if (isMethodCall) {
kmillikin@chromium.org3cdd9e12010-09-06 11:39:48 +0000902 var methodName = frame.getMethodName();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000903 line += frame.getTypeName() + ".";
904 if (functionName) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000905 line += functionName;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000906 if (methodName && (methodName != functionName)) {
907 line += " [as " + methodName + "]";
908 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000909 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000910 line += methodName || "<anonymous>";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000911 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000912 } else if (isConstructor) {
913 line += "new " + (functionName || "<anonymous>");
914 } else if (functionName) {
915 line += functionName;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000916 } else {
917 line += fileLocation;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000918 addPrefix = false;
919 }
920 if (addPrefix) {
921 line += " (" + fileLocation + ")";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000922 }
923 return line;
924}
925
926function FormatStackTrace(error, frames) {
927 var lines = [];
928 try {
929 lines.push(error.toString());
930 } catch (e) {
931 try {
932 lines.push("<error: " + e + ">");
933 } catch (ee) {
934 lines.push("<error>");
935 }
936 }
937 for (var i = 0; i < frames.length; i++) {
938 var frame = frames[i];
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000939 var line;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000940 try {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000941 line = FormatSourcePosition(frame);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000942 } catch (e) {
943 try {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000944 line = "<error: " + e + ">";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000945 } catch (ee) {
946 // Any code that reaches this point is seriously nasty!
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000947 line = "<error>";
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000948 }
949 }
950 lines.push(" at " + line);
951 }
952 return lines.join("\n");
953}
954
955function FormatRawStackTrace(error, raw_stack) {
956 var frames = [ ];
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000957 for (var i = 0; i < raw_stack.length; i += 4) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000958 var recv = raw_stack[i];
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000959 var fun = raw_stack[i + 1];
960 var code = raw_stack[i + 2];
961 var pc = raw_stack[i + 3];
962 var pos = %FunctionGetPositionForOffset(code, pc);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000963 frames.push(new CallSite(recv, fun, pos));
964 }
965 if (IS_FUNCTION($Error.prepareStackTrace)) {
966 return $Error.prepareStackTrace(error, frames);
967 } else {
968 return FormatStackTrace(error, frames);
969 }
970}
971
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000972function DefineError(f) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000973 // Store the error function in both the global object
974 // and the runtime object. The function is fetched
975 // from the runtime object when throwing errors from
976 // within the runtime system to avoid strange side
977 // effects when overwriting the error functions from
978 // user code.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000979 var name = f.name;
980 %SetProperty(global, name, f, DONT_ENUM);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000981 this['$' + name] = f;
982 // Configure the error function.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000983 if (name == 'Error') {
984 // The prototype of the Error object must itself be an error.
985 // However, it can't be an instance of the Error object because
986 // it hasn't been properly configured yet. Instead we create a
987 // special not-a-true-error-but-close-enough object.
988 function ErrorPrototype() {}
989 %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
990 %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
991 %FunctionSetPrototype(f, new ErrorPrototype());
992 } else {
993 %FunctionSetPrototype(f, new $Error());
994 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000995 %FunctionSetInstanceClassName(f, 'Error');
ager@chromium.org7c537e22008-10-16 08:43:32 +0000996 %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000997 // The name property on the prototype of error objects is not
998 // specified as being read-one and dont-delete. However, allowing
999 // overwriting allows leaks of error objects between script blocks
1000 // in the same context in a browser setting. Therefore we fix the
1001 // name.
1002 %SetProperty(f.prototype, "name", name, READ_ONLY | DONT_DELETE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001003 %SetCode(f, function(m) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001004 if (%_IsConstructCall()) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00001005 // Define all the expected properties directly on the error
1006 // object. This avoids going through getters and setters defined
1007 // on prototype objects.
1008 %IgnoreAttributesAndSetProperty(this, 'stack', void 0);
1009 %IgnoreAttributesAndSetProperty(this, 'arguments', void 0);
1010 %IgnoreAttributesAndSetProperty(this, 'type', void 0);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001011 if (m === kAddMessageAccessorsMarker) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00001012 // DefineOneShotAccessor always inserts a message property and
1013 // ignores setters.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001014 DefineOneShotAccessor(this, 'message', function (obj) {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001015 return FormatMessage(%NewMessageObject(obj.type, obj.arguments));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001016 });
1017 } else if (!IS_UNDEFINED(m)) {
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00001018 %IgnoreAttributesAndSetProperty(this, 'message', ToString(m));
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001019 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001020 captureStackTrace(this, f);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001021 } else {
1022 return new f(m);
1023 }
1024 });
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001025}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001026
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001027function captureStackTrace(obj, cons_opt) {
1028 var stackTraceLimit = $Error.stackTraceLimit;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001029 if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001030 if (stackTraceLimit < 0 || stackTraceLimit > 10000)
1031 stackTraceLimit = 10000;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001032 var raw_stack = %CollectStackTrace(cons_opt
1033 ? cons_opt
1034 : captureStackTrace, stackTraceLimit);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001035 DefineOneShotAccessor(obj, 'stack', function (obj) {
1036 return FormatRawStackTrace(obj, raw_stack);
1037 });
1038};
1039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040$Math.__proto__ = global.Object.prototype;
1041
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001042DefineError(function Error() { });
1043DefineError(function TypeError() { });
1044DefineError(function RangeError() { });
1045DefineError(function SyntaxError() { });
1046DefineError(function ReferenceError() { });
1047DefineError(function EvalError() { });
1048DefineError(function URIError() { });
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001049
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001050$Error.captureStackTrace = captureStackTrace;
1051
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001052// Setup extra properties of the Error.prototype object.
1053$Error.prototype.message = '';
1054
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001055// Global list of error objects visited during errorToString. This is
1056// used to detect cycles in error toString formatting.
1057var visited_errors = new $Array();
1058var cyclic_error_marker = new $Object();
1059
1060function errorToStringDetectCycle() {
1061 if (!%PushIfAbsent(visited_errors, this)) throw cyclic_error_marker;
1062 try {
1063 var type = this.type;
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001064 if (type && !%_CallFunction(this, "message", ObjectHasOwnProperty)) {
1065 var formatted = FormatMessage(%NewMessageObject(type, this.arguments));
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001066 return this.name + ": " + formatted;
1067 }
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001068 var message = %_CallFunction(this, "message", ObjectHasOwnProperty)
1069 ? (": " + this.message)
1070 : "";
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001071 return this.name + message;
1072 } finally {
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001073 visited_errors.length = visited_errors.length - 1;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001074 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001075}
1076
1077function errorToString() {
lrn@chromium.org1c092762011-05-09 09:42:16 +00001078 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
1079 throw MakeTypeError("called_on_null_or_undefined",
1080 ["Error.prototype.toString"]);
1081 }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +00001082 // This helper function is needed because access to properties on
1083 // the builtins object do not work inside of a catch clause.
1084 function isCyclicErrorMarker(o) { return o === cyclic_error_marker; }
1085
1086 try {
1087 return %_CallFunction(this, errorToStringDetectCycle);
1088 } catch(e) {
1089 // If this error message was encountered already return the empty
1090 // string for it instead of recursively formatting it.
1091 if (isCyclicErrorMarker(e)) return '';
1092 else throw e;
1093 }
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +00001094}
1095
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001096
1097InstallFunctions($Error.prototype, DONT_ENUM, ['toString', errorToString]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001098
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001099// Boilerplate for exceptions for stack overflows. Used from
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001100// Isolate::StackOverflow().
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001101const kStackOverflowBoilerplate = MakeRangeError('stack_overflow', []);