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