blob: a30ef8a914b1ff5ae006f82531bfca416059adae [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// 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// -------------------------------------------------------------------
Andrei Popescu31002712010-02-23 13:46:05 +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;
Steve Blocka7e24c12009-10-30 11:49:00 +000040
41// Lazily initialized.
42var kVowelSounds = 0;
43var kCapitalVowelSounds = 0;
44
Kristian Monsen25f61362010-05-21 11:50:48 +010045// Matches Messages::kNoLineNumberInfo from v8.h
46var kNoLineNumberInfo = 0;
47
Steve Blocka7e24c12009-10-30 11:49:00 +000048// If this object gets passed to an error constructor the error will
49// get an accessor for .message that constructs a descriptive error
50// message on access.
51var kAddMessageAccessorsMarker = { };
52
53
54function GetInstanceName(cons) {
55 if (cons.length == 0) {
56 return "";
57 }
58 var first = %StringToLowerCase(StringCharAt.call(cons, 0));
59 if (kVowelSounds === 0) {
60 kVowelSounds = {a: true, e: true, i: true, o: true, u: true, y: true};
61 kCapitalVowelSounds = {a: true, e: true, i: true, o: true, u: true, h: true,
62 f: true, l: true, m: true, n: true, r: true, s: true, x: true, y: true};
63 }
64 var vowel_mapping = kVowelSounds;
65 if (cons.length > 1 && (StringCharAt.call(cons, 0) != first)) {
66 // First char is upper case
67 var second = %StringToLowerCase(StringCharAt.call(cons, 1));
68 // Second char is upper case
69 if (StringCharAt.call(cons, 1) != second) {
70 vowel_mapping = kCapitalVowelSounds;
71 }
72 }
73 var s = vowel_mapping[first] ? "an " : "a ";
74 return s + cons;
75}
76
77
78var kMessages = 0;
79
80
81function FormatString(format, args) {
82 var result = format;
83 for (var i = 0; i < args.length; i++) {
84 var str;
85 try { str = ToDetailString(args[i]); }
86 catch (e) { str = "#<error>"; }
87 result = ArrayJoin.call(StringSplit.call(result, "%" + i), str);
88 }
89 return result;
90}
91
92
93function ToDetailString(obj) {
94 if (obj != null && IS_OBJECT(obj) && obj.toString === $Object.prototype.toString) {
95 var constructor = obj.constructor;
96 if (!constructor) return ToString(obj);
97 var constructorName = constructor.name;
98 if (!constructorName) return ToString(obj);
99 return "#<" + GetInstanceName(constructorName) + ">";
Ben Murdochb8e0da22011-05-16 14:20:40 +0100100 } else if (obj instanceof $Error) {
101 // When formatting internally created error messages, do not
102 // invoke overwritten error toString methods but explicitly use
103 // the error to string method. This is to avoid leaking error
104 // objects between script tags in a browser setting.
105 return %_CallFunction(obj, errorToString);
Steve Blocka7e24c12009-10-30 11:49:00 +0000106 } else {
107 return ToString(obj);
108 }
109}
110
111
112function MakeGenericError(constructor, type, args) {
113 if (IS_UNDEFINED(args)) {
114 args = [];
115 }
116 var e = new constructor(kAddMessageAccessorsMarker);
117 e.type = type;
118 e.arguments = args;
119 return e;
120}
121
122
123/**
124 * Setup the Script function and constructor.
125 */
126%FunctionSetInstanceClassName(Script, 'Script');
127%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM);
128%SetCode(Script, function(x) {
129 // Script objects can only be created by the VM.
130 throw new $Error("Not supported");
131});
132
133
134// Helper functions; called from the runtime system.
135function FormatMessage(message) {
136 if (kMessages === 0) {
137 kMessages = {
138 // Error
139 cyclic_proto: "Cyclic __proto__ value",
140 // TypeError
141 unexpected_token: "Unexpected token %0",
142 unexpected_token_number: "Unexpected number",
143 unexpected_token_string: "Unexpected string",
144 unexpected_token_identifier: "Unexpected identifier",
145 unexpected_eos: "Unexpected end of input",
146 malformed_regexp: "Invalid regular expression: /%0/: %1",
147 unterminated_regexp: "Invalid regular expression: missing /",
148 regexp_flags: "Cannot supply flags when constructing one RegExp from another",
Steve Block6ded16b2010-05-10 14:33:55 +0100149 incompatible_method_receiver: "Method %0 called on incompatible receiver %1",
Steve Blocka7e24c12009-10-30 11:49:00 +0000150 invalid_lhs_in_assignment: "Invalid left-hand side in assignment",
151 invalid_lhs_in_for_in: "Invalid left-hand side in for-in",
152 invalid_lhs_in_postfix_op: "Invalid left-hand side expression in postfix operation",
153 invalid_lhs_in_prefix_op: "Invalid left-hand side expression in prefix operation",
154 multiple_defaults_in_switch: "More than one default clause in switch statement",
155 newline_after_throw: "Illegal newline after throw",
156 redeclaration: "%0 '%1' has already been declared",
157 no_catch_or_finally: "Missing catch or finally after try",
158 unknown_label: "Undefined label '%0'",
159 uncaught_exception: "Uncaught %0",
160 stack_trace: "Stack Trace:\n%0",
161 called_non_callable: "%0 is not a function",
162 undefined_method: "Object %1 has no method '%0'",
163 property_not_function: "Property '%0' of object %1 is not a function",
164 cannot_convert_to_primitive: "Cannot convert object to primitive value",
165 not_constructor: "%0 is not a constructor",
166 not_defined: "%0 is not defined",
167 non_object_property_load: "Cannot read property '%0' of %1",
168 non_object_property_store: "Cannot set property '%0' of %1",
169 non_object_property_call: "Cannot call method '%0' of %1",
170 with_expression: "%0 has no properties",
171 illegal_invocation: "Illegal invocation",
172 no_setter_in_callback: "Cannot set property %0 of %1 which has only a getter",
173 apply_non_function: "Function.prototype.apply was called on %0, which is a %1 and not a function",
174 apply_wrong_args: "Function.prototype.apply: Arguments list has wrong type",
175 invalid_in_operator_use: "Cannot use 'in' operator to search for '%0' in %1",
176 instanceof_function_expected: "Expecting a function in instanceof check, but got %0",
177 instanceof_nonobject_proto: "Function has non-object prototype '%0' in instanceof check",
178 null_to_object: "Cannot convert null to object",
179 reduce_no_initial: "Reduce of empty array with no initial value",
Leon Clarkee46be812010-01-19 14:06:41 +0000180 getter_must_be_callable: "Getter must be a function: %0",
181 setter_must_be_callable: "Setter must be a function: %0",
182 value_and_accessor: "Invalid property. A property cannot both have accessors and be writable or have a value: %0",
183 proto_object_or_null: "Object prototype may only be an Object or null",
184 property_desc_object: "Property description must be an object: %0",
Andrei Popescu31002712010-02-23 13:46:05 +0000185 redefine_disallowed: "Cannot redefine property: %0",
186 define_disallowed: "Cannot define property, object is not extensible: %0",
Steve Blocka7e24c12009-10-30 11:49:00 +0000187 // RangeError
188 invalid_array_length: "Invalid array length",
189 stack_overflow: "Maximum call stack size exceeded",
Steve Blocka7e24c12009-10-30 11:49:00 +0000190 // SyntaxError
191 unable_to_parse: "Parse error",
192 duplicate_regexp_flag: "Duplicate RegExp flag %0",
193 invalid_regexp: "Invalid RegExp pattern /%0/",
194 illegal_break: "Illegal break statement",
195 illegal_continue: "Illegal continue statement",
196 illegal_return: "Illegal return statement",
197 error_loading_debugger: "Error loading debugger",
198 no_input_to_regexp: "No input to %0",
Steve Blocka7e24c12009-10-30 11:49:00 +0000199 invalid_json: "String '%0' is not valid JSON",
200 circular_structure: "Converting circular structure to JSON",
Leon Clarkee46be812010-01-19 14:06:41 +0000201 obj_ctor_property_non_object: "Object.%0 called on non-object",
Steve Block6ded16b2010-05-10 14:33:55 +0100202 array_indexof_not_defined: "Array.getIndexOf: Argument undefined",
Steve Block8defd9f2010-07-08 12:39:36 +0100203 object_not_extensible: "Can't add property %0, object is not extensible",
Leon Clarkeac952652010-07-15 11:15:24 +0100204 illegal_access: "Illegal access",
205 invalid_preparser_data: "Invalid preparser data for function %0"
Steve Blocka7e24c12009-10-30 11:49:00 +0000206 };
207 }
208 var format = kMessages[message.type];
209 if (!format) return "<unknown message " + message.type + ">";
210 return FormatString(format, message.args);
211}
212
213
214function GetLineNumber(message) {
Kristian Monsen25f61362010-05-21 11:50:48 +0100215 if (message.startPos == -1) return kNoLineNumberInfo;
Steve Blocka7e24c12009-10-30 11:49:00 +0000216 var location = message.script.locationFromPosition(message.startPos, true);
Kristian Monsen25f61362010-05-21 11:50:48 +0100217 if (location == null) return kNoLineNumberInfo;
Steve Blocka7e24c12009-10-30 11:49:00 +0000218 return location.line + 1;
219}
220
221
222// Returns the source code line containing the given source
223// position, or the empty string if the position is invalid.
224function GetSourceLine(message) {
225 var location = message.script.locationFromPosition(message.startPos, true);
226 if (location == null) return "";
227 location.restrict();
228 return location.sourceText();
229}
230
231
232function MakeTypeError(type, args) {
233 return MakeGenericError($TypeError, type, args);
234}
235
236
237function MakeRangeError(type, args) {
238 return MakeGenericError($RangeError, type, args);
239}
240
241
242function MakeSyntaxError(type, args) {
243 return MakeGenericError($SyntaxError, type, args);
244}
245
246
247function MakeReferenceError(type, args) {
248 return MakeGenericError($ReferenceError, type, args);
249}
250
251
252function MakeEvalError(type, args) {
253 return MakeGenericError($EvalError, type, args);
254}
255
256
257function MakeError(type, args) {
258 return MakeGenericError($Error, type, args);
259}
260
261/**
262 * Find a line number given a specific source position.
263 * @param {number} position The source position.
264 * @return {number} 0 if input too small, -1 if input too large,
265 else the line number.
266 */
267Script.prototype.lineFromPosition = function(position) {
268 var lower = 0;
269 var upper = this.lineCount() - 1;
Steve Blockd0582a62009-12-15 09:54:21 +0000270 var line_ends = this.line_ends;
Steve Blocka7e24c12009-10-30 11:49:00 +0000271
272 // We'll never find invalid positions so bail right away.
Steve Blockd0582a62009-12-15 09:54:21 +0000273 if (position > line_ends[upper]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000274 return -1;
275 }
276
277 // This means we don't have to safe-guard indexing line_ends[i - 1].
Steve Blockd0582a62009-12-15 09:54:21 +0000278 if (position <= line_ends[0]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000279 return 0;
280 }
281
282 // Binary search to find line # from position range.
283 while (upper >= 1) {
284 var i = (lower + upper) >> 1;
285
Steve Blockd0582a62009-12-15 09:54:21 +0000286 if (position > line_ends[i]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000287 lower = i + 1;
Steve Blockd0582a62009-12-15 09:54:21 +0000288 } else if (position <= line_ends[i - 1]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000289 upper = i - 1;
290 } else {
291 return i;
292 }
293 }
294 return -1;
295}
296
297/**
298 * Get information on a specific source position.
299 * @param {number} position The source position
300 * @param {boolean} include_resource_offset Set to true to have the resource
301 * offset added to the location
302 * @return {SourceLocation}
303 * If line is negative or not in the source null is returned.
304 */
305Script.prototype.locationFromPosition = function (position,
306 include_resource_offset) {
307 var line = this.lineFromPosition(position);
308 if (line == -1) return null;
309
310 // Determine start, end and column.
Steve Blockd0582a62009-12-15 09:54:21 +0000311 var line_ends = this.line_ends;
312 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
313 var end = line_ends[line];
Steve Blocka7e24c12009-10-30 11:49:00 +0000314 if (end > 0 && StringCharAt.call(this.source, end - 1) == '\r') end--;
315 var column = position - start;
316
317 // Adjust according to the offset within the resource.
318 if (include_resource_offset) {
319 line += this.line_offset;
320 if (line == this.line_offset) {
321 column += this.column_offset;
322 }
323 }
324
325 return new SourceLocation(this, position, line, column, start, end);
326};
327
328
329/**
330 * Get information on a specific source line and column possibly offset by a
331 * fixed source position. This function is used to find a source position from
332 * a line and column position. The fixed source position offset is typically
333 * used to find a source position in a function based on a line and column in
334 * the source for the function alone. The offset passed will then be the
335 * start position of the source for the function within the full script source.
336 * @param {number} opt_line The line within the source. Default value is 0
337 * @param {number} opt_column The column in within the line. Default value is 0
338 * @param {number} opt_offset_position The offset from the begining of the
339 * source from where the line and column calculation starts. Default value is 0
340 * @return {SourceLocation}
341 * If line is negative or not in the source null is returned.
342 */
343Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) {
344 // Default is the first line in the script. Lines in the script is relative
345 // to the offset within the resource.
346 var line = 0;
347 if (!IS_UNDEFINED(opt_line)) {
348 line = opt_line - this.line_offset;
349 }
350
351 // Default is first column. If on the first line add the offset within the
352 // resource.
353 var column = opt_column || 0;
354 if (line == 0) {
355 column -= this.column_offset
356 }
357
358 var offset_position = opt_offset_position || 0;
359 if (line < 0 || column < 0 || offset_position < 0) return null;
360 if (line == 0) {
361 return this.locationFromPosition(offset_position + column, false);
362 } else {
363 // Find the line where the offset position is located.
364 var offset_line = this.lineFromPosition(offset_position);
365
366 if (offset_line == -1 || offset_line + line >= this.lineCount()) {
367 return null;
368 }
369
370 return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
371 }
372}
373
374
375/**
376 * Get a slice of source code from the script. The boundaries for the slice is
377 * specified in lines.
378 * @param {number} opt_from_line The first line (zero bound) in the slice.
379 * Default is 0
380 * @param {number} opt_to_column The last line (zero bound) in the slice (non
381 * inclusive). Default is the number of lines in the script
382 * @return {SourceSlice} The source slice or null of the parameters where
383 * invalid
384 */
385Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
386 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line;
387 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line
388
389 // Adjust according to the offset within the resource.
390 from_line -= this.line_offset;
391 to_line -= this.line_offset;
392 if (from_line < 0) from_line = 0;
393 if (to_line > this.lineCount()) to_line = this.lineCount();
394
395 // Check parameters.
396 if (from_line >= this.lineCount() ||
397 to_line < 0 ||
398 from_line > to_line) {
399 return null;
400 }
401
Steve Blockd0582a62009-12-15 09:54:21 +0000402 var line_ends = this.line_ends;
403 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
404 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000405
406 // Return a source slice with line numbers re-adjusted to the resource.
407 return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset,
408 from_position, to_position);
409}
410
411
412Script.prototype.sourceLine = function (opt_line) {
413 // Default is the first line in the script. Lines in the script are relative
414 // to the offset within the resource.
415 var line = 0;
416 if (!IS_UNDEFINED(opt_line)) {
417 line = opt_line - this.line_offset;
418 }
419
420 // Check parameter.
421 if (line < 0 || this.lineCount() <= line) {
422 return null;
423 }
424
425 // Return the source line.
Steve Blockd0582a62009-12-15 09:54:21 +0000426 var line_ends = this.line_ends;
427 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
428 var end = line_ends[line];
Steve Blocka7e24c12009-10-30 11:49:00 +0000429 return StringSubstring.call(this.source, start, end);
430}
431
432
433/**
434 * Returns the number of source lines.
435 * @return {number}
436 * Number of source lines.
437 */
438Script.prototype.lineCount = function() {
439 // Return number of source lines.
440 return this.line_ends.length;
441};
442
443
444/**
Steve Block6ded16b2010-05-10 14:33:55 +0100445 * Returns the name of script if available, contents of sourceURL comment
Ben Murdochf87a2032010-10-22 12:50:53 +0100446 * otherwise. See
Steve Block6ded16b2010-05-10 14:33:55 +0100447 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
448 * for details on using //@ sourceURL comment to identify scritps that don't
449 * have name.
Ben Murdochf87a2032010-10-22 12:50:53 +0100450 *
Steve Block6ded16b2010-05-10 14:33:55 +0100451 * @return {?string} script name if present, value for //@ sourceURL comment
452 * otherwise.
453 */
454Script.prototype.nameOrSourceURL = function() {
455 if (this.name)
456 return this.name;
Ben Murdochf87a2032010-10-22 12:50:53 +0100457 // TODO(608): the spaces in a regexp below had to be escaped as \040
Steve Block6ded16b2010-05-10 14:33:55 +0100458 // because this file is being processed by js2c whose handling of spaces
459 // in regexps is broken. Also, ['"] are excluded from allowed URLs to
460 // avoid matches against sources that invoke evals with sourceURL.
461 var sourceUrlPattern =
462 /\/\/@[\040\t]sourceURL=[\040\t]*([^\s'"]*)[\040\t]*$/m;
463 var match = sourceUrlPattern.exec(this.source);
464 return match ? match[1] : this.name;
465}
466
467
468/**
Steve Blocka7e24c12009-10-30 11:49:00 +0000469 * Class for source location. A source location is a position within some
470 * source with the following properties:
471 * script : script object for the source
472 * line : source line number
473 * column : source column within the line
474 * position : position within the source
475 * start : position of start of source context (inclusive)
476 * end : position of end of source context (not inclusive)
477 * Source text for the source context is the character interval [start, end[. In
478 * most cases end will point to a newline character. It might point just past
479 * the final position of the source if the last source line does not end with a
480 * newline character.
481 * @param {Script} script The Script object for which this is a location
482 * @param {number} position Source position for the location
483 * @param {number} line The line number for the location
484 * @param {number} column The column within the line for the location
485 * @param {number} start Source position for start of source context
486 * @param {number} end Source position for end of source context
487 * @constructor
488 */
489function SourceLocation(script, position, line, column, start, end) {
490 this.script = script;
491 this.position = position;
492 this.line = line;
493 this.column = column;
494 this.start = start;
495 this.end = end;
496}
497
498
499const kLineLengthLimit = 78;
500
501/**
502 * Restrict source location start and end positions to make the source slice
503 * no more that a certain number of characters wide.
504 * @param {number} opt_limit The with limit of the source text with a default
505 * of 78
506 * @param {number} opt_before The number of characters to prefer before the
507 * position with a default value of 10 less that the limit
508 */
509SourceLocation.prototype.restrict = function (opt_limit, opt_before) {
510 // Find the actual limit to use.
511 var limit;
512 var before;
513 if (!IS_UNDEFINED(opt_limit)) {
514 limit = opt_limit;
515 } else {
516 limit = kLineLengthLimit;
517 }
518 if (!IS_UNDEFINED(opt_before)) {
519 before = opt_before;
520 } else {
521 // If no before is specified center for small limits and perfer more source
522 // before the the position that after for longer limits.
523 if (limit <= 20) {
524 before = $floor(limit / 2);
525 } else {
526 before = limit - 10;
527 }
528 }
529 if (before >= limit) {
530 before = limit - 1;
531 }
532
533 // If the [start, end[ interval is too big we restrict
534 // it in one or both ends. We make sure to always produce
535 // restricted intervals of maximum allowed size.
536 if (this.end - this.start > limit) {
537 var start_limit = this.position - before;
538 var end_limit = this.position + limit - before;
539 if (this.start < start_limit && end_limit < this.end) {
540 this.start = start_limit;
541 this.end = end_limit;
542 } else if (this.start < start_limit) {
543 this.start = this.end - limit;
544 } else {
545 this.end = this.start + limit;
546 }
547 }
548};
549
550
551/**
552 * Get the source text for a SourceLocation
553 * @return {String}
554 * Source text for this location.
555 */
556SourceLocation.prototype.sourceText = function () {
557 return StringSubstring.call(this.script.source, this.start, this.end);
558};
559
560
561/**
562 * Class for a source slice. A source slice is a part of a script source with
563 * the following properties:
564 * script : script object for the source
565 * from_line : line number for the first line in the slice
566 * to_line : source line number for the last line in the slice
567 * from_position : position of the first character in the slice
568 * to_position : position of the last character in the slice
569 * The to_line and to_position are not included in the slice, that is the lines
570 * in the slice are [from_line, to_line[. Likewise the characters in the slice
571 * are [from_position, to_position[.
572 * @param {Script} script The Script object for the source slice
573 * @param {number} from_line
574 * @param {number} to_line
575 * @param {number} from_position
576 * @param {number} to_position
577 * @constructor
578 */
579function SourceSlice(script, from_line, to_line, from_position, to_position) {
580 this.script = script;
581 this.from_line = from_line;
582 this.to_line = to_line;
583 this.from_position = from_position;
584 this.to_position = to_position;
585}
586
587
588/**
589 * Get the source text for a SourceSlice
590 * @return {String} Source text for this slice. The last line will include
591 * the line terminating characters (if any)
592 */
593SourceSlice.prototype.sourceText = function () {
594 return StringSubstring.call(this.script.source, this.from_position, this.to_position);
595};
596
597
598// Returns the offset of the given position within the containing
599// line.
600function GetPositionInLine(message) {
601 var location = message.script.locationFromPosition(message.startPos, false);
602 if (location == null) return -1;
603 location.restrict();
604 return message.startPos - location.start;
605}
606
607
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100608function ErrorMessage(type, args, startPos, endPos, script, stackTrace,
609 stackFrames) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000610 this.startPos = startPos;
611 this.endPos = endPos;
612 this.type = type;
613 this.args = args;
614 this.script = script;
615 this.stackTrace = stackTrace;
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100616 this.stackFrames = stackFrames;
Steve Blocka7e24c12009-10-30 11:49:00 +0000617}
618
619
Ben Murdoch3bec4d22010-07-22 14:51:16 +0100620function MakeMessage(type, args, startPos, endPos, script, stackTrace,
621 stackFrames) {
622 return new ErrorMessage(type, args, startPos, endPos, script, stackTrace,
623 stackFrames);
Steve Blocka7e24c12009-10-30 11:49:00 +0000624}
625
626
627function GetStackTraceLine(recv, fun, pos, isGlobal) {
628 return FormatSourcePosition(new CallSite(recv, fun, pos));
629}
630
631// ----------------------------------------------------------------------------
632// Error implementation
633
634// Defines accessors for a property that is calculated the first time
635// the property is read.
636function DefineOneShotAccessor(obj, name, fun) {
637 // Note that the accessors consistently operate on 'obj', not 'this'.
638 // Since the object may occur in someone else's prototype chain we
639 // can't rely on 'this' being the same as 'obj'.
640 var hasBeenSet = false;
641 var value;
642 obj.__defineGetter__(name, function () {
643 if (hasBeenSet) {
644 return value;
645 }
646 hasBeenSet = true;
647 value = fun(obj);
648 return value;
649 });
650 obj.__defineSetter__(name, function (v) {
651 hasBeenSet = true;
652 value = v;
653 });
654}
655
656function CallSite(receiver, fun, pos) {
657 this.receiver = receiver;
658 this.fun = fun;
659 this.pos = pos;
660}
661
662CallSite.prototype.getThis = function () {
663 return this.receiver;
664};
665
666CallSite.prototype.getTypeName = function () {
667 var constructor = this.receiver.constructor;
668 if (!constructor)
669 return $Object.prototype.toString.call(this.receiver);
670 var constructorName = constructor.name;
671 if (!constructorName)
672 return $Object.prototype.toString.call(this.receiver);
673 return constructorName;
674};
675
676CallSite.prototype.isToplevel = function () {
677 if (this.receiver == null)
678 return true;
679 return IS_GLOBAL(this.receiver);
680};
681
682CallSite.prototype.isEval = function () {
683 var script = %FunctionGetScript(this.fun);
Andrei Popescu31002712010-02-23 13:46:05 +0000684 return script && script.compilation_type == COMPILATION_TYPE_EVAL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000685};
686
687CallSite.prototype.getEvalOrigin = function () {
688 var script = %FunctionGetScript(this.fun);
Steve Blockd0582a62009-12-15 09:54:21 +0000689 return FormatEvalOrigin(script);
Steve Blocka7e24c12009-10-30 11:49:00 +0000690};
691
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100692CallSite.prototype.getScriptNameOrSourceURL = function () {
693 var script = %FunctionGetScript(this.fun);
694 return script ? script.nameOrSourceURL() : null;
695};
696
Steve Blocka7e24c12009-10-30 11:49:00 +0000697CallSite.prototype.getFunction = function () {
698 return this.fun;
699};
700
701CallSite.prototype.getFunctionName = function () {
702 // See if the function knows its own name
703 var name = this.fun.name;
704 if (name) {
705 return name;
706 } else {
707 return %FunctionGetInferredName(this.fun);
708 }
709 // Maybe this is an evaluation?
710 var script = %FunctionGetScript(this.fun);
Andrei Popescu31002712010-02-23 13:46:05 +0000711 if (script && script.compilation_type == COMPILATION_TYPE_EVAL)
Steve Blocka7e24c12009-10-30 11:49:00 +0000712 return "eval";
713 return null;
714};
715
716CallSite.prototype.getMethodName = function () {
717 // See if we can find a unique property on the receiver that holds
718 // this function.
719 var ownName = this.fun.name;
Iain Merrick75681382010-08-19 15:07:18 +0100720 if (ownName && this.receiver &&
721 (ObjectLookupGetter.call(this.receiver, ownName) === this.fun ||
722 ObjectLookupSetter.call(this.receiver, ownName) === this.fun ||
723 this.receiver[ownName] === this.fun)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000724 // To handle DontEnum properties we guess that the method has
725 // the same name as the function.
726 return ownName;
Iain Merrick75681382010-08-19 15:07:18 +0100727 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000728 var name = null;
729 for (var prop in this.receiver) {
Iain Merrick75681382010-08-19 15:07:18 +0100730 if (this.receiver.__lookupGetter__(prop) === this.fun ||
731 this.receiver.__lookupSetter__(prop) === this.fun ||
732 (!this.receiver.__lookupGetter__(prop) && this.receiver[prop] === this.fun)) {
733 // If we find more than one match bail out to avoid confusion.
Steve Blocka7e24c12009-10-30 11:49:00 +0000734 if (name)
735 return null;
736 name = prop;
737 }
738 }
739 if (name)
740 return name;
741 return null;
742};
743
744CallSite.prototype.getFileName = function () {
745 var script = %FunctionGetScript(this.fun);
746 return script ? script.name : null;
747};
748
749CallSite.prototype.getLineNumber = function () {
750 if (this.pos == -1)
751 return null;
752 var script = %FunctionGetScript(this.fun);
753 var location = null;
754 if (script) {
755 location = script.locationFromPosition(this.pos, true);
756 }
757 return location ? location.line + 1 : null;
758};
759
760CallSite.prototype.getColumnNumber = function () {
761 if (this.pos == -1)
762 return null;
763 var script = %FunctionGetScript(this.fun);
764 var location = null;
765 if (script) {
766 location = script.locationFromPosition(this.pos, true);
767 }
Steve Blockd0582a62009-12-15 09:54:21 +0000768 return location ? location.column + 1: null;
Steve Blocka7e24c12009-10-30 11:49:00 +0000769};
770
771CallSite.prototype.isNative = function () {
772 var script = %FunctionGetScript(this.fun);
Andrei Popescu31002712010-02-23 13:46:05 +0000773 return script ? (script.type == TYPE_NATIVE) : false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000774};
775
776CallSite.prototype.getPosition = function () {
777 return this.pos;
778};
779
780CallSite.prototype.isConstructor = function () {
781 var constructor = this.receiver ? this.receiver.constructor : null;
782 if (!constructor)
783 return false;
784 return this.fun === constructor;
785};
786
Steve Blockd0582a62009-12-15 09:54:21 +0000787function FormatEvalOrigin(script) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100788 var sourceURL = script.nameOrSourceURL();
789 if (sourceURL)
790 return sourceURL;
791
792 var eval_origin = "eval at ";
Steve Blockd0582a62009-12-15 09:54:21 +0000793 if (script.eval_from_function_name) {
794 eval_origin += script.eval_from_function_name;
795 } else {
796 eval_origin += "<anonymous>";
797 }
Steve Block6ded16b2010-05-10 14:33:55 +0100798
Steve Blockd0582a62009-12-15 09:54:21 +0000799 var eval_from_script = script.eval_from_script;
800 if (eval_from_script) {
Andrei Popescu31002712010-02-23 13:46:05 +0000801 if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
Steve Blockd0582a62009-12-15 09:54:21 +0000802 // eval script originated from another eval.
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100803 eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
Steve Blockd0582a62009-12-15 09:54:21 +0000804 } else {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100805 // eval script originated from "real" source.
Steve Blockd0582a62009-12-15 09:54:21 +0000806 if (eval_from_script.name) {
807 eval_origin += " (" + eval_from_script.name;
808 var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true);
809 if (location) {
810 eval_origin += ":" + (location.line + 1);
811 eval_origin += ":" + (location.column + 1);
812 }
813 eval_origin += ")"
814 } else {
815 eval_origin += " (unknown source)";
816 }
817 }
818 }
Steve Block6ded16b2010-05-10 14:33:55 +0100819
Steve Blockd0582a62009-12-15 09:54:21 +0000820 return eval_origin;
821};
822
Steve Blocka7e24c12009-10-30 11:49:00 +0000823function FormatSourcePosition(frame) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100824 var fileName;
Steve Blocka7e24c12009-10-30 11:49:00 +0000825 var fileLocation = "";
826 if (frame.isNative()) {
827 fileLocation = "native";
828 } else if (frame.isEval()) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100829 fileName = frame.getScriptNameOrSourceURL();
830 if (!fileName)
831 fileLocation = frame.getEvalOrigin();
Steve Blocka7e24c12009-10-30 11:49:00 +0000832 } else {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100833 fileName = frame.getFileName();
834 }
835
836 if (fileName) {
837 fileLocation += fileName;
838 var lineNumber = frame.getLineNumber();
839 if (lineNumber != null) {
840 fileLocation += ":" + lineNumber;
841 var columnNumber = frame.getColumnNumber();
842 if (columnNumber) {
843 fileLocation += ":" + columnNumber;
Steve Blocka7e24c12009-10-30 11:49:00 +0000844 }
845 }
846 }
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100847
Steve Blocka7e24c12009-10-30 11:49:00 +0000848 if (!fileLocation) {
849 fileLocation = "unknown source";
850 }
851 var line = "";
852 var functionName = frame.getFunction().name;
Steve Blocka7e24c12009-10-30 11:49:00 +0000853 var addPrefix = true;
854 var isConstructor = frame.isConstructor();
855 var isMethodCall = !(frame.isToplevel() || isConstructor);
856 if (isMethodCall) {
Iain Merrick9ac36c92010-09-13 15:29:50 +0100857 var methodName = frame.getMethodName();
Steve Blocka7e24c12009-10-30 11:49:00 +0000858 line += frame.getTypeName() + ".";
859 if (functionName) {
860 line += functionName;
861 if (methodName && (methodName != functionName)) {
862 line += " [as " + methodName + "]";
863 }
864 } else {
865 line += methodName || "<anonymous>";
866 }
867 } else if (isConstructor) {
868 line += "new " + (functionName || "<anonymous>");
869 } else if (functionName) {
870 line += functionName;
871 } else {
872 line += fileLocation;
873 addPrefix = false;
874 }
875 if (addPrefix) {
876 line += " (" + fileLocation + ")";
877 }
878 return line;
879}
880
881function FormatStackTrace(error, frames) {
882 var lines = [];
883 try {
884 lines.push(error.toString());
885 } catch (e) {
886 try {
887 lines.push("<error: " + e + ">");
888 } catch (ee) {
889 lines.push("<error>");
890 }
891 }
892 for (var i = 0; i < frames.length; i++) {
893 var frame = frames[i];
894 var line;
895 try {
896 line = FormatSourcePosition(frame);
897 } catch (e) {
898 try {
899 line = "<error: " + e + ">";
900 } catch (ee) {
901 // Any code that reaches this point is seriously nasty!
902 line = "<error>";
903 }
904 }
905 lines.push(" at " + line);
906 }
907 return lines.join("\n");
908}
909
910function FormatRawStackTrace(error, raw_stack) {
911 var frames = [ ];
Ben Murdochb0fe1622011-05-05 13:52:32 +0100912 for (var i = 0; i < raw_stack.length; i += 4) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000913 var recv = raw_stack[i];
Ben Murdochb0fe1622011-05-05 13:52:32 +0100914 var fun = raw_stack[i + 1];
915 var code = raw_stack[i + 2];
916 var pc = raw_stack[i + 3];
917 var pos = %FunctionGetPositionForOffset(code, pc);
Steve Blocka7e24c12009-10-30 11:49:00 +0000918 frames.push(new CallSite(recv, fun, pos));
919 }
920 if (IS_FUNCTION($Error.prepareStackTrace)) {
921 return $Error.prepareStackTrace(error, frames);
922 } else {
923 return FormatStackTrace(error, frames);
924 }
925}
926
927function DefineError(f) {
928 // Store the error function in both the global object
929 // and the runtime object. The function is fetched
930 // from the runtime object when throwing errors from
931 // within the runtime system to avoid strange side
932 // effects when overwriting the error functions from
933 // user code.
934 var name = f.name;
935 %SetProperty(global, name, f, DONT_ENUM);
936 this['$' + name] = f;
937 // Configure the error function.
938 if (name == 'Error') {
939 // The prototype of the Error object must itself be an error.
940 // However, it can't be an instance of the Error object because
941 // it hasn't been properly configured yet. Instead we create a
942 // special not-a-true-error-but-close-enough object.
943 function ErrorPrototype() {}
944 %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
945 %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
946 %FunctionSetPrototype(f, new ErrorPrototype());
947 } else {
948 %FunctionSetPrototype(f, new $Error());
949 }
950 %FunctionSetInstanceClassName(f, 'Error');
951 %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
Ben Murdochb8e0da22011-05-16 14:20:40 +0100952 // The name property on the prototype of error objects is not
953 // specified as being read-one and dont-delete. However, allowing
954 // overwriting allows leaks of error objects between script blocks
955 // in the same context in a browser setting. Therefore we fix the
956 // name.
957 %SetProperty(f.prototype, "name", name, READ_ONLY | DONT_DELETE);
Steve Blocka7e24c12009-10-30 11:49:00 +0000958 %SetCode(f, function(m) {
959 if (%_IsConstructCall()) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100960 // Define all the expected properties directly on the error
961 // object. This avoids going through getters and setters defined
962 // on prototype objects.
963 %IgnoreAttributesAndSetProperty(this, 'stack', void 0);
964 %IgnoreAttributesAndSetProperty(this, 'arguments', void 0);
965 %IgnoreAttributesAndSetProperty(this, 'type', void 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000966 if (m === kAddMessageAccessorsMarker) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100967 // DefineOneShotAccessor always inserts a message property and
968 // ignores setters.
Steve Blocka7e24c12009-10-30 11:49:00 +0000969 DefineOneShotAccessor(this, 'message', function (obj) {
970 return FormatMessage({type: obj.type, args: obj.arguments});
971 });
972 } else if (!IS_UNDEFINED(m)) {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100973 %IgnoreAttributesAndSetProperty(this, 'message', ToString(m));
Steve Blocka7e24c12009-10-30 11:49:00 +0000974 }
975 captureStackTrace(this, f);
976 } else {
977 return new f(m);
978 }
979 });
980}
981
982function captureStackTrace(obj, cons_opt) {
983 var stackTraceLimit = $Error.stackTraceLimit;
984 if (!stackTraceLimit) return;
985 if (stackTraceLimit < 0 || stackTraceLimit > 10000)
986 stackTraceLimit = 10000;
987 var raw_stack = %CollectStackTrace(cons_opt ? cons_opt : captureStackTrace,
988 stackTraceLimit);
989 DefineOneShotAccessor(obj, 'stack', function (obj) {
990 return FormatRawStackTrace(obj, raw_stack);
991 });
992};
993
994$Math.__proto__ = global.Object.prototype;
995
996DefineError(function Error() { });
997DefineError(function TypeError() { });
998DefineError(function RangeError() { });
999DefineError(function SyntaxError() { });
1000DefineError(function ReferenceError() { });
1001DefineError(function EvalError() { });
1002DefineError(function URIError() { });
1003
1004$Error.captureStackTrace = captureStackTrace;
1005
1006// Setup extra properties of the Error.prototype object.
1007$Error.prototype.message = '';
1008
Ben Murdochb8e0da22011-05-16 14:20:40 +01001009function errorToString() {
Steve Blocka7e24c12009-10-30 11:49:00 +00001010 var type = this.type;
1011 if (type && !this.hasOwnProperty("message")) {
1012 return this.name + ": " + FormatMessage({ type: type, args: this.arguments });
1013 }
Ben Murdochb8e0da22011-05-16 14:20:40 +01001014 var message = this.hasOwnProperty("message") ? (": " + this.message) : "";
1015 return this.name + message;
1016}
1017
1018%FunctionSetName(errorToString, 'toString');
1019%SetProperty($Error.prototype, 'toString', errorToString, DONT_ENUM);
Steve Blocka7e24c12009-10-30 11:49:00 +00001020
1021
1022// Boilerplate for exceptions for stack overflows. Used from
1023// Top::StackOverflow().
1024const kStackOverflowBoilerplate = MakeRangeError('stack_overflow', []);