1// Copyright 2012 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
30var kMessages = {
31  // Error
32  cyclic_proto:                  ["Cyclic __proto__ value"],
33  code_gen_from_strings:         ["%0"],
34  generator_running:             ["Generator is already running"],
35  generator_finished:            ["Generator has already finished"],
36  // TypeError
37  unexpected_token:              ["Unexpected token ", "%0"],
38  unexpected_token_number:       ["Unexpected number"],
39  unexpected_token_string:       ["Unexpected string"],
40  unexpected_token_identifier:   ["Unexpected identifier"],
41  unexpected_reserved:           ["Unexpected reserved word"],
42  unexpected_strict_reserved:    ["Unexpected strict mode reserved word"],
43  unexpected_eos:                ["Unexpected end of input"],
44  malformed_regexp:              ["Invalid regular expression: /", "%0", "/: ", "%1"],
45  unterminated_regexp:           ["Invalid regular expression: missing /"],
46  regexp_flags:                  ["Cannot supply flags when constructing one RegExp from another"],
47  incompatible_method_receiver:  ["Method ", "%0", " called on incompatible receiver ", "%1"],
48  invalid_lhs_in_assignment:     ["Invalid left-hand side in assignment"],
49  invalid_lhs_in_for_in:         ["Invalid left-hand side in for-in"],
50  invalid_lhs_in_postfix_op:     ["Invalid left-hand side expression in postfix operation"],
51  invalid_lhs_in_prefix_op:      ["Invalid left-hand side expression in prefix operation"],
52  multiple_defaults_in_switch:   ["More than one default clause in switch statement"],
53  newline_after_throw:           ["Illegal newline after throw"],
54  redeclaration:                 ["%0", " '", "%1", "' has already been declared"],
55  no_catch_or_finally:           ["Missing catch or finally after try"],
56  unknown_label:                 ["Undefined label '", "%0", "'"],
57  uncaught_exception:            ["Uncaught ", "%0"],
58  stack_trace:                   ["Stack Trace:\n", "%0"],
59  called_non_callable:           ["%0", " is not a function"],
60  undefined_method:              ["Object ", "%1", " has no method '", "%0", "'"],
61  property_not_function:         ["Property '", "%0", "' of object ", "%1", " is not a function"],
62  cannot_convert_to_primitive:   ["Cannot convert object to primitive value"],
63  not_constructor:               ["%0", " is not a constructor"],
64  not_defined:                   ["%0", " is not defined"],
65  non_object_property_load:      ["Cannot read property '", "%0", "' of ", "%1"],
66  non_object_property_store:     ["Cannot set property '", "%0", "' of ", "%1"],
67  non_object_property_call:      ["Cannot call method '", "%0", "' of ", "%1"],
68  with_expression:               ["%0", " has no properties"],
69  illegal_invocation:            ["Illegal invocation"],
70  no_setter_in_callback:         ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
71  apply_non_function:            ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
72  apply_wrong_args:              ["Function.prototype.apply: Arguments list has wrong type"],
73  invalid_in_operator_use:       ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
74  instanceof_function_expected:  ["Expecting a function in instanceof check, but got ", "%0"],
75  instanceof_nonobject_proto:    ["Function has non-object prototype '", "%0", "' in instanceof check"],
76  undefined_or_null_to_object:   ["Cannot convert undefined or null to object"],
77  reduce_no_initial:             ["Reduce of empty array with no initial value"],
78  getter_must_be_callable:       ["Getter must be a function: ", "%0"],
79  setter_must_be_callable:       ["Setter must be a function: ", "%0"],
80  value_and_accessor:            ["Invalid property.  A property cannot both have accessors and be writable or have a value, ", "%0"],
81  proto_object_or_null:          ["Object prototype may only be an Object or null"],
82  property_desc_object:          ["Property description must be an object: ", "%0"],
83  redefine_disallowed:           ["Cannot redefine property: ", "%0"],
84  define_disallowed:             ["Cannot define property:", "%0", ", object is not extensible."],
85  non_extensible_proto:          ["%0", " is not extensible"],
86  handler_non_object:            ["Proxy.", "%0", " called with non-object as handler"],
87  proto_non_object:              ["Proxy.", "%0", " called with non-object as prototype"],
88  trap_function_expected:        ["Proxy.", "%0", " called with non-function for '", "%1", "' trap"],
89  handler_trap_missing:          ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
90  handler_trap_must_be_callable: ["Proxy handler ", "%0", " has non-callable '", "%1", "' trap"],
91  handler_returned_false:        ["Proxy handler ", "%0", " returned false from '", "%1", "' trap"],
92  handler_returned_undefined:    ["Proxy handler ", "%0", " returned undefined from '", "%1", "' trap"],
93  proxy_prop_not_configurable:   ["Proxy handler ", "%0", " returned non-configurable descriptor for property '", "%2", "' from '", "%1", "' trap"],
94  proxy_non_object_prop_names:   ["Trap '", "%1", "' returned non-object ", "%0"],
95  proxy_repeated_prop_name:      ["Trap '", "%1", "' returned repeated property name '", "%2", "'"],
96  invalid_weakmap_key:           ["Invalid value used as weak map key"],
97  invalid_weakset_value:         ["Invalid value used in weak set"],
98  not_date_object:               ["this is not a Date object."],
99  observe_non_object:            ["Object.", "%0", " cannot ", "%0", " non-object"],
100  observe_non_function:          ["Object.", "%0", " cannot deliver to non-function"],
101  observe_callback_frozen:       ["Object.observe cannot deliver to a frozen function object"],
102  observe_invalid_accept:        ["Object.observe accept must be an array of strings."],
103  observe_type_non_string:       ["Invalid changeRecord with non-string 'type' property"],
104  observe_perform_non_string:    ["Invalid non-string changeType"],
105  observe_perform_non_function:  ["Cannot perform non-function"],
106  observe_notify_non_notifier:   ["notify called on non-notifier object"],
107  proto_poison_pill:             ["Generic use of __proto__ accessor not allowed"],
108  not_typed_array:               ["this is not a typed array."],
109  invalid_argument:              ["invalid_argument"],
110  data_view_not_array_buffer:    ["First argument to DataView constructor must be an ArrayBuffer"],
111  constructor_not_function:      ["Constructor ", "%0", " requires 'new'"],
112  not_a_promise:                 ["%0", "is not a promise"],
113  promise_cyclic:                ["Chaining cycle detected for promise", "%0"],
114  array_functions_on_frozen:     ["Cannot modify frozen array elements"],
115  array_functions_change_sealed: ["Cannot add/remove sealed array elements"],
116  // RangeError
117  invalid_array_length:          ["Invalid array length"],
118  invalid_array_buffer_length:   ["Invalid array buffer length"],
119  invalid_typed_array_offset:    ["Start offset is too large:"],
120  invalid_typed_array_length:    ["Invalid typed array length"],
121  invalid_typed_array_alignment: ["%0", "of", "%1", "should be a multiple of", "%3"],
122  typed_array_set_source_too_large:
123                                 ["Source is too large"],
124  typed_array_set_negative_offset:
125                                 ["Start offset is negative"],
126  invalid_data_view_offset:      ["Start offset is outside the bounds of the buffer"],
127  invalid_data_view_length:      ["Invalid data view length"],
128  invalid_data_view_accessor_offset:
129                                 ["Offset is outside the bounds of the DataView"],
130
131  stack_overflow:                ["Maximum call stack size exceeded"],
132  invalid_time_value:            ["Invalid time value"],
133  invalid_count_value:           ["Invalid count value"],
134  // SyntaxError
135  paren_in_arg_string:           ["Function arg string contains parenthesis"],
136  not_isvar:                     ["builtin %IS_VAR: not a variable"],
137  single_function_literal:       ["Single function literal required"],
138  invalid_regexp_flags:          ["Invalid flags supplied to RegExp constructor '", "%0", "'"],
139  invalid_regexp:                ["Invalid RegExp pattern /", "%0", "/"],
140  illegal_break:                 ["Illegal break statement"],
141  illegal_continue:              ["Illegal continue statement"],
142  illegal_return:                ["Illegal return statement"],
143  illegal_let:                   ["Illegal let declaration outside extended mode"],
144  error_loading_debugger:        ["Error loading debugger"],
145  no_input_to_regexp:            ["No input to ", "%0"],
146  invalid_json:                  ["String '", "%0", "' is not valid JSON"],
147  circular_structure:            ["Converting circular structure to JSON"],
148  called_on_non_object:          ["%0", " called on non-object"],
149  called_on_null_or_undefined:   ["%0", " called on null or undefined"],
150  array_indexof_not_defined:     ["Array.getIndexOf: Argument undefined"],
151  object_not_extensible:         ["Can't add property ", "%0", ", object is not extensible"],
152  illegal_access:                ["Illegal access"],
153  invalid_preparser_data:        ["Invalid preparser data for function ", "%0"],
154  strict_mode_with:              ["Strict mode code may not include a with statement"],
155  strict_catch_variable:         ["Catch variable may not be eval or arguments in strict mode"],
156  too_many_arguments:            ["Too many arguments in function call (only 32766 allowed)"],
157  too_many_parameters:           ["Too many parameters in function definition (only 32766 allowed)"],
158  too_many_variables:            ["Too many variables declared (only 131071 allowed)"],
159  strict_param_name:             ["Parameter name eval or arguments is not allowed in strict mode"],
160  strict_param_dupe:             ["Strict mode function may not have duplicate parameter names"],
161  strict_var_name:               ["Variable name may not be eval or arguments in strict mode"],
162  strict_function_name:          ["Function name may not be eval or arguments in strict mode"],
163  strict_octal_literal:          ["Octal literals are not allowed in strict mode."],
164  strict_duplicate_property:     ["Duplicate data property in object literal not allowed in strict mode"],
165  accessor_data_property:        ["Object literal may not have data and accessor property with the same name"],
166  accessor_get_set:              ["Object literal may not have multiple get/set accessors with the same name"],
167  strict_lhs_assignment:         ["Assignment to eval or arguments is not allowed in strict mode"],
168  strict_lhs_postfix:            ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
169  strict_lhs_prefix:             ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
170  strict_reserved_word:          ["Use of future reserved word in strict mode"],
171  strict_delete:                 ["Delete of an unqualified identifier in strict mode."],
172  strict_delete_property:        ["Cannot delete property '", "%0", "' of ", "%1"],
173  strict_const:                  ["Use of const in strict mode."],
174  strict_function:               ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
175  strict_read_only_property:     ["Cannot assign to read only property '", "%0", "' of ", "%1"],
176  strict_cannot_assign:          ["Cannot assign to read only '", "%0", "' in strict mode"],
177  strict_poison_pill:            ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
178  strict_caller:                 ["Illegal access to a strict mode caller function."],
179  unprotected_let:               ["Illegal let declaration in unprotected statement context."],
180  unprotected_const:             ["Illegal const declaration in unprotected statement context."],
181  cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
182  redef_external_array_element:  ["Cannot redefine a property of an object with external array elements"],
183  harmony_const_assign:          ["Assignment to constant variable."],
184  symbol_to_string:              ["Conversion from symbol to string"],
185  invalid_module_path:           ["Module does not export '", "%0", "', or export is not itself a module"],
186  module_type_error:             ["Module '", "%0", "' used improperly"],
187  module_export_undefined:       ["Export '", "%0", "' is not defined in module"]
188};
189
190
191function FormatString(format, args) {
192  var result = "";
193  var arg_num = 0;
194  for (var i = 0; i < format.length; i++) {
195    var str = format[i];
196    if (str.length == 2 && %_StringCharCodeAt(str, 0) == 0x25) {
197      // Two-char string starts with "%".
198      var arg_num = (%_StringCharCodeAt(str, 1) - 0x30) >>> 0;
199      if (arg_num < 4) {
200        // str is one of %0, %1, %2 or %3.
201        try {
202          str = NoSideEffectToString(args[arg_num]);
203          if (str.length > 256) {
204            str = %SubString(str, 0, 239) + "...<omitted>..." +
205                  %SubString(str, str.length - 2, str.length);
206          }
207        } catch (e) {
208          if (%IsJSModule(args[arg_num]))
209            str = "module";
210          else if (IS_SPEC_OBJECT(args[arg_num]))
211            str = "object";
212          else
213            str = "#<error>";
214        }
215      }
216    }
217    result += str;
218  }
219  return result;
220}
221
222
223function NoSideEffectToString(obj) {
224  if (IS_STRING(obj)) return obj;
225  if (IS_NUMBER(obj)) return %_NumberToString(obj);
226  if (IS_BOOLEAN(obj)) return x ? 'true' : 'false';
227  if (IS_UNDEFINED(obj)) return 'undefined';
228  if (IS_NULL(obj)) return 'null';
229  if (IS_FUNCTION(obj)) return  %_CallFunction(obj, FunctionToString);
230  if (IS_OBJECT(obj) && %GetDataProperty(obj, "toString") === ObjectToString) {
231    var constructor = %GetDataProperty(obj, "constructor");
232    if (typeof constructor == "function") {
233      var constructorName = constructor.name;
234      if (IS_STRING(constructorName) && constructorName !== "") {
235        return "#<" + constructorName + ">";
236      }
237    }
238  }
239  if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
240    return %_CallFunction(obj, ErrorToString);
241  }
242  return %_CallFunction(obj, ObjectToString);
243}
244
245// To determine whether we can safely stringify an object using ErrorToString
246// without the risk of side-effects, we need to check whether the object is
247// either an instance of a native error type (via '%_ClassOf'), or has $Error
248// in its prototype chain and hasn't overwritten 'toString' with something
249// strange and unusual.
250function CanBeSafelyTreatedAsAnErrorObject(obj) {
251  switch (%_ClassOf(obj)) {
252    case 'Error':
253    case 'EvalError':
254    case 'RangeError':
255    case 'ReferenceError':
256    case 'SyntaxError':
257    case 'TypeError':
258    case 'URIError':
259      return true;
260  }
261
262  var objToString = %GetDataProperty(obj, "toString");
263  return obj instanceof $Error && objToString === ErrorToString;
264}
265
266
267// When formatting internally created error messages, do not
268// invoke overwritten error toString methods but explicitly use
269// the error to string method. This is to avoid leaking error
270// objects between script tags in a browser setting.
271function ToStringCheckErrorObject(obj) {
272  if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
273    return %_CallFunction(obj, ErrorToString);
274  } else {
275    return ToString(obj);
276  }
277}
278
279
280function ToDetailString(obj) {
281  if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) {
282    var constructor = obj.constructor;
283    if (typeof constructor == "function") {
284      var constructorName = constructor.name;
285      if (IS_STRING(constructorName) && constructorName !== "") {
286        return "#<" + constructorName + ">";
287      }
288    }
289  }
290  return ToStringCheckErrorObject(obj);
291}
292
293
294function MakeGenericError(constructor, type, args) {
295  if (IS_UNDEFINED(args)) args = [];
296  return new constructor(FormatMessage(type, args));
297}
298
299
300/**
301 * Set up the Script function and constructor.
302 */
303%FunctionSetInstanceClassName(Script, 'Script');
304%SetProperty(Script.prototype, 'constructor', Script,
305             DONT_ENUM | DONT_DELETE | READ_ONLY);
306%SetCode(Script, function(x) {
307  // Script objects can only be created by the VM.
308  throw new $Error("Not supported");
309});
310
311
312// Helper functions; called from the runtime system.
313function FormatMessage(type, args) {
314  var format = kMessages[type];
315  if (!format) return "<unknown message " + type + ">";
316  return FormatString(format, args);
317}
318
319
320function GetLineNumber(message) {
321  var start_position = %MessageGetStartPosition(message);
322  if (start_position == -1) return kNoLineNumberInfo;
323  var script = %MessageGetScript(message);
324  var location = script.locationFromPosition(start_position, true);
325  if (location == null) return kNoLineNumberInfo;
326  return location.line + 1;
327}
328
329
330// Returns the source code line containing the given source
331// position, or the empty string if the position is invalid.
332function GetSourceLine(message) {
333  var script = %MessageGetScript(message);
334  var start_position = %MessageGetStartPosition(message);
335  var location = script.locationFromPosition(start_position, true);
336  if (location == null) return "";
337  location.restrict();
338  return location.sourceText();
339}
340
341
342function MakeTypeError(type, args) {
343  return MakeGenericError($TypeError, type, args);
344}
345
346
347function MakeRangeError(type, args) {
348  return MakeGenericError($RangeError, type, args);
349}
350
351
352function MakeSyntaxError(type, args) {
353  return MakeGenericError($SyntaxError, type, args);
354}
355
356
357function MakeReferenceError(type, args) {
358  return MakeGenericError($ReferenceError, type, args);
359}
360
361
362function MakeEvalError(type, args) {
363  return MakeGenericError($EvalError, type, args);
364}
365
366
367function MakeError(type, args) {
368  return MakeGenericError($Error, type, args);
369}
370
371/**
372 * Find a line number given a specific source position.
373 * @param {number} position The source position.
374 * @return {number} 0 if input too small, -1 if input too large,
375       else the line number.
376 */
377function ScriptLineFromPosition(position) {
378  var lower = 0;
379  var upper = this.lineCount() - 1;
380  var line_ends = this.line_ends;
381
382  // We'll never find invalid positions so bail right away.
383  if (position > line_ends[upper]) {
384    return -1;
385  }
386
387  // This means we don't have to safe-guard indexing line_ends[i - 1].
388  if (position <= line_ends[0]) {
389    return 0;
390  }
391
392  // Binary search to find line # from position range.
393  while (upper >= 1) {
394    var i = (lower + upper) >> 1;
395
396    if (position > line_ends[i]) {
397      lower = i + 1;
398    } else if (position <= line_ends[i - 1]) {
399      upper = i - 1;
400    } else {
401      return i;
402    }
403  }
404
405  return -1;
406}
407
408/**
409 * Get information on a specific source position.
410 * @param {number} position The source position
411 * @param {boolean} include_resource_offset Set to true to have the resource
412 *     offset added to the location
413 * @return {SourceLocation}
414 *     If line is negative or not in the source null is returned.
415 */
416function ScriptLocationFromPosition(position,
417                                    include_resource_offset) {
418  var line = this.lineFromPosition(position);
419  if (line == -1) return null;
420
421  // Determine start, end and column.
422  var line_ends = this.line_ends;
423  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
424  var end = line_ends[line];
425  if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') {
426    end--;
427  }
428  var column = position - start;
429
430  // Adjust according to the offset within the resource.
431  if (include_resource_offset) {
432    line += this.line_offset;
433    if (line == this.line_offset) {
434      column += this.column_offset;
435    }
436  }
437
438  return new SourceLocation(this, position, line, column, start, end);
439}
440
441
442/**
443 * Get information on a specific source line and column possibly offset by a
444 * fixed source position. This function is used to find a source position from
445 * a line and column position. The fixed source position offset is typically
446 * used to find a source position in a function based on a line and column in
447 * the source for the function alone. The offset passed will then be the
448 * start position of the source for the function within the full script source.
449 * @param {number} opt_line The line within the source. Default value is 0
450 * @param {number} opt_column The column in within the line. Default value is 0
451 * @param {number} opt_offset_position The offset from the begining of the
452 *     source from where the line and column calculation starts.
453 *     Default value is 0
454 * @return {SourceLocation}
455 *     If line is negative or not in the source null is returned.
456 */
457function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
458  // Default is the first line in the script. Lines in the script is 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  }
464
465  // Default is first column. If on the first line add the offset within the
466  // resource.
467  var column = opt_column || 0;
468  if (line == 0) {
469    column -= this.column_offset;
470  }
471
472  var offset_position = opt_offset_position || 0;
473  if (line < 0 || column < 0 || offset_position < 0) return null;
474  if (line == 0) {
475    return this.locationFromPosition(offset_position + column, false);
476  } else {
477    // Find the line where the offset position is located.
478    var offset_line = this.lineFromPosition(offset_position);
479
480    if (offset_line == -1 || offset_line + line >= this.lineCount()) {
481      return null;
482    }
483
484    return this.locationFromPosition(
485        this.line_ends[offset_line + line - 1] + 1 + column);  // line > 0 here.
486  }
487}
488
489
490/**
491 * Get a slice of source code from the script. The boundaries for the slice is
492 * specified in lines.
493 * @param {number} opt_from_line The first line (zero bound) in the slice.
494 *     Default is 0
495 * @param {number} opt_to_column The last line (zero bound) in the slice (non
496 *     inclusive). Default is the number of lines in the script
497 * @return {SourceSlice} The source slice or null of the parameters where
498 *     invalid
499 */
500function ScriptSourceSlice(opt_from_line, opt_to_line) {
501  var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset
502                                              : opt_from_line;
503  var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount()
504                                          : opt_to_line;
505
506  // Adjust according to the offset within the resource.
507  from_line -= this.line_offset;
508  to_line -= this.line_offset;
509  if (from_line < 0) from_line = 0;
510  if (to_line > this.lineCount()) to_line = this.lineCount();
511
512  // Check parameters.
513  if (from_line >= this.lineCount() ||
514      to_line < 0 ||
515      from_line > to_line) {
516    return null;
517  }
518
519  var line_ends = this.line_ends;
520  var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
521  var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
522
523  // Return a source slice with line numbers re-adjusted to the resource.
524  return new SourceSlice(this,
525                         from_line + this.line_offset,
526                         to_line + this.line_offset,
527                          from_position, to_position);
528}
529
530
531function ScriptSourceLine(opt_line) {
532  // Default is the first line in the script. Lines in the script are relative
533  // to the offset within the resource.
534  var line = 0;
535  if (!IS_UNDEFINED(opt_line)) {
536    line = opt_line - this.line_offset;
537  }
538
539  // Check parameter.
540  if (line < 0 || this.lineCount() <= line) {
541    return null;
542  }
543
544  // Return the source line.
545  var line_ends = this.line_ends;
546  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
547  var end = line_ends[line];
548  return %_CallFunction(this.source, start, end, StringSubstring);
549}
550
551
552/**
553 * Returns the number of source lines.
554 * @return {number}
555 *     Number of source lines.
556 */
557function ScriptLineCount() {
558  // Return number of source lines.
559  return this.line_ends.length;
560}
561
562
563/**
564 * If sourceURL comment is available and script starts at zero returns sourceURL
565 * comment contents. Otherwise, script name is returned. See
566 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
567 * and Source Map Revision 3 proposal for details on using //# sourceURL and
568 * deprecated //@ sourceURL comment to identify scripts that don't have name.
569 *
570 * @return {?string} script name if present, value for //# sourceURL or
571 * deprecated //@ sourceURL comment otherwise.
572 */
573function ScriptNameOrSourceURL() {
574  if (this.line_offset > 0 || this.column_offset > 0) {
575    return this.name;
576  }
577
578  // The result is cached as on long scripts it takes noticable time to search
579  // for the sourceURL.
580  if (this.hasCachedNameOrSourceURL) {
581    return this.cachedNameOrSourceURL;
582  }
583  this.hasCachedNameOrSourceURL = true;
584
585  // TODO(608): the spaces in a regexp below had to be escaped as \040
586  // because this file is being processed by js2c whose handling of spaces
587  // in regexps is broken. Also, ['"] are excluded from allowed URLs to
588  // avoid matches against sources that invoke evals with sourceURL.
589  // A better solution would be to detect these special comments in
590  // the scanner/parser.
591  var source = ToString(this.source);
592  var sourceUrlPos = %StringIndexOf(source, "sourceURL=", 0);
593  this.cachedNameOrSourceURL = this.name;
594  if (sourceUrlPos > 4) {
595    var sourceUrlPattern =
596        /\/\/[#@][\040\t]sourceURL=[\040\t]*([^\s\'\"]*)[\040\t]*$/gm;
597    // Don't reuse lastMatchInfo here, so we create a new array with room
598    // for four captures (array with length one longer than the index
599    // of the fourth capture, where the numbering is zero-based).
600    var matchInfo = new InternalArray(CAPTURE(3) + 1);
601    var match =
602        %_RegExpExec(sourceUrlPattern, source, sourceUrlPos - 4, matchInfo);
603    if (match) {
604      this.cachedNameOrSourceURL =
605          %_SubString(source, matchInfo[CAPTURE(2)], matchInfo[CAPTURE(3)]);
606    }
607  }
608  return this.cachedNameOrSourceURL;
609}
610
611
612SetUpLockedPrototype(Script,
613  $Array("source", "name", "line_ends", "line_offset", "column_offset",
614         "cachedNameOrSourceURL", "hasCachedNameOrSourceURL" ),
615  $Array(
616    "lineFromPosition", ScriptLineFromPosition,
617    "locationFromPosition", ScriptLocationFromPosition,
618    "locationFromLine", ScriptLocationFromLine,
619    "sourceSlice", ScriptSourceSlice,
620    "sourceLine", ScriptSourceLine,
621    "lineCount", ScriptLineCount,
622    "nameOrSourceURL", ScriptNameOrSourceURL
623  )
624);
625
626
627/**
628 * Class for source location. A source location is a position within some
629 * source with the following properties:
630 *   script   : script object for the source
631 *   line     : source line number
632 *   column   : source column within the line
633 *   position : position within the source
634 *   start    : position of start of source context (inclusive)
635 *   end      : position of end of source context (not inclusive)
636 * Source text for the source context is the character interval
637 * [start, end[. In most cases end will point to a newline character.
638 * It might point just past the final position of the source if the last
639 * source line does not end with a newline character.
640 * @param {Script} script The Script object for which this is a location
641 * @param {number} position Source position for the location
642 * @param {number} line The line number for the location
643 * @param {number} column The column within the line for the location
644 * @param {number} start Source position for start of source context
645 * @param {number} end Source position for end of source context
646 * @constructor
647 */
648function SourceLocation(script, position, line, column, start, end) {
649  this.script = script;
650  this.position = position;
651  this.line = line;
652  this.column = column;
653  this.start = start;
654  this.end = end;
655}
656
657var kLineLengthLimit = 78;
658
659/**
660 * Restrict source location start and end positions to make the source slice
661 * no more that a certain number of characters wide.
662 * @param {number} opt_limit The with limit of the source text with a default
663 *     of 78
664 * @param {number} opt_before The number of characters to prefer before the
665 *     position with a default value of 10 less that the limit
666 */
667function SourceLocationRestrict(opt_limit, opt_before) {
668  // Find the actual limit to use.
669  var limit;
670  var before;
671  if (!IS_UNDEFINED(opt_limit)) {
672    limit = opt_limit;
673  } else {
674    limit = kLineLengthLimit;
675  }
676  if (!IS_UNDEFINED(opt_before)) {
677    before = opt_before;
678  } else {
679    // If no before is specified center for small limits and perfer more source
680    // before the the position that after for longer limits.
681    if (limit <= 20) {
682      before = $floor(limit / 2);
683    } else {
684      before = limit - 10;
685    }
686  }
687  if (before >= limit) {
688    before = limit - 1;
689  }
690
691  // If the [start, end[ interval is too big we restrict
692  // it in one or both ends. We make sure to always produce
693  // restricted intervals of maximum allowed size.
694  if (this.end - this.start > limit) {
695    var start_limit = this.position - before;
696    var end_limit = this.position + limit - before;
697    if (this.start < start_limit && end_limit < this.end) {
698      this.start = start_limit;
699      this.end = end_limit;
700    } else if (this.start < start_limit) {
701      this.start = this.end - limit;
702    } else {
703      this.end = this.start + limit;
704    }
705  }
706}
707
708
709/**
710 * Get the source text for a SourceLocation
711 * @return {String}
712 *     Source text for this location.
713 */
714function SourceLocationSourceText() {
715  return %_CallFunction(this.script.source,
716                        this.start,
717                        this.end,
718                        StringSubstring);
719}
720
721
722SetUpLockedPrototype(SourceLocation,
723  $Array("script", "position", "line", "column", "start", "end"),
724  $Array(
725    "restrict", SourceLocationRestrict,
726    "sourceText", SourceLocationSourceText
727 )
728);
729
730
731/**
732 * Class for a source slice. A source slice is a part of a script source with
733 * the following properties:
734 *   script        : script object for the source
735 *   from_line     : line number for the first line in the slice
736 *   to_line       : source line number for the last line in the slice
737 *   from_position : position of the first character in the slice
738 *   to_position   : position of the last character in the slice
739 * The to_line and to_position are not included in the slice, that is the lines
740 * in the slice are [from_line, to_line[. Likewise the characters in the slice
741 * are [from_position, to_position[.
742 * @param {Script} script The Script object for the source slice
743 * @param {number} from_line
744 * @param {number} to_line
745 * @param {number} from_position
746 * @param {number} to_position
747 * @constructor
748 */
749function SourceSlice(script, from_line, to_line, from_position, to_position) {
750  this.script = script;
751  this.from_line = from_line;
752  this.to_line = to_line;
753  this.from_position = from_position;
754  this.to_position = to_position;
755}
756
757/**
758 * Get the source text for a SourceSlice
759 * @return {String} Source text for this slice. The last line will include
760 *     the line terminating characters (if any)
761 */
762function SourceSliceSourceText() {
763  return %_CallFunction(this.script.source,
764                        this.from_position,
765                        this.to_position,
766                        StringSubstring);
767}
768
769SetUpLockedPrototype(SourceSlice,
770  $Array("script", "from_line", "to_line", "from_position", "to_position"),
771  $Array("sourceText", SourceSliceSourceText)
772);
773
774
775// Returns the offset of the given position within the containing
776// line.
777function GetPositionInLine(message) {
778  var script = %MessageGetScript(message);
779  var start_position = %MessageGetStartPosition(message);
780  var location = script.locationFromPosition(start_position, false);
781  if (location == null) return -1;
782  location.restrict();
783  return start_position - location.start;
784}
785
786
787function GetStackTraceLine(recv, fun, pos, isGlobal) {
788  return new CallSite(recv, fun, pos, false).toString();
789}
790
791// ----------------------------------------------------------------------------
792// Error implementation
793
794//TODO(rossberg)
795var CallSiteReceiverKey = NEW_PRIVATE("receiver");
796var CallSiteFunctionKey = NEW_PRIVATE("function");
797var CallSitePositionKey = NEW_PRIVATE("position");
798var CallSiteStrictModeKey = NEW_PRIVATE("strict mode");
799
800function CallSite(receiver, fun, pos, strict_mode) {
801  SET_PRIVATE(this, CallSiteReceiverKey, receiver);
802  SET_PRIVATE(this, CallSiteFunctionKey, fun);
803  SET_PRIVATE(this, CallSitePositionKey, pos);
804  SET_PRIVATE(this, CallSiteStrictModeKey, strict_mode);
805}
806
807function CallSiteGetThis() {
808  return GET_PRIVATE(this, CallSiteStrictModeKey)
809      ? UNDEFINED : GET_PRIVATE(this, CallSiteReceiverKey);
810}
811
812function CallSiteGetTypeName() {
813  return GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), false);
814}
815
816function CallSiteIsToplevel() {
817  if (GET_PRIVATE(this, CallSiteReceiverKey) == null) {
818    return true;
819  }
820  return IS_GLOBAL(GET_PRIVATE(this, CallSiteReceiverKey));
821}
822
823function CallSiteIsEval() {
824  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
825  return script && script.compilation_type == COMPILATION_TYPE_EVAL;
826}
827
828function CallSiteGetEvalOrigin() {
829  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
830  return FormatEvalOrigin(script);
831}
832
833function CallSiteGetScriptNameOrSourceURL() {
834  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
835  return script ? script.nameOrSourceURL() : null;
836}
837
838function CallSiteGetFunction() {
839  return GET_PRIVATE(this, CallSiteStrictModeKey)
840      ? UNDEFINED : GET_PRIVATE(this, CallSiteFunctionKey);
841}
842
843function CallSiteGetFunctionName() {
844  // See if the function knows its own name
845  var name = GET_PRIVATE(this, CallSiteFunctionKey).name;
846  if (name) {
847    return name;
848  }
849  name = %FunctionGetInferredName(GET_PRIVATE(this, CallSiteFunctionKey));
850  if (name) {
851    return name;
852  }
853  // Maybe this is an evaluation?
854  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
855  if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
856    return "eval";
857  }
858  return null;
859}
860
861function CallSiteGetMethodName() {
862  // See if we can find a unique property on the receiver that holds
863  // this function.
864  var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
865  var fun = GET_PRIVATE(this, CallSiteFunctionKey);
866  var ownName = fun.name;
867  if (ownName && receiver &&
868      (%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun ||
869       %_CallFunction(receiver, ownName, ObjectLookupSetter) === fun ||
870       (IS_OBJECT(receiver) && %GetDataProperty(receiver, ownName) === fun))) {
871    // To handle DontEnum properties we guess that the method has
872    // the same name as the function.
873    return ownName;
874  }
875  var name = null;
876  for (var prop in receiver) {
877    if (%_CallFunction(receiver, prop, ObjectLookupGetter) === fun ||
878        %_CallFunction(receiver, prop, ObjectLookupSetter) === fun ||
879        (IS_OBJECT(receiver) && %GetDataProperty(receiver, prop) === fun)) {
880      // If we find more than one match bail out to avoid confusion.
881      if (name) {
882        return null;
883      }
884      name = prop;
885    }
886  }
887  if (name) {
888    return name;
889  }
890  return null;
891}
892
893function CallSiteGetFileName() {
894  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
895  return script ? script.name : null;
896}
897
898function CallSiteGetLineNumber() {
899  if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
900    return null;
901  }
902  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
903  var location = null;
904  if (script) {
905    location = script.locationFromPosition(
906        GET_PRIVATE(this, CallSitePositionKey), true);
907  }
908  return location ? location.line + 1 : null;
909}
910
911function CallSiteGetColumnNumber() {
912  if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
913    return null;
914  }
915  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
916  var location = null;
917  if (script) {
918    location = script.locationFromPosition(
919      GET_PRIVATE(this, CallSitePositionKey), true);
920  }
921  return location ? location.column + 1: null;
922}
923
924function CallSiteIsNative() {
925  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
926  return script ? (script.type == TYPE_NATIVE) : false;
927}
928
929function CallSiteGetPosition() {
930  return GET_PRIVATE(this, CallSitePositionKey);
931}
932
933function CallSiteIsConstructor() {
934  var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
935  var constructor = (receiver != null && IS_OBJECT(receiver))
936                        ? %GetDataProperty(receiver, "constructor") : null;
937  if (!constructor) return false;
938  return GET_PRIVATE(this, CallSiteFunctionKey) === constructor;
939}
940
941function CallSiteToString() {
942  var fileName;
943  var fileLocation = "";
944  if (this.isNative()) {
945    fileLocation = "native";
946  } else {
947    if (this.isEval()) {
948      fileName = this.getScriptNameOrSourceURL();
949      if (!fileName) {
950        fileLocation = this.getEvalOrigin();
951        fileLocation += ", ";  // Expecting source position to follow.
952      }
953    } else {
954      fileName = this.getFileName();
955    }
956
957    if (fileName) {
958      fileLocation += fileName;
959    } else {
960      // Source code does not originate from a file and is not native, but we
961      // can still get the source position inside the source string, e.g. in
962      // an eval string.
963      fileLocation += "<anonymous>";
964    }
965    var lineNumber = this.getLineNumber();
966    if (lineNumber != null) {
967      fileLocation += ":" + lineNumber;
968      var columnNumber = this.getColumnNumber();
969      if (columnNumber) {
970        fileLocation += ":" + columnNumber;
971      }
972    }
973  }
974
975  var line = "";
976  var functionName = this.getFunctionName();
977  var addSuffix = true;
978  var isConstructor = this.isConstructor();
979  var isMethodCall = !(this.isToplevel() || isConstructor);
980  if (isMethodCall) {
981    var typeName = GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), true);
982    var methodName = this.getMethodName();
983    if (functionName) {
984      if (typeName &&
985          %_CallFunction(functionName, typeName, StringIndexOf) != 0) {
986        line += typeName + ".";
987      }
988      line += functionName;
989      if (methodName &&
990          (%_CallFunction(functionName, "." + methodName, StringIndexOf) !=
991           functionName.length - methodName.length - 1)) {
992        line += " [as " + methodName + "]";
993      }
994    } else {
995      line += typeName + "." + (methodName || "<anonymous>");
996    }
997  } else if (isConstructor) {
998    line += "new " + (functionName || "<anonymous>");
999  } else if (functionName) {
1000    line += functionName;
1001  } else {
1002    line += fileLocation;
1003    addSuffix = false;
1004  }
1005  if (addSuffix) {
1006    line += " (" + fileLocation + ")";
1007  }
1008  return line;
1009}
1010
1011SetUpLockedPrototype(CallSite, $Array("receiver", "fun", "pos"), $Array(
1012  "getThis", CallSiteGetThis,
1013  "getTypeName", CallSiteGetTypeName,
1014  "isToplevel", CallSiteIsToplevel,
1015  "isEval", CallSiteIsEval,
1016  "getEvalOrigin", CallSiteGetEvalOrigin,
1017  "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL,
1018  "getFunction", CallSiteGetFunction,
1019  "getFunctionName", CallSiteGetFunctionName,
1020  "getMethodName", CallSiteGetMethodName,
1021  "getFileName", CallSiteGetFileName,
1022  "getLineNumber", CallSiteGetLineNumber,
1023  "getColumnNumber", CallSiteGetColumnNumber,
1024  "isNative", CallSiteIsNative,
1025  "getPosition", CallSiteGetPosition,
1026  "isConstructor", CallSiteIsConstructor,
1027  "toString", CallSiteToString
1028));
1029
1030
1031function FormatEvalOrigin(script) {
1032  var sourceURL = script.nameOrSourceURL();
1033  if (sourceURL) {
1034    return sourceURL;
1035  }
1036
1037  var eval_origin = "eval at ";
1038  if (script.eval_from_function_name) {
1039    eval_origin += script.eval_from_function_name;
1040  } else {
1041    eval_origin +=  "<anonymous>";
1042  }
1043
1044  var eval_from_script = script.eval_from_script;
1045  if (eval_from_script) {
1046    if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
1047      // eval script originated from another eval.
1048      eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
1049    } else {
1050      // eval script originated from "real" source.
1051      if (eval_from_script.name) {
1052        eval_origin += " (" + eval_from_script.name;
1053        var location = eval_from_script.locationFromPosition(
1054            script.eval_from_script_position, true);
1055        if (location) {
1056          eval_origin += ":" + (location.line + 1);
1057          eval_origin += ":" + (location.column + 1);
1058        }
1059        eval_origin += ")";
1060      } else {
1061        eval_origin += " (unknown source)";
1062      }
1063    }
1064  }
1065
1066  return eval_origin;
1067}
1068
1069
1070function FormatErrorString(error) {
1071  try {
1072    return %_CallFunction(error, ErrorToString);
1073  } catch (e) {
1074    try {
1075      return "<error: " + e + ">";
1076    } catch (ee) {
1077      return "<error>";
1078    }
1079  }
1080}
1081
1082
1083function GetStackFrames(raw_stack) {
1084  var frames = new InternalArray();
1085  var non_strict_frames = raw_stack[0];
1086  for (var i = 1; i < raw_stack.length; i += 4) {
1087    var recv = raw_stack[i];
1088    var fun = raw_stack[i + 1];
1089    var code = raw_stack[i + 2];
1090    var pc = raw_stack[i + 3];
1091    var pos = %FunctionGetPositionForOffset(code, pc);
1092    non_strict_frames--;
1093    frames.push(new CallSite(recv, fun, pos, (non_strict_frames < 0)));
1094  }
1095  return frames;
1096}
1097
1098
1099// Flag to prevent recursive call of Error.prepareStackTrace.
1100var formatting_custom_stack_trace = false;
1101
1102
1103function FormatStackTrace(obj, error_string, frames) {
1104  if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
1105    var array = [];
1106    %MoveArrayContents(frames, array);
1107    formatting_custom_stack_trace = true;
1108    var stack_trace = UNDEFINED;
1109    try {
1110      stack_trace = $Error.prepareStackTrace(obj, array);
1111    } catch (e) {
1112      throw e;  // The custom formatting function threw.  Rethrow.
1113    } finally {
1114      formatting_custom_stack_trace = false;
1115    }
1116    return stack_trace;
1117  }
1118
1119  var lines = new InternalArray();
1120  lines.push(error_string);
1121  for (var i = 0; i < frames.length; i++) {
1122    var frame = frames[i];
1123    var line;
1124    try {
1125      line = frame.toString();
1126    } catch (e) {
1127      try {
1128        line = "<error: " + e + ">";
1129      } catch (ee) {
1130        // Any code that reaches this point is seriously nasty!
1131        line = "<error>";
1132      }
1133    }
1134    lines.push("    at " + line);
1135  }
1136  return %_CallFunction(lines, "\n", ArrayJoin);
1137}
1138
1139
1140function GetTypeName(receiver, requireConstructor) {
1141  var constructor = receiver.constructor;
1142  if (!constructor) {
1143    return requireConstructor ? null :
1144        %_CallFunction(receiver, ObjectToString);
1145  }
1146  var constructorName = constructor.name;
1147  if (!constructorName) {
1148    return requireConstructor ? null :
1149        %_CallFunction(receiver, ObjectToString);
1150  }
1151  return constructorName;
1152}
1153
1154
1155function captureStackTrace(obj, cons_opt) {
1156  var stackTraceLimit = $Error.stackTraceLimit;
1157  if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
1158  if (stackTraceLimit < 0 || stackTraceLimit > 10000) {
1159    stackTraceLimit = 10000;
1160  }
1161  var stack = %CollectStackTrace(obj,
1162                                 cons_opt ? cons_opt : captureStackTrace,
1163                                 stackTraceLimit);
1164
1165  var error_string = FormatErrorString(obj);
1166  // The holder of this getter ('obj') may not be the receiver ('this').
1167  // When this getter is called the first time, we use the context values to
1168  // format a stack trace string and turn this accessor pair into a data
1169  // property (on the holder).
1170  var getter = function() {
1171    // Stack is still a raw array awaiting to be formatted.
1172    var result = FormatStackTrace(obj, error_string, GetStackFrames(stack));
1173    // Turn this accessor into a data property.
1174    %DefineOrRedefineDataProperty(obj, 'stack', result, NONE);
1175    // Release context values.
1176    stack = error_string = UNDEFINED;
1177    return result;
1178  };
1179
1180  // Set the 'stack' property on the receiver.  If the receiver is the same as
1181  // holder of this setter, the accessor pair is turned into a data property.
1182  var setter = function(v) {
1183    // Set data property on the receiver (not necessarily holder).
1184    %DefineOrRedefineDataProperty(this, 'stack', v, NONE);
1185    if (this === obj) {
1186      // Release context values if holder is the same as the receiver.
1187      stack = error_string = UNDEFINED;
1188    }
1189  };
1190
1191  %DefineOrRedefineAccessorProperty(obj, 'stack', getter, setter, DONT_ENUM);
1192}
1193
1194
1195function SetUpError() {
1196  // Define special error type constructors.
1197
1198  var DefineError = function(f) {
1199    // Store the error function in both the global object
1200    // and the runtime object. The function is fetched
1201    // from the runtime object when throwing errors from
1202    // within the runtime system to avoid strange side
1203    // effects when overwriting the error functions from
1204    // user code.
1205    var name = f.name;
1206    %SetProperty(global, name, f, DONT_ENUM);
1207    %SetProperty(builtins, '$' + name, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
1208    // Configure the error function.
1209    if (name == 'Error') {
1210      // The prototype of the Error object must itself be an error.
1211      // However, it can't be an instance of the Error object because
1212      // it hasn't been properly configured yet.  Instead we create a
1213      // special not-a-true-error-but-close-enough object.
1214      var ErrorPrototype = function() {};
1215      %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
1216      %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
1217      %FunctionSetPrototype(f, new ErrorPrototype());
1218    } else {
1219      %FunctionSetPrototype(f, new $Error());
1220    }
1221    %FunctionSetInstanceClassName(f, 'Error');
1222    %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
1223    %SetProperty(f.prototype, "name", name, DONT_ENUM);
1224    %SetCode(f, function(m) {
1225      if (%_IsConstructCall()) {
1226        // Define all the expected properties directly on the error
1227        // object. This avoids going through getters and setters defined
1228        // on prototype objects.
1229        %IgnoreAttributesAndSetProperty(this, 'stack', UNDEFINED, DONT_ENUM);
1230        if (!IS_UNDEFINED(m)) {
1231          %IgnoreAttributesAndSetProperty(
1232            this, 'message', ToString(m), DONT_ENUM);
1233        }
1234        captureStackTrace(this, f);
1235      } else {
1236        return new f(m);
1237      }
1238    });
1239    %SetNativeFlag(f);
1240  };
1241
1242  DefineError(function Error() { });
1243  DefineError(function TypeError() { });
1244  DefineError(function RangeError() { });
1245  DefineError(function SyntaxError() { });
1246  DefineError(function ReferenceError() { });
1247  DefineError(function EvalError() { });
1248  DefineError(function URIError() { });
1249}
1250
1251SetUpError();
1252
1253$Error.captureStackTrace = captureStackTrace;
1254
1255%SetProperty($Error.prototype, 'message', '', DONT_ENUM);
1256
1257// Global list of error objects visited during ErrorToString. This is
1258// used to detect cycles in error toString formatting.
1259var visited_errors = new InternalArray();
1260var cyclic_error_marker = new $Object();
1261
1262function GetPropertyWithoutInvokingMonkeyGetters(error, name) {
1263  var current = error;
1264  // Climb the prototype chain until we find the holder.
1265  while (current && !%HasLocalProperty(current, name)) {
1266    current = %GetPrototype(current);
1267  }
1268  if (IS_NULL(current)) return UNDEFINED;
1269  if (!IS_OBJECT(current)) return error[name];
1270  // If the property is an accessor on one of the predefined errors that can be
1271  // generated statically by the compiler, don't touch it. This is to address
1272  // http://code.google.com/p/chromium/issues/detail?id=69187
1273  var desc = %GetOwnProperty(current, name);
1274  if (desc && desc[IS_ACCESSOR_INDEX]) {
1275    var isName = name === "name";
1276    if (current === $ReferenceError.prototype)
1277      return isName ? "ReferenceError" : UNDEFINED;
1278    if (current === $SyntaxError.prototype)
1279      return isName ? "SyntaxError" : UNDEFINED;
1280    if (current === $TypeError.prototype)
1281      return isName ? "TypeError" : UNDEFINED;
1282  }
1283  // Otherwise, read normally.
1284  return error[name];
1285}
1286
1287function ErrorToStringDetectCycle(error) {
1288  if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker;
1289  try {
1290    var name = GetPropertyWithoutInvokingMonkeyGetters(error, "name");
1291    name = IS_UNDEFINED(name) ? "Error" : TO_STRING_INLINE(name);
1292    var message = GetPropertyWithoutInvokingMonkeyGetters(error, "message");
1293    message = IS_UNDEFINED(message) ? "" : TO_STRING_INLINE(message);
1294    if (name === "") return message;
1295    if (message === "") return name;
1296    return name + ": " + message;
1297  } finally {
1298    visited_errors.length = visited_errors.length - 1;
1299  }
1300}
1301
1302function ErrorToString() {
1303  if (!IS_SPEC_OBJECT(this)) {
1304    throw MakeTypeError("called_on_non_object", ["Error.prototype.toString"]);
1305  }
1306
1307  try {
1308    return ErrorToStringDetectCycle(this);
1309  } catch(e) {
1310    // If this error message was encountered already return the empty
1311    // string for it instead of recursively formatting it.
1312    if (e === cyclic_error_marker) {
1313      return '';
1314    }
1315    throw e;
1316  }
1317}
1318
1319
1320InstallFunctions($Error.prototype, DONT_ENUM, ['toString', ErrorToString]);
1321
1322// Boilerplate for exceptions for stack overflows. Used from
1323// Isolate::StackOverflow().
1324function SetUpStackOverflowBoilerplate() {
1325  var boilerplate = MakeRangeError('stack_overflow', []);
1326
1327  var error_string = boilerplate.name + ": " + boilerplate.message;
1328
1329  // The raw stack trace is stored as a hidden property on the holder of this
1330  // getter, which may not be the same as the receiver.  Find the holder to
1331  // retrieve the raw stack trace and then turn this accessor pair into a
1332  // data property.
1333  var getter = function() {
1334    var holder = this;
1335    while (!IS_ERROR(holder)) {
1336      holder = %GetPrototype(holder);
1337      if (IS_NULL(holder)) return MakeSyntaxError('illegal_access', []);
1338    }
1339    var stack = %GetAndClearOverflowedStackTrace(holder);
1340    // We may not have captured any stack trace.
1341    if (IS_UNDEFINED(stack)) return stack;
1342
1343    var result = FormatStackTrace(holder, error_string, GetStackFrames(stack));
1344    // Replace this accessor with a data property.
1345    %DefineOrRedefineDataProperty(holder, 'stack', result, NONE);
1346    return result;
1347  };
1348
1349  // Set the 'stack' property on the receiver.  If the receiver is the same as
1350  // holder of this setter, the accessor pair is turned into a data property.
1351  var setter = function(v) {
1352    %DefineOrRedefineDataProperty(this, 'stack', v, NONE);
1353    // Tentatively clear the hidden property. If the receiver is the same as
1354    // holder, we release the raw stack trace this way.
1355    %GetAndClearOverflowedStackTrace(this);
1356  };
1357
1358  %DefineOrRedefineAccessorProperty(
1359      boilerplate, 'stack', getter, setter, DONT_ENUM);
1360
1361  return boilerplate;
1362}
1363
1364var kStackOverflowBoilerplate = SetUpStackOverflowBoilerplate();
1365