• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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// -------------------------------------------------------------------
30//
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;
40
41// Matches Messages::kNoLineNumberInfo from v8.h
42var kNoLineNumberInfo = 0;
43
44// 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
49var kMessages = 0;
50
51var kReplacementMarkers = [ "%0", "%1", "%2", "%3" ];
52
53function 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      }
66    }
67    result += str;
68  }
69  return result;
70}
71
72
73// 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
89// 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) {
94  if (IsNativeErrorObject(obj)) {
95    return %_CallFunction(obj, errorToString);
96  } else {
97    return ToString(obj);
98  }
99}
100
101
102function 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;
107    if (!constructorName || !IS_STRING(constructorName)) {
108      return ToStringCheckErrorObject(obj);
109    }
110    return "#<" + constructorName + ">";
111  } else {
112    return ToStringCheckErrorObject(obj);
113  }
114}
115
116
117function MakeGenericError(constructor, type, args) {
118  if (IS_UNDEFINED(args)) {
119    args = [];
120  }
121  var e = new constructor(kAddMessageAccessorsMarker);
122  e.type = type;
123  e.arguments = args;
124  return e;
125}
126
127
128/**
129 * Setup the Script function and constructor.
130 */
131%FunctionSetInstanceClassName(Script, 'Script');
132%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM);
133%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) {
141  if (kMessages === 0) {
142    kMessages = {
143      // Error
144      cyclic_proto:                 ["Cyclic __proto__ value"],
145      // TypeError
146      unexpected_token:             ["Unexpected token ", "%0"],
147      unexpected_token_number:      ["Unexpected number"],
148      unexpected_token_string:      ["Unexpected string"],
149      unexpected_token_identifier:  ["Unexpected identifier"],
150      unexpected_strict_reserved:   ["Unexpected strict mode reserved word"],
151      unexpected_eos:               ["Unexpected end of input"],
152      malformed_regexp:             ["Invalid regular expression: /", "%0", "/: ", "%1"],
153      unterminated_regexp:          ["Invalid regular expression: missing /"],
154      regexp_flags:                 ["Cannot supply flags when constructing one RegExp from another"],
155      incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"],
156      invalid_lhs_in_assignment:    ["Invalid left-hand side in assignment"],
157      invalid_lhs_in_for_in:        ["Invalid left-hand side in for-in"],
158      invalid_lhs_in_postfix_op:    ["Invalid left-hand side expression in postfix operation"],
159      invalid_lhs_in_prefix_op:     ["Invalid left-hand side expression in prefix operation"],
160      multiple_defaults_in_switch:  ["More than one default clause in switch statement"],
161      newline_after_throw:          ["Illegal newline after throw"],
162      redeclaration:                ["%0", " '", "%1", "' has already been declared"],
163      no_catch_or_finally:          ["Missing catch or finally after try"],
164      unknown_label:                ["Undefined label '", "%0", "'"],
165      uncaught_exception:           ["Uncaught ", "%0"],
166      stack_trace:                  ["Stack Trace:\n", "%0"],
167      called_non_callable:          ["%0", " is not a function"],
168      undefined_method:             ["Object ", "%1", " has no method '", "%0", "'"],
169      property_not_function:        ["Property '", "%0", "' of object ", "%1", " is not a function"],
170      cannot_convert_to_primitive:  ["Cannot convert object to primitive value"],
171      not_constructor:              ["%0", " is not a constructor"],
172      not_defined:                  ["%0", " is not defined"],
173      non_object_property_load:     ["Cannot read property '", "%0", "' of ", "%1"],
174      non_object_property_store:    ["Cannot set property '", "%0", "' of ", "%1"],
175      non_object_property_call:     ["Cannot call method '", "%0", "' of ", "%1"],
176      with_expression:              ["%0", " has no properties"],
177      illegal_invocation:           ["Illegal invocation"],
178      no_setter_in_callback:        ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
179      apply_non_function:           ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
180      apply_wrong_args:             ["Function.prototype.apply: Arguments list has wrong type"],
181      invalid_in_operator_use:      ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
182      instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"],
183      instanceof_nonobject_proto:   ["Function has non-object prototype '", "%0", "' in instanceof check"],
184      null_to_object:               ["Cannot convert null to object"],
185      reduce_no_initial:            ["Reduce of empty array with no initial value"],
186      getter_must_be_callable:      ["Getter must be a function: ", "%0"],
187      setter_must_be_callable:      ["Setter must be a function: ", "%0"],
188      value_and_accessor:           ["Invalid property.  A property cannot both have accessors and be writable or have a value: ", "%0"],
189      proto_object_or_null:         ["Object prototype may only be an Object or null"],
190      property_desc_object:         ["Property description must be an object: ", "%0"],
191      redefine_disallowed:          ["Cannot redefine property: ", "%0"],
192      define_disallowed:            ["Cannot define property, object is not extensible: ", "%0"],
193      non_extensible_proto:         ["%0", " is not extensible"],
194      // RangeError
195      invalid_array_length:         ["Invalid array length"],
196      stack_overflow:               ["Maximum call stack size exceeded"],
197      // SyntaxError
198      unable_to_parse:              ["Parse error"],
199      duplicate_regexp_flag:        ["Duplicate RegExp flag ", "%0"],
200      invalid_regexp:               ["Invalid RegExp pattern /", "%0", "/"],
201      illegal_break:                ["Illegal break statement"],
202      illegal_continue:             ["Illegal continue statement"],
203      illegal_return:               ["Illegal return statement"],
204      error_loading_debugger:       ["Error loading debugger"],
205      no_input_to_regexp:           ["No input to ", "%0"],
206      invalid_json:                 ["String '", "%0", "' is not valid JSON"],
207      circular_structure:           ["Converting circular structure to JSON"],
208      obj_ctor_property_non_object: ["Object.", "%0", " called on non-object"],
209      array_indexof_not_defined:    ["Array.getIndexOf: Argument undefined"],
210      object_not_extensible:        ["Can't add property ", "%0", ", object is not extensible"],
211      illegal_access:               ["Illegal access"],
212      invalid_preparser_data:       ["Invalid preparser data for function ", "%0"],
213      strict_mode_with:             ["Strict mode code may not include a with statement"],
214      strict_catch_variable:        ["Catch variable may not be eval or arguments in strict mode"],
215      too_many_arguments:           ["Too many arguments in function call (only 32766 allowed)"],
216      too_many_parameters:          ["Too many parameters in function definition (only 32766 allowed)"],
217      too_many_variables:           ["Too many variables declared (only 32767 allowed)"],
218      strict_param_name:            ["Parameter name eval or arguments is not allowed in strict mode"],
219      strict_param_dupe:            ["Strict mode function may not have duplicate parameter names"],
220      strict_var_name:              ["Variable name may not be eval or arguments in strict mode"],
221      strict_function_name:         ["Function name may not be eval or arguments in strict mode"],
222      strict_octal_literal:         ["Octal literals are not allowed in strict mode."],
223      strict_duplicate_property:    ["Duplicate data property in object literal not allowed in strict mode"],
224      accessor_data_property:       ["Object literal may not have data and accessor property with the same name"],
225      accessor_get_set:             ["Object literal may not have multiple get/set accessors with the same name"],
226      strict_lhs_assignment:        ["Assignment to eval or arguments is not allowed in strict mode"],
227      strict_lhs_postfix:           ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
228      strict_lhs_prefix:            ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
229      strict_reserved_word:         ["Use of future reserved word in strict mode"],
230      strict_delete:                ["Delete of an unqualified identifier in strict mode."],
231      strict_delete_property:       ["Cannot delete property '", "%0", "' of ", "%1"],
232      strict_const:                 ["Use of const in strict mode."],
233      strict_function:              ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
234      strict_read_only_property:    ["Cannot assign to read only property '", "%0", "' of ", "%1"],
235      strict_cannot_assign:         ["Cannot assign to read only '", "%0", "' in strict mode"],
236      strict_arguments_callee:      ["Cannot access property 'callee' of strict mode arguments"],
237      strict_arguments_caller:      ["Cannot access property 'caller' of strict mode arguments"],
238      strict_function_caller:       ["Cannot access property 'caller' of a strict mode function"],
239      strict_function_arguments:    ["Cannot access property 'arguments' of a strict mode function"],
240      strict_caller:                ["Illegal access to a strict mode caller function."],
241    };
242  }
243  var message_type = %MessageGetType(message);
244  var format = kMessages[message_type];
245  if (!format) return "<unknown message " + message_type + ">";
246  return FormatString(format, message);
247}
248
249
250function GetLineNumber(message) {
251  var start_position = %MessageGetStartPosition(message);
252  if (start_position == -1) return kNoLineNumberInfo;
253  var script = %MessageGetScript(message);
254  var location = script.locationFromPosition(start_position, true);
255  if (location == null) return kNoLineNumberInfo;
256  return location.line + 1;
257}
258
259
260// Returns the source code line containing the given source
261// position, or the empty string if the position is invalid.
262function GetSourceLine(message) {
263  var script = %MessageGetScript(message);
264  var start_position = %MessageGetStartPosition(message);
265  var location = script.locationFromPosition(start_position, true);
266  if (location == null) return "";
267  location.restrict();
268  return location.sourceText();
269}
270
271
272function MakeTypeError(type, args) {
273  return MakeGenericError($TypeError, type, args);
274}
275
276
277function MakeRangeError(type, args) {
278  return MakeGenericError($RangeError, type, args);
279}
280
281
282function MakeSyntaxError(type, args) {
283  return MakeGenericError($SyntaxError, type, args);
284}
285
286
287function MakeReferenceError(type, args) {
288  return MakeGenericError($ReferenceError, type, args);
289}
290
291
292function MakeEvalError(type, args) {
293  return MakeGenericError($EvalError, type, args);
294}
295
296
297function MakeError(type, args) {
298  return MakeGenericError($Error, type, args);
299}
300
301/**
302 * Find a line number given a specific source position.
303 * @param {number} position The source position.
304 * @return {number} 0 if input too small, -1 if input too large,
305       else the line number.
306 */
307Script.prototype.lineFromPosition = function(position) {
308  var lower = 0;
309  var upper = this.lineCount() - 1;
310  var line_ends = this.line_ends;
311
312  // We'll never find invalid positions so bail right away.
313  if (position > line_ends[upper]) {
314    return -1;
315  }
316
317  // This means we don't have to safe-guard indexing line_ends[i - 1].
318  if (position <= line_ends[0]) {
319    return 0;
320  }
321
322  // Binary search to find line # from position range.
323  while (upper >= 1) {
324    var i = (lower + upper) >> 1;
325
326    if (position > line_ends[i]) {
327      lower = i + 1;
328    } else if (position <= line_ends[i - 1]) {
329      upper = i - 1;
330    } else {
331      return i;
332    }
333  }
334
335  return -1;
336}
337
338/**
339 * Get information on a specific source position.
340 * @param {number} position The source position
341 * @param {boolean} include_resource_offset Set to true to have the resource
342 *     offset added to the location
343 * @return {SourceLocation}
344 *     If line is negative or not in the source null is returned.
345 */
346Script.prototype.locationFromPosition = function (position,
347                                                  include_resource_offset) {
348  var line = this.lineFromPosition(position);
349  if (line == -1) return null;
350
351  // Determine start, end and column.
352  var line_ends = this.line_ends;
353  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
354  var end = line_ends[line];
355  if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') end--;
356  var column = position - start;
357
358  // Adjust according to the offset within the resource.
359  if (include_resource_offset) {
360    line += this.line_offset;
361    if (line == this.line_offset) {
362      column += this.column_offset;
363    }
364  }
365
366  return new SourceLocation(this, position, line, column, start, end);
367};
368
369
370/**
371 * Get information on a specific source line and column possibly offset by a
372 * fixed source position. This function is used to find a source position from
373 * a line and column position. The fixed source position offset is typically
374 * used to find a source position in a function based on a line and column in
375 * the source for the function alone. The offset passed will then be the
376 * start position of the source for the function within the full script source.
377 * @param {number} opt_line The line within the source. Default value is 0
378 * @param {number} opt_column The column in within the line. Default value is 0
379 * @param {number} opt_offset_position The offset from the begining of the
380 *     source from where the line and column calculation starts. Default value is 0
381 * @return {SourceLocation}
382 *     If line is negative or not in the source null is returned.
383 */
384Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) {
385  // Default is the first line in the script. Lines in the script is relative
386  // to the offset within the resource.
387  var line = 0;
388  if (!IS_UNDEFINED(opt_line)) {
389    line = opt_line - this.line_offset;
390  }
391
392  // Default is first column. If on the first line add the offset within the
393  // resource.
394  var column = opt_column || 0;
395  if (line == 0) {
396    column -= this.column_offset
397  }
398
399  var offset_position = opt_offset_position || 0;
400  if (line < 0 || column < 0 || offset_position < 0) return null;
401  if (line == 0) {
402    return this.locationFromPosition(offset_position + column, false);
403  } else {
404    // Find the line where the offset position is located.
405    var offset_line = this.lineFromPosition(offset_position);
406
407    if (offset_line == -1 || offset_line + line >= this.lineCount()) {
408      return null;
409    }
410
411    return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column);  // line > 0 here.
412  }
413}
414
415
416/**
417 * Get a slice of source code from the script. The boundaries for the slice is
418 * specified in lines.
419 * @param {number} opt_from_line The first line (zero bound) in the slice.
420 *     Default is 0
421 * @param {number} opt_to_column The last line (zero bound) in the slice (non
422 *     inclusive). Default is the number of lines in the script
423 * @return {SourceSlice} The source slice or null of the parameters where
424 *     invalid
425 */
426Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
427  var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line;
428  var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line
429
430  // Adjust according to the offset within the resource.
431  from_line -= this.line_offset;
432  to_line -= this.line_offset;
433  if (from_line < 0) from_line = 0;
434  if (to_line > this.lineCount()) to_line = this.lineCount();
435
436  // Check parameters.
437  if (from_line >= this.lineCount() ||
438      to_line < 0 ||
439      from_line > to_line) {
440    return null;
441  }
442
443  var line_ends = this.line_ends;
444  var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
445  var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
446
447  // Return a source slice with line numbers re-adjusted to the resource.
448  return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset,
449                         from_position, to_position);
450}
451
452
453Script.prototype.sourceLine = function (opt_line) {
454  // Default is the first line in the script. Lines in the script are relative
455  // to the offset within the resource.
456  var line = 0;
457  if (!IS_UNDEFINED(opt_line)) {
458    line = opt_line - this.line_offset;
459  }
460
461  // Check parameter.
462  if (line < 0 || this.lineCount() <= line) {
463    return null;
464  }
465
466  // Return the source line.
467  var line_ends = this.line_ends;
468  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
469  var end = line_ends[line];
470  return %_CallFunction(this.source, start, end, StringSubstring);
471}
472
473
474/**
475 * Returns the number of source lines.
476 * @return {number}
477 *     Number of source lines.
478 */
479Script.prototype.lineCount = function() {
480  // Return number of source lines.
481  return this.line_ends.length;
482};
483
484
485/**
486 * Returns the name of script if available, contents of sourceURL comment
487 * otherwise. See
488 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
489 * for details on using //@ sourceURL comment to identify scritps that don't
490 * have name.
491 *
492 * @return {?string} script name if present, value for //@ sourceURL comment
493 * otherwise.
494 */
495Script.prototype.nameOrSourceURL = function() {
496  if (this.name)
497    return this.name;
498  // TODO(608): the spaces in a regexp below had to be escaped as \040
499  // because this file is being processed by js2c whose handling of spaces
500  // in regexps is broken. Also, ['"] are excluded from allowed URLs to
501  // avoid matches against sources that invoke evals with sourceURL.
502  // A better solution would be to detect these special comments in
503  // the scanner/parser.
504  var source = ToString(this.source);
505  var sourceUrlPos = %StringIndexOf(source, "sourceURL=", 0);
506  if (sourceUrlPos > 4) {
507    var sourceUrlPattern =
508        /\/\/@[\040\t]sourceURL=[\040\t]*([^\s\'\"]*)[\040\t]*$/gm;
509    // Don't reuse lastMatchInfo here, so we create a new array with room
510    // for four captures (array with length one longer than the index
511    // of the fourth capture, where the numbering is zero-based).
512    var matchInfo = new InternalArray(CAPTURE(3) + 1);
513    var match =
514        %_RegExpExec(sourceUrlPattern, source, sourceUrlPos - 4, matchInfo);
515    if (match) {
516      return SubString(source, matchInfo[CAPTURE(2)], matchInfo[CAPTURE(3)]);
517    }
518  }
519  return this.name;
520}
521
522
523/**
524 * Class for source location. A source location is a position within some
525 * source with the following properties:
526 *   script   : script object for the source
527 *   line     : source line number
528 *   column   : source column within the line
529 *   position : position within the source
530 *   start    : position of start of source context (inclusive)
531 *   end      : position of end of source context (not inclusive)
532 * Source text for the source context is the character interval [start, end[. In
533 * most cases end will point to a newline character. It might point just past
534 * the final position of the source if the last source line does not end with a
535 * newline character.
536 * @param {Script} script The Script object for which this is a location
537 * @param {number} position Source position for the location
538 * @param {number} line The line number for the location
539 * @param {number} column The column within the line for the location
540 * @param {number} start Source position for start of source context
541 * @param {number} end Source position for end of source context
542 * @constructor
543 */
544function SourceLocation(script, position, line, column, start, end) {
545  this.script = script;
546  this.position = position;
547  this.line = line;
548  this.column = column;
549  this.start = start;
550  this.end = end;
551}
552
553
554const kLineLengthLimit = 78;
555
556/**
557 * Restrict source location start and end positions to make the source slice
558 * no more that a certain number of characters wide.
559 * @param {number} opt_limit The with limit of the source text with a default
560 *     of 78
561 * @param {number} opt_before The number of characters to prefer before the
562 *     position with a default value of 10 less that the limit
563 */
564SourceLocation.prototype.restrict = function (opt_limit, opt_before) {
565  // Find the actual limit to use.
566  var limit;
567  var before;
568  if (!IS_UNDEFINED(opt_limit)) {
569    limit = opt_limit;
570  } else {
571    limit = kLineLengthLimit;
572  }
573  if (!IS_UNDEFINED(opt_before)) {
574    before = opt_before;
575  } else {
576    // If no before is specified center for small limits and perfer more source
577    // before the the position that after for longer limits.
578    if (limit <= 20) {
579      before = $floor(limit / 2);
580    } else {
581      before = limit - 10;
582    }
583  }
584  if (before >= limit) {
585    before = limit - 1;
586  }
587
588  // If the [start, end[ interval is too big we restrict
589  // it in one or both ends. We make sure to always produce
590  // restricted intervals of maximum allowed size.
591  if (this.end - this.start > limit) {
592    var start_limit = this.position - before;
593    var end_limit = this.position + limit - before;
594    if (this.start < start_limit && end_limit < this.end) {
595      this.start = start_limit;
596      this.end = end_limit;
597    } else if (this.start < start_limit) {
598      this.start = this.end - limit;
599    } else {
600      this.end = this.start + limit;
601    }
602  }
603};
604
605
606/**
607 * Get the source text for a SourceLocation
608 * @return {String}
609 *     Source text for this location.
610 */
611SourceLocation.prototype.sourceText = function () {
612  return %_CallFunction(this.script.source, this.start, this.end, StringSubstring);
613};
614
615
616/**
617 * Class for a source slice. A source slice is a part of a script source with
618 * the following properties:
619 *   script        : script object for the source
620 *   from_line     : line number for the first line in the slice
621 *   to_line       : source line number for the last line in the slice
622 *   from_position : position of the first character in the slice
623 *   to_position   : position of the last character in the slice
624 * The to_line and to_position are not included in the slice, that is the lines
625 * in the slice are [from_line, to_line[. Likewise the characters in the slice
626 * are [from_position, to_position[.
627 * @param {Script} script The Script object for the source slice
628 * @param {number} from_line
629 * @param {number} to_line
630 * @param {number} from_position
631 * @param {number} to_position
632 * @constructor
633 */
634function SourceSlice(script, from_line, to_line, from_position, to_position) {
635  this.script = script;
636  this.from_line = from_line;
637  this.to_line = to_line;
638  this.from_position = from_position;
639  this.to_position = to_position;
640}
641
642
643/**
644 * Get the source text for a SourceSlice
645 * @return {String} Source text for this slice. The last line will include
646 *     the line terminating characters (if any)
647 */
648SourceSlice.prototype.sourceText = function () {
649  return %_CallFunction(this.script.source,
650                        this.from_position,
651                        this.to_position,
652                        StringSubstring);
653};
654
655
656// Returns the offset of the given position within the containing
657// line.
658function GetPositionInLine(message) {
659  var script = %MessageGetScript(message);
660  var start_position = %MessageGetStartPosition(message);
661  var location = script.locationFromPosition(start_position, false);
662  if (location == null) return -1;
663  location.restrict();
664  return start_position - location.start;
665}
666
667
668function GetStackTraceLine(recv, fun, pos, isGlobal) {
669  return FormatSourcePosition(new CallSite(recv, fun, pos));
670}
671
672// ----------------------------------------------------------------------------
673// Error implementation
674
675// Defines accessors for a property that is calculated the first time
676// the property is read.
677function DefineOneShotAccessor(obj, name, fun) {
678  // Note that the accessors consistently operate on 'obj', not 'this'.
679  // Since the object may occur in someone else's prototype chain we
680  // can't rely on 'this' being the same as 'obj'.
681  var hasBeenSet = false;
682  var value;
683  obj.__defineGetter__(name, function () {
684    if (hasBeenSet) {
685      return value;
686    }
687    hasBeenSet = true;
688    value = fun(obj);
689    return value;
690  });
691  obj.__defineSetter__(name, function (v) {
692    hasBeenSet = true;
693    value = v;
694  });
695}
696
697function CallSite(receiver, fun, pos) {
698  this.receiver = receiver;
699  this.fun = fun;
700  this.pos = pos;
701}
702
703CallSite.prototype.getThis = function () {
704  return this.receiver;
705};
706
707CallSite.prototype.getTypeName = function () {
708  var constructor = this.receiver.constructor;
709  if (!constructor)
710    return %_CallFunction(this.receiver, ObjectToString);
711  var constructorName = constructor.name;
712  if (!constructorName)
713    return %_CallFunction(this.receiver, ObjectToString);
714  return constructorName;
715};
716
717CallSite.prototype.isToplevel = function () {
718  if (this.receiver == null)
719    return true;
720  return IS_GLOBAL(this.receiver);
721};
722
723CallSite.prototype.isEval = function () {
724  var script = %FunctionGetScript(this.fun);
725  return script && script.compilation_type == COMPILATION_TYPE_EVAL;
726};
727
728CallSite.prototype.getEvalOrigin = function () {
729  var script = %FunctionGetScript(this.fun);
730  return FormatEvalOrigin(script);
731};
732
733CallSite.prototype.getScriptNameOrSourceURL = function () {
734  var script = %FunctionGetScript(this.fun);
735  return script ? script.nameOrSourceURL() : null;
736};
737
738CallSite.prototype.getFunction = function () {
739  return this.fun;
740};
741
742CallSite.prototype.getFunctionName = function () {
743  // See if the function knows its own name
744  var name = this.fun.name;
745  if (name) {
746    return name;
747  } else {
748    return %FunctionGetInferredName(this.fun);
749  }
750  // Maybe this is an evaluation?
751  var script = %FunctionGetScript(this.fun);
752  if (script && script.compilation_type == COMPILATION_TYPE_EVAL)
753    return "eval";
754  return null;
755};
756
757CallSite.prototype.getMethodName = function () {
758  // See if we can find a unique property on the receiver that holds
759  // this function.
760  var ownName = this.fun.name;
761  if (ownName && this.receiver &&
762      (%_CallFunction(this.receiver, ownName, ObjectLookupGetter) === this.fun ||
763       %_CallFunction(this.receiver, ownName, ObjectLookupSetter) === this.fun ||
764       this.receiver[ownName] === this.fun)) {
765    // To handle DontEnum properties we guess that the method has
766    // the same name as the function.
767    return ownName;
768  }
769  var name = null;
770  for (var prop in this.receiver) {
771    if (this.receiver.__lookupGetter__(prop) === this.fun ||
772        this.receiver.__lookupSetter__(prop) === this.fun ||
773        (!this.receiver.__lookupGetter__(prop) && this.receiver[prop] === this.fun)) {
774      // If we find more than one match bail out to avoid confusion.
775      if (name)
776        return null;
777      name = prop;
778    }
779  }
780  if (name)
781    return name;
782  return null;
783};
784
785CallSite.prototype.getFileName = function () {
786  var script = %FunctionGetScript(this.fun);
787  return script ? script.name : null;
788};
789
790CallSite.prototype.getLineNumber = function () {
791  if (this.pos == -1)
792    return null;
793  var script = %FunctionGetScript(this.fun);
794  var location = null;
795  if (script) {
796    location = script.locationFromPosition(this.pos, true);
797  }
798  return location ? location.line + 1 : null;
799};
800
801CallSite.prototype.getColumnNumber = function () {
802  if (this.pos == -1)
803    return null;
804  var script = %FunctionGetScript(this.fun);
805  var location = null;
806  if (script) {
807    location = script.locationFromPosition(this.pos, true);
808  }
809  return location ? location.column + 1: null;
810};
811
812CallSite.prototype.isNative = function () {
813  var script = %FunctionGetScript(this.fun);
814  return script ? (script.type == TYPE_NATIVE) : false;
815};
816
817CallSite.prototype.getPosition = function () {
818  return this.pos;
819};
820
821CallSite.prototype.isConstructor = function () {
822  var constructor = this.receiver ? this.receiver.constructor : null;
823  if (!constructor)
824    return false;
825  return this.fun === constructor;
826};
827
828function FormatEvalOrigin(script) {
829  var sourceURL = script.nameOrSourceURL();
830  if (sourceURL)
831    return sourceURL;
832
833  var eval_origin = "eval at ";
834  if (script.eval_from_function_name) {
835    eval_origin += script.eval_from_function_name;
836  } else {
837    eval_origin +=  "<anonymous>";
838  }
839
840  var eval_from_script = script.eval_from_script;
841  if (eval_from_script) {
842    if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
843      // eval script originated from another eval.
844      eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
845    } else {
846      // eval script originated from "real" source.
847      if (eval_from_script.name) {
848        eval_origin += " (" + eval_from_script.name;
849        var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true);
850        if (location) {
851          eval_origin += ":" + (location.line + 1);
852          eval_origin += ":" + (location.column + 1);
853        }
854        eval_origin += ")"
855      } else {
856        eval_origin += " (unknown source)";
857      }
858    }
859  }
860
861  return eval_origin;
862};
863
864function FormatSourcePosition(frame) {
865  var fileName;
866  var fileLocation = "";
867  if (frame.isNative()) {
868    fileLocation = "native";
869  } else if (frame.isEval()) {
870    fileName = frame.getScriptNameOrSourceURL();
871    if (!fileName)
872      fileLocation = frame.getEvalOrigin();
873  } else {
874    fileName = frame.getFileName();
875  }
876
877  if (fileName) {
878    fileLocation += fileName;
879    var lineNumber = frame.getLineNumber();
880    if (lineNumber != null) {
881      fileLocation += ":" + lineNumber;
882      var columnNumber = frame.getColumnNumber();
883      if (columnNumber) {
884        fileLocation += ":" + columnNumber;
885      }
886    }
887  }
888
889  if (!fileLocation) {
890    fileLocation = "unknown source";
891  }
892  var line = "";
893  var functionName = frame.getFunction().name;
894  var addPrefix = true;
895  var isConstructor = frame.isConstructor();
896  var isMethodCall = !(frame.isToplevel() || isConstructor);
897  if (isMethodCall) {
898    var methodName = frame.getMethodName();
899    line += frame.getTypeName() + ".";
900    if (functionName) {
901      line += functionName;
902      if (methodName && (methodName != functionName)) {
903        line += " [as " + methodName + "]";
904      }
905    } else {
906      line += methodName || "<anonymous>";
907    }
908  } else if (isConstructor) {
909    line += "new " + (functionName || "<anonymous>");
910  } else if (functionName) {
911    line += functionName;
912  } else {
913    line += fileLocation;
914    addPrefix = false;
915  }
916  if (addPrefix) {
917    line += " (" + fileLocation + ")";
918  }
919  return line;
920}
921
922function FormatStackTrace(error, frames) {
923  var lines = [];
924  try {
925    lines.push(error.toString());
926  } catch (e) {
927    try {
928      lines.push("<error: " + e + ">");
929    } catch (ee) {
930      lines.push("<error>");
931    }
932  }
933  for (var i = 0; i < frames.length; i++) {
934    var frame = frames[i];
935    var line;
936    try {
937      line = FormatSourcePosition(frame);
938    } catch (e) {
939      try {
940        line = "<error: " + e + ">";
941      } catch (ee) {
942        // Any code that reaches this point is seriously nasty!
943        line = "<error>";
944      }
945    }
946    lines.push("    at " + line);
947  }
948  return lines.join("\n");
949}
950
951function FormatRawStackTrace(error, raw_stack) {
952  var frames = [ ];
953  for (var i = 0; i < raw_stack.length; i += 4) {
954    var recv = raw_stack[i];
955    var fun = raw_stack[i + 1];
956    var code = raw_stack[i + 2];
957    var pc = raw_stack[i + 3];
958    var pos = %FunctionGetPositionForOffset(code, pc);
959    frames.push(new CallSite(recv, fun, pos));
960  }
961  if (IS_FUNCTION($Error.prepareStackTrace)) {
962    return $Error.prepareStackTrace(error, frames);
963  } else {
964    return FormatStackTrace(error, frames);
965  }
966}
967
968function DefineError(f) {
969  // Store the error function in both the global object
970  // and the runtime object. The function is fetched
971  // from the runtime object when throwing errors from
972  // within the runtime system to avoid strange side
973  // effects when overwriting the error functions from
974  // user code.
975  var name = f.name;
976  %SetProperty(global, name, f, DONT_ENUM);
977  this['$' + name] = f;
978  // Configure the error function.
979  if (name == 'Error') {
980    // The prototype of the Error object must itself be an error.
981    // However, it can't be an instance of the Error object because
982    // it hasn't been properly configured yet.  Instead we create a
983    // special not-a-true-error-but-close-enough object.
984    function ErrorPrototype() {}
985    %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
986    %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
987    %FunctionSetPrototype(f, new ErrorPrototype());
988  } else {
989    %FunctionSetPrototype(f, new $Error());
990  }
991  %FunctionSetInstanceClassName(f, 'Error');
992  %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
993  // The name property on the prototype of error objects is not
994  // specified as being read-one and dont-delete. However, allowing
995  // overwriting allows leaks of error objects between script blocks
996  // in the same context in a browser setting. Therefore we fix the
997  // name.
998  %SetProperty(f.prototype, "name", name, READ_ONLY | DONT_DELETE);
999  %SetCode(f, function(m) {
1000    if (%_IsConstructCall()) {
1001      // Define all the expected properties directly on the error
1002      // object. This avoids going through getters and setters defined
1003      // on prototype objects.
1004      %IgnoreAttributesAndSetProperty(this, 'stack', void 0);
1005      %IgnoreAttributesAndSetProperty(this, 'arguments', void 0);
1006      %IgnoreAttributesAndSetProperty(this, 'type', void 0);
1007      if (m === kAddMessageAccessorsMarker) {
1008        // DefineOneShotAccessor always inserts a message property and
1009        // ignores setters.
1010        DefineOneShotAccessor(this, 'message', function (obj) {
1011            return FormatMessage(%NewMessageObject(obj.type, obj.arguments));
1012        });
1013      } else if (!IS_UNDEFINED(m)) {
1014        %IgnoreAttributesAndSetProperty(this, 'message', ToString(m));
1015      }
1016      captureStackTrace(this, f);
1017    } else {
1018      return new f(m);
1019    }
1020  });
1021}
1022
1023function captureStackTrace(obj, cons_opt) {
1024  var stackTraceLimit = $Error.stackTraceLimit;
1025  if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
1026  if (stackTraceLimit < 0 || stackTraceLimit > 10000)
1027    stackTraceLimit = 10000;
1028  var raw_stack = %CollectStackTrace(cons_opt
1029                                     ? cons_opt
1030                                     : captureStackTrace, stackTraceLimit);
1031  DefineOneShotAccessor(obj, 'stack', function (obj) {
1032    return FormatRawStackTrace(obj, raw_stack);
1033  });
1034};
1035
1036$Math.__proto__ = global.Object.prototype;
1037
1038DefineError(function Error() { });
1039DefineError(function TypeError() { });
1040DefineError(function RangeError() { });
1041DefineError(function SyntaxError() { });
1042DefineError(function ReferenceError() { });
1043DefineError(function EvalError() { });
1044DefineError(function URIError() { });
1045
1046$Error.captureStackTrace = captureStackTrace;
1047
1048// Setup extra properties of the Error.prototype object.
1049$Error.prototype.message = '';
1050
1051// Global list of error objects visited during errorToString. This is
1052// used to detect cycles in error toString formatting.
1053var visited_errors = new $Array();
1054var cyclic_error_marker = new $Object();
1055
1056function errorToStringDetectCycle() {
1057  if (!%PushIfAbsent(visited_errors, this)) throw cyclic_error_marker;
1058  try {
1059    var type = this.type;
1060    if (type && !%_CallFunction(this, "message", ObjectHasOwnProperty)) {
1061      var formatted = FormatMessage(%NewMessageObject(type, this.arguments));
1062      return this.name + ": " + formatted;
1063    }
1064    var message = %_CallFunction(this, "message", ObjectHasOwnProperty)
1065        ? (": " + this.message)
1066        : "";
1067    return this.name + message;
1068  } finally {
1069    visited_errors.length = visited_errors.length - 1;
1070  }
1071}
1072
1073function errorToString() {
1074  // This helper function is needed because access to properties on
1075  // the builtins object do not work inside of a catch clause.
1076  function isCyclicErrorMarker(o) { return o === cyclic_error_marker; }
1077
1078  try {
1079    return %_CallFunction(this, errorToStringDetectCycle);
1080  } catch(e) {
1081    // If this error message was encountered already return the empty
1082    // string for it instead of recursively formatting it.
1083    if (isCyclicErrorMarker(e)) return '';
1084    else throw e;
1085  }
1086}
1087
1088
1089InstallFunctions($Error.prototype, DONT_ENUM, ['toString', errorToString]);
1090
1091// Boilerplate for exceptions for stack overflows. Used from
1092// Isolate::StackOverflow().
1093const kStackOverflowBoilerplate = MakeRangeError('stack_overflow', []);
1094