1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// -------------------------------------------------------------------
6
7(function(global, utils) {
8
9%CheckIsBootstrapping();
10
11// -------------------------------------------------------------------
12// Imports
13
14var ArrayJoin;
15var Bool16x8ToString;
16var Bool32x4ToString;
17var Bool8x16ToString;
18var callSiteReceiverSymbol =
19    utils.ImportNow("call_site_receiver_symbol");
20var callSiteFunctionSymbol =
21    utils.ImportNow("call_site_function_symbol");
22var callSitePositionSymbol =
23    utils.ImportNow("call_site_position_symbol");
24var callSiteStrictSymbol =
25    utils.ImportNow("call_site_strict_symbol");
26var FLAG_harmony_tostring;
27var Float32x4ToString;
28var formattedStackTraceSymbol =
29    utils.ImportNow("formatted_stack_trace_symbol");
30var GlobalObject = global.Object;
31var Int16x8ToString;
32var Int32x4ToString;
33var Int8x16ToString;
34var InternalArray = utils.InternalArray;
35var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
36var ObjectDefineProperty;
37var ObjectToString = utils.ImportNow("object_to_string");
38var Script = utils.ImportNow("Script");
39var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
40var StringCharAt;
41var StringIndexOf;
42var StringSubstring;
43var SymbolToString;
44var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
45var Uint16x8ToString;
46var Uint32x4ToString;
47var Uint8x16ToString;
48
49utils.Import(function(from) {
50  ArrayJoin = from.ArrayJoin;
51  Bool16x8ToString = from.Bool16x8ToString;
52  Bool32x4ToString = from.Bool32x4ToString;
53  Bool8x16ToString = from.Bool8x16ToString;
54  Float32x4ToString = from.Float32x4ToString;
55  Int16x8ToString = from.Int16x8ToString;
56  Int32x4ToString = from.Int32x4ToString;
57  Int8x16ToString = from.Int8x16ToString;
58  ObjectDefineProperty = from.ObjectDefineProperty;
59  StringCharAt = from.StringCharAt;
60  StringIndexOf = from.StringIndexOf;
61  StringSubstring = from.StringSubstring;
62  SymbolToString = from.SymbolToString;
63  Uint16x8ToString = from.Uint16x8ToString;
64  Uint32x4ToString = from.Uint32x4ToString;
65  Uint8x16ToString = from.Uint8x16ToString;
66});
67
68utils.ImportFromExperimental(function(from) {
69  FLAG_harmony_tostring = from.FLAG_harmony_tostring;
70});
71
72// -------------------------------------------------------------------
73
74var GlobalError;
75var GlobalTypeError;
76var GlobalRangeError;
77var GlobalURIError;
78var GlobalSyntaxError;
79var GlobalReferenceError;
80var GlobalEvalError;
81
82
83function NoSideEffectsObjectToString() {
84  if (IS_UNDEFINED(this)) return "[object Undefined]";
85  if (IS_NULL(this)) return "[object Null]";
86  var O = TO_OBJECT(this);
87  var builtinTag = %_ClassOf(O);
88  var tag;
89  if (FLAG_harmony_tostring) {
90    tag = %GetDataProperty(O, toStringTagSymbol);
91    if (!IS_STRING(tag)) {
92      tag = builtinTag;
93    }
94  } else {
95    tag = builtinTag;
96  }
97  return `[object ${tag}]`;
98}
99
100function IsErrorObject(obj) {
101  return HAS_PRIVATE(obj, stackTraceSymbol);
102}
103
104function NoSideEffectsErrorToString() {
105  var name = %GetDataProperty(this, "name");
106  var message = %GetDataProperty(this, "message");
107  name = IS_UNDEFINED(name) ? "Error" : NoSideEffectsToString(name);
108  message = IS_UNDEFINED(message) ? "" : NoSideEffectsToString(message);
109  if (name == "") return message;
110  if (message == "") return name;
111  return `${name}: ${message}`;
112}
113
114function NoSideEffectsToString(obj) {
115  if (IS_STRING(obj)) return obj;
116  if (IS_NUMBER(obj)) return %_NumberToString(obj);
117  if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false';
118  if (IS_UNDEFINED(obj)) return 'undefined';
119  if (IS_NULL(obj)) return 'null';
120  if (IS_FUNCTION(obj)) {
121    var str = %FunctionToString(obj);
122    if (str.length > 128) {
123      str = %_SubString(str, 0, 111) + "...<omitted>..." +
124            %_SubString(str, str.length - 2, str.length);
125    }
126    return str;
127  }
128  if (IS_SYMBOL(obj)) return %_Call(SymbolToString, obj);
129  if (IS_SIMD_VALUE(obj)) {
130    switch (typeof(obj)) {
131      case 'float32x4': return %_Call(Float32x4ToString, obj);
132      case 'int32x4':   return %_Call(Int32x4ToString, obj);
133      case 'int16x8':   return %_Call(Int16x8ToString, obj);
134      case 'int8x16':   return %_Call(Int8x16ToString, obj);
135      case 'uint32x4':   return %_Call(Uint32x4ToString, obj);
136      case 'uint16x8':   return %_Call(Uint16x8ToString, obj);
137      case 'uint8x16':   return %_Call(Uint8x16ToString, obj);
138      case 'bool32x4':  return %_Call(Bool32x4ToString, obj);
139      case 'bool16x8':  return %_Call(Bool16x8ToString, obj);
140      case 'bool8x16':  return %_Call(Bool8x16ToString, obj);
141    }
142  }
143
144  if (IS_RECEIVER(obj)) {
145    // When internally formatting error objects, use a side-effects-free version
146    // of Error.prototype.toString independent of the actually installed
147    // toString method.
148    if (IsErrorObject(obj) ||
149        %GetDataProperty(obj, "toString") === ErrorToString) {
150      return %_Call(NoSideEffectsErrorToString, obj);
151    }
152
153    if (%GetDataProperty(obj, "toString") === ObjectToString) {
154      var constructor = %GetDataProperty(obj, "constructor");
155      if (IS_FUNCTION(constructor)) {
156        var constructor_name = %FunctionGetName(constructor);
157        if (constructor_name != "") return `#<${constructor_name}>`;
158      }
159    }
160  }
161
162  return %_Call(NoSideEffectsObjectToString, obj);
163}
164
165
166function MakeGenericError(constructor, type, arg0, arg1, arg2) {
167  var error = new constructor(FormatMessage(type, arg0, arg1, arg2));
168  error[internalErrorSymbol] = true;
169  return error;
170}
171
172
173/**
174 * Set up the Script function and constructor.
175 */
176%FunctionSetInstanceClassName(Script, 'Script');
177%AddNamedProperty(Script.prototype, 'constructor', Script,
178                  DONT_ENUM | DONT_DELETE | READ_ONLY);
179%SetCode(Script, function(x) {
180  // Script objects can only be created by the VM.
181  throw MakeError(kUnsupported);
182});
183
184
185// Helper functions; called from the runtime system.
186function FormatMessage(type, arg0, arg1, arg2) {
187  var arg0 = NoSideEffectsToString(arg0);
188  var arg1 = NoSideEffectsToString(arg1);
189  var arg2 = NoSideEffectsToString(arg2);
190  try {
191    return %FormatMessageString(type, arg0, arg1, arg2);
192  } catch (e) {
193    return "<error>";
194  }
195}
196
197
198function GetLineNumber(message) {
199  var start_position = %MessageGetStartPosition(message);
200  if (start_position == -1) return kNoLineNumberInfo;
201  var script = %MessageGetScript(message);
202  var location = script.locationFromPosition(start_position, true);
203  if (location == null) return kNoLineNumberInfo;
204  return location.line + 1;
205}
206
207
208//Returns the offset of the given position within the containing line.
209function GetColumnNumber(message) {
210  var script = %MessageGetScript(message);
211  var start_position = %MessageGetStartPosition(message);
212  var location = script.locationFromPosition(start_position, true);
213  if (location == null) return -1;
214  return location.column;
215}
216
217
218// Returns the source code line containing the given source
219// position, or the empty string if the position is invalid.
220function GetSourceLine(message) {
221  var script = %MessageGetScript(message);
222  var start_position = %MessageGetStartPosition(message);
223  var location = script.locationFromPosition(start_position, true);
224  if (location == null) return "";
225  return location.sourceText();
226}
227
228
229/**
230 * Find a line number given a specific source position.
231 * @param {number} position The source position.
232 * @return {number} 0 if input too small, -1 if input too large,
233       else the line number.
234 */
235function ScriptLineFromPosition(position) {
236  var lower = 0;
237  var upper = this.lineCount() - 1;
238  var line_ends = this.line_ends;
239
240  // We'll never find invalid positions so bail right away.
241  if (position > line_ends[upper]) {
242    return -1;
243  }
244
245  // This means we don't have to safe-guard indexing line_ends[i - 1].
246  if (position <= line_ends[0]) {
247    return 0;
248  }
249
250  // Binary search to find line # from position range.
251  while (upper >= 1) {
252    var i = (lower + upper) >> 1;
253
254    if (position > line_ends[i]) {
255      lower = i + 1;
256    } else if (position <= line_ends[i - 1]) {
257      upper = i - 1;
258    } else {
259      return i;
260    }
261  }
262
263  return -1;
264}
265
266/**
267 * Get information on a specific source position.
268 * @param {number} position The source position
269 * @param {boolean} include_resource_offset Set to true to have the resource
270 *     offset added to the location
271 * @return {SourceLocation}
272 *     If line is negative or not in the source null is returned.
273 */
274function ScriptLocationFromPosition(position,
275                                    include_resource_offset) {
276  var line = this.lineFromPosition(position);
277  if (line == -1) return null;
278
279  // Determine start, end and column.
280  var line_ends = this.line_ends;
281  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
282  var end = line_ends[line];
283  if (end > 0 && %_Call(StringCharAt, this.source, end - 1) == '\r') {
284    end--;
285  }
286  var column = position - start;
287
288  // Adjust according to the offset within the resource.
289  if (include_resource_offset) {
290    line += this.line_offset;
291    if (line == this.line_offset) {
292      column += this.column_offset;
293    }
294  }
295
296  return new SourceLocation(this, position, line, column, start, end);
297}
298
299
300/**
301 * Get information on a specific source line and column possibly offset by a
302 * fixed source position. This function is used to find a source position from
303 * a line and column position. The fixed source position offset is typically
304 * used to find a source position in a function based on a line and column in
305 * the source for the function alone. The offset passed will then be the
306 * start position of the source for the function within the full script source.
307 * @param {number} opt_line The line within the source. Default value is 0
308 * @param {number} opt_column The column in within the line. Default value is 0
309 * @param {number} opt_offset_position The offset from the begining of the
310 *     source from where the line and column calculation starts.
311 *     Default value is 0
312 * @return {SourceLocation}
313 *     If line is negative or not in the source null is returned.
314 */
315function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
316  // Default is the first line in the script. Lines in the script is relative
317  // to the offset within the resource.
318  var line = 0;
319  if (!IS_UNDEFINED(opt_line)) {
320    line = opt_line - this.line_offset;
321  }
322
323  // Default is first column. If on the first line add the offset within the
324  // resource.
325  var column = opt_column || 0;
326  if (line == 0) {
327    column -= this.column_offset;
328  }
329
330  var offset_position = opt_offset_position || 0;
331  if (line < 0 || column < 0 || offset_position < 0) return null;
332  if (line == 0) {
333    return this.locationFromPosition(offset_position + column, false);
334  } else {
335    // Find the line where the offset position is located.
336    var offset_line = this.lineFromPosition(offset_position);
337
338    if (offset_line == -1 || offset_line + line >= this.lineCount()) {
339      return null;
340    }
341
342    return this.locationFromPosition(
343        this.line_ends[offset_line + line - 1] + 1 + column);  // line > 0 here.
344  }
345}
346
347
348/**
349 * Get a slice of source code from the script. The boundaries for the slice is
350 * specified in lines.
351 * @param {number} opt_from_line The first line (zero bound) in the slice.
352 *     Default is 0
353 * @param {number} opt_to_column The last line (zero bound) in the slice (non
354 *     inclusive). Default is the number of lines in the script
355 * @return {SourceSlice} The source slice or null of the parameters where
356 *     invalid
357 */
358function ScriptSourceSlice(opt_from_line, opt_to_line) {
359  var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset
360                                              : opt_from_line;
361  var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount()
362                                          : opt_to_line;
363
364  // Adjust according to the offset within the resource.
365  from_line -= this.line_offset;
366  to_line -= this.line_offset;
367  if (from_line < 0) from_line = 0;
368  if (to_line > this.lineCount()) to_line = this.lineCount();
369
370  // Check parameters.
371  if (from_line >= this.lineCount() ||
372      to_line < 0 ||
373      from_line > to_line) {
374    return null;
375  }
376
377  var line_ends = this.line_ends;
378  var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
379  var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
380
381  // Return a source slice with line numbers re-adjusted to the resource.
382  return new SourceSlice(this,
383                         from_line + this.line_offset,
384                         to_line + this.line_offset,
385                          from_position, to_position);
386}
387
388
389function ScriptSourceLine(opt_line) {
390  // Default is the first line in the script. Lines in the script are relative
391  // to the offset within the resource.
392  var line = 0;
393  if (!IS_UNDEFINED(opt_line)) {
394    line = opt_line - this.line_offset;
395  }
396
397  // Check parameter.
398  if (line < 0 || this.lineCount() <= line) {
399    return null;
400  }
401
402  // Return the source line.
403  var line_ends = this.line_ends;
404  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
405  var end = line_ends[line];
406  return %_Call(StringSubstring, this.source, start, end);
407}
408
409
410/**
411 * Returns the number of source lines.
412 * @return {number}
413 *     Number of source lines.
414 */
415function ScriptLineCount() {
416  // Return number of source lines.
417  return this.line_ends.length;
418}
419
420
421/**
422 * Returns the position of the nth line end.
423 * @return {number}
424 *     Zero-based position of the nth line end in the script.
425 */
426function ScriptLineEnd(n) {
427  return this.line_ends[n];
428}
429
430
431/**
432 * If sourceURL comment is available returns sourceURL comment contents.
433 * Otherwise, script name is returned. See
434 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
435 * and Source Map Revision 3 proposal for details on using //# sourceURL and
436 * deprecated //@ sourceURL comment to identify scripts that don't have name.
437 *
438 * @return {?string} script name if present, value for //# sourceURL comment or
439 * deprecated //@ sourceURL comment otherwise.
440 */
441function ScriptNameOrSourceURL() {
442  if (this.source_url) return this.source_url;
443  return this.name;
444}
445
446
447utils.SetUpLockedPrototype(Script, [
448    "source",
449    "name",
450    "source_url",
451    "source_mapping_url",
452    "line_ends",
453    "line_offset",
454    "column_offset"
455  ], [
456    "lineFromPosition", ScriptLineFromPosition,
457    "locationFromPosition", ScriptLocationFromPosition,
458    "locationFromLine", ScriptLocationFromLine,
459    "sourceSlice", ScriptSourceSlice,
460    "sourceLine", ScriptSourceLine,
461    "lineCount", ScriptLineCount,
462    "nameOrSourceURL", ScriptNameOrSourceURL,
463    "lineEnd", ScriptLineEnd
464  ]
465);
466
467
468/**
469 * Class for source location. A source location is a position within some
470 * source with the following properties:
471 *   script   : script object for the source
472 *   line     : source line number
473 *   column   : source column within the line
474 *   position : position within the source
475 *   start    : position of start of source context (inclusive)
476 *   end      : position of end of source context (not inclusive)
477 * Source text for the source context is the character interval
478 * [start, end[. In most cases end will point to a newline character.
479 * It might point just past the final position of the source if the last
480 * source line does not end with a newline character.
481 * @param {Script} script The Script object for which this is a location
482 * @param {number} position Source position for the location
483 * @param {number} line The line number for the location
484 * @param {number} column The column within the line for the location
485 * @param {number} start Source position for start of source context
486 * @param {number} end Source position for end of source context
487 * @constructor
488 */
489function SourceLocation(script, position, line, column, start, end) {
490  this.script = script;
491  this.position = position;
492  this.line = line;
493  this.column = column;
494  this.start = start;
495  this.end = end;
496}
497
498
499/**
500 * Get the source text for a SourceLocation
501 * @return {String}
502 *     Source text for this location.
503 */
504function SourceLocationSourceText() {
505  return %_Call(StringSubstring, this.script.source, this.start, this.end);
506}
507
508
509utils.SetUpLockedPrototype(SourceLocation,
510  ["script", "position", "line", "column", "start", "end"],
511  ["sourceText", SourceLocationSourceText]
512);
513
514
515/**
516 * Class for a source slice. A source slice is a part of a script source with
517 * the following properties:
518 *   script        : script object for the source
519 *   from_line     : line number for the first line in the slice
520 *   to_line       : source line number for the last line in the slice
521 *   from_position : position of the first character in the slice
522 *   to_position   : position of the last character in the slice
523 * The to_line and to_position are not included in the slice, that is the lines
524 * in the slice are [from_line, to_line[. Likewise the characters in the slice
525 * are [from_position, to_position[.
526 * @param {Script} script The Script object for the source slice
527 * @param {number} from_line
528 * @param {number} to_line
529 * @param {number} from_position
530 * @param {number} to_position
531 * @constructor
532 */
533function SourceSlice(script, from_line, to_line, from_position, to_position) {
534  this.script = script;
535  this.from_line = from_line;
536  this.to_line = to_line;
537  this.from_position = from_position;
538  this.to_position = to_position;
539}
540
541/**
542 * Get the source text for a SourceSlice
543 * @return {String} Source text for this slice. The last line will include
544 *     the line terminating characters (if any)
545 */
546function SourceSliceSourceText() {
547  return %_Call(StringSubstring,
548                this.script.source,
549                this.from_position,
550                this.to_position);
551}
552
553utils.SetUpLockedPrototype(SourceSlice,
554  ["script", "from_line", "to_line", "from_position", "to_position"],
555  ["sourceText", SourceSliceSourceText]
556);
557
558
559function GetStackTraceLine(recv, fun, pos, isGlobal) {
560  return new CallSite(recv, fun, pos, false).toString();
561}
562
563// ----------------------------------------------------------------------------
564// Error implementation
565
566function CallSite(receiver, fun, pos, strict_mode) {
567  if (!IS_FUNCTION(fun)) {
568    throw MakeTypeError(kCallSiteExpectsFunction, typeof fun);
569  }
570
571  if (IS_UNDEFINED(new.target)) {
572    return new CallSite(receiver, fun, pos, strict_mode);
573  }
574
575  SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
576  SET_PRIVATE(this, callSiteFunctionSymbol, fun);
577  SET_PRIVATE(this, callSitePositionSymbol, TO_INT32(pos));
578  SET_PRIVATE(this, callSiteStrictSymbol, TO_BOOLEAN(strict_mode));
579}
580
581function CallSiteGetThis() {
582  return GET_PRIVATE(this, callSiteStrictSymbol)
583      ? UNDEFINED : GET_PRIVATE(this, callSiteReceiverSymbol);
584}
585
586function CallSiteGetFunction() {
587  return GET_PRIVATE(this, callSiteStrictSymbol)
588      ? UNDEFINED : GET_PRIVATE(this, callSiteFunctionSymbol);
589}
590
591function CallSiteGetPosition() {
592  return GET_PRIVATE(this, callSitePositionSymbol);
593}
594
595function CallSiteGetTypeName() {
596  return GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), false);
597}
598
599function CallSiteIsToplevel() {
600  return %CallSiteIsToplevelRT(this);
601}
602
603function CallSiteIsEval() {
604  return %CallSiteIsEvalRT(this);
605}
606
607function CallSiteGetEvalOrigin() {
608  var script = %FunctionGetScript(GET_PRIVATE(this, callSiteFunctionSymbol));
609  return FormatEvalOrigin(script);
610}
611
612function CallSiteGetScriptNameOrSourceURL() {
613  return %CallSiteGetScriptNameOrSourceUrlRT(this);
614}
615
616function CallSiteGetFunctionName() {
617  // See if the function knows its own name
618  return %CallSiteGetFunctionNameRT(this);
619}
620
621function CallSiteGetMethodName() {
622  // See if we can find a unique property on the receiver that holds
623  // this function.
624  return %CallSiteGetMethodNameRT(this);
625}
626
627function CallSiteGetFileName() {
628  return %CallSiteGetFileNameRT(this);
629}
630
631function CallSiteGetLineNumber() {
632  return %CallSiteGetLineNumberRT(this);
633}
634
635function CallSiteGetColumnNumber() {
636  return %CallSiteGetColumnNumberRT(this);
637}
638
639function CallSiteIsNative() {
640  return %CallSiteIsNativeRT(this);
641}
642
643function CallSiteIsConstructor() {
644  return %CallSiteIsConstructorRT(this);
645}
646
647function CallSiteToString() {
648  var fileName;
649  var fileLocation = "";
650  if (this.isNative()) {
651    fileLocation = "native";
652  } else {
653    fileName = this.getScriptNameOrSourceURL();
654    if (!fileName && this.isEval()) {
655      fileLocation = this.getEvalOrigin();
656      fileLocation += ", ";  // Expecting source position to follow.
657    }
658
659    if (fileName) {
660      fileLocation += fileName;
661    } else {
662      // Source code does not originate from a file and is not native, but we
663      // can still get the source position inside the source string, e.g. in
664      // an eval string.
665      fileLocation += "<anonymous>";
666    }
667    var lineNumber = this.getLineNumber();
668    if (lineNumber != null) {
669      fileLocation += ":" + lineNumber;
670      var columnNumber = this.getColumnNumber();
671      if (columnNumber) {
672        fileLocation += ":" + columnNumber;
673      }
674    }
675  }
676
677  var line = "";
678  var functionName = this.getFunctionName();
679  var addSuffix = true;
680  var isConstructor = this.isConstructor();
681  var isMethodCall = !(this.isToplevel() || isConstructor);
682  if (isMethodCall) {
683    var typeName = GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), true);
684    var methodName = this.getMethodName();
685    if (functionName) {
686      if (typeName && %_Call(StringIndexOf, functionName, typeName) != 0) {
687        line += typeName + ".";
688      }
689      line += functionName;
690      if (methodName &&
691          (%_Call(StringIndexOf, functionName, "." + methodName) !=
692           functionName.length - methodName.length - 1)) {
693        line += " [as " + methodName + "]";
694      }
695    } else {
696      line += typeName + "." + (methodName || "<anonymous>");
697    }
698  } else if (isConstructor) {
699    line += "new " + (functionName || "<anonymous>");
700  } else if (functionName) {
701    line += functionName;
702  } else {
703    line += fileLocation;
704    addSuffix = false;
705  }
706  if (addSuffix) {
707    line += " (" + fileLocation + ")";
708  }
709  return line;
710}
711
712utils.SetUpLockedPrototype(CallSite, ["receiver", "fun", "pos"], [
713  "getThis", CallSiteGetThis,
714  "getTypeName", CallSiteGetTypeName,
715  "isToplevel", CallSiteIsToplevel,
716  "isEval", CallSiteIsEval,
717  "getEvalOrigin", CallSiteGetEvalOrigin,
718  "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL,
719  "getFunction", CallSiteGetFunction,
720  "getFunctionName", CallSiteGetFunctionName,
721  "getMethodName", CallSiteGetMethodName,
722  "getFileName", CallSiteGetFileName,
723  "getLineNumber", CallSiteGetLineNumber,
724  "getColumnNumber", CallSiteGetColumnNumber,
725  "isNative", CallSiteIsNative,
726  "getPosition", CallSiteGetPosition,
727  "isConstructor", CallSiteIsConstructor,
728  "toString", CallSiteToString
729]);
730
731
732function FormatEvalOrigin(script) {
733  var sourceURL = script.nameOrSourceURL();
734  if (sourceURL) {
735    return sourceURL;
736  }
737
738  var eval_origin = "eval at ";
739  if (script.eval_from_function_name) {
740    eval_origin += script.eval_from_function_name;
741  } else {
742    eval_origin +=  "<anonymous>";
743  }
744
745  var eval_from_script = script.eval_from_script;
746  if (eval_from_script) {
747    if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
748      // eval script originated from another eval.
749      eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
750    } else {
751      // eval script originated from "real" source.
752      if (eval_from_script.name) {
753        eval_origin += " (" + eval_from_script.name;
754        var location = eval_from_script.locationFromPosition(
755            script.eval_from_script_position, true);
756        if (location) {
757          eval_origin += ":" + (location.line + 1);
758          eval_origin += ":" + (location.column + 1);
759        }
760        eval_origin += ")";
761      } else {
762        eval_origin += " (unknown source)";
763      }
764    }
765  }
766
767  return eval_origin;
768}
769
770
771function FormatErrorString(error) {
772  try {
773    return %_Call(ErrorToString, error);
774  } catch (e) {
775    try {
776      return "<error: " + e + ">";
777    } catch (ee) {
778      return "<error>";
779    }
780  }
781}
782
783
784function GetStackFrames(raw_stack) {
785  var frames = new InternalArray();
786  var sloppy_frames = raw_stack[0];
787  for (var i = 1; i < raw_stack.length; i += 4) {
788    var recv = raw_stack[i];
789    var fun = raw_stack[i + 1];
790    var code = raw_stack[i + 2];
791    var pc = raw_stack[i + 3];
792    var pos = %_IsSmi(code) ? code : %FunctionGetPositionForOffset(code, pc);
793    sloppy_frames--;
794    frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
795  }
796  return frames;
797}
798
799
800// Flag to prevent recursive call of Error.prepareStackTrace.
801var formatting_custom_stack_trace = false;
802
803
804function FormatStackTrace(obj, raw_stack) {
805  var frames = GetStackFrames(raw_stack);
806  if (IS_FUNCTION(GlobalError.prepareStackTrace) &&
807      !formatting_custom_stack_trace) {
808    var array = [];
809    %MoveArrayContents(frames, array);
810    formatting_custom_stack_trace = true;
811    var stack_trace = UNDEFINED;
812    try {
813      stack_trace = GlobalError.prepareStackTrace(obj, array);
814    } catch (e) {
815      throw e;  // The custom formatting function threw.  Rethrow.
816    } finally {
817      formatting_custom_stack_trace = false;
818    }
819    return stack_trace;
820  }
821
822  var lines = new InternalArray();
823  lines.push(FormatErrorString(obj));
824  for (var i = 0; i < frames.length; i++) {
825    var frame = frames[i];
826    var line;
827    try {
828      line = frame.toString();
829    } catch (e) {
830      try {
831        line = "<error: " + e + ">";
832      } catch (ee) {
833        // Any code that reaches this point is seriously nasty!
834        line = "<error>";
835      }
836    }
837    lines.push("    at " + line);
838  }
839  return %_Call(ArrayJoin, lines, "\n");
840}
841
842
843function GetTypeName(receiver, requireConstructor) {
844  if (IS_NULL_OR_UNDEFINED(receiver)) return null;
845  if (IS_PROXY(receiver)) return "Proxy";
846
847  var constructor = %GetDataProperty(TO_OBJECT(receiver), "constructor");
848  if (!IS_FUNCTION(constructor)) {
849    return requireConstructor ? null : %_Call(NoSideEffectsToString, receiver);
850  }
851  return %FunctionGetName(constructor);
852}
853
854
855// Format the stack trace if not yet done, and return it.
856// Cache the formatted stack trace on the holder.
857var StackTraceGetter = function() {
858  var formatted_stack_trace = UNDEFINED;
859  var holder = this;
860  while (holder) {
861    var formatted_stack_trace =
862      GET_PRIVATE(holder, formattedStackTraceSymbol);
863    if (IS_UNDEFINED(formatted_stack_trace)) {
864      // No formatted stack trace available.
865      var stack_trace = GET_PRIVATE(holder, stackTraceSymbol);
866      if (IS_UNDEFINED(stack_trace)) {
867        // Neither formatted nor structured stack trace available.
868        // Look further up the prototype chain.
869        holder = %_GetPrototype(holder);
870        continue;
871      }
872      formatted_stack_trace = FormatStackTrace(holder, stack_trace);
873      SET_PRIVATE(holder, stackTraceSymbol, UNDEFINED);
874      SET_PRIVATE(holder, formattedStackTraceSymbol, formatted_stack_trace);
875    }
876    return formatted_stack_trace;
877  }
878  return UNDEFINED;
879};
880
881
882// If the receiver equals the holder, set the formatted stack trace that the
883// getter returns.
884var StackTraceSetter = function(v) {
885  if (IsErrorObject(this)) {
886    SET_PRIVATE(this, stackTraceSymbol, UNDEFINED);
887    SET_PRIVATE(this, formattedStackTraceSymbol, v);
888  }
889};
890
891
892// Use a dummy function since we do not actually want to capture a stack trace
893// when constructing the initial Error prototytpes.
894var captureStackTrace = function() {};
895
896
897// Set up special error type constructors.
898function SetUpError(error_function) {
899  %FunctionSetInstanceClassName(error_function, 'Error');
900  var name = error_function.name;
901  var prototype = new GlobalObject();
902  if (name !== 'Error') {
903    %InternalSetPrototype(error_function, GlobalError);
904    %InternalSetPrototype(prototype, GlobalError.prototype);
905  }
906  %FunctionSetPrototype(error_function, prototype);
907
908  %AddNamedProperty(error_function.prototype, 'name', name, DONT_ENUM);
909  %AddNamedProperty(error_function.prototype, 'message', '', DONT_ENUM);
910  %AddNamedProperty(
911      error_function.prototype, 'constructor', error_function, DONT_ENUM);
912
913  %SetCode(error_function, function(m) {
914    if (IS_UNDEFINED(new.target)) return new error_function(m);
915
916    try { captureStackTrace(this, error_function); } catch (e) { }
917    // Define all the expected properties directly on the error
918    // object. This avoids going through getters and setters defined
919    // on prototype objects.
920    if (!IS_UNDEFINED(m)) {
921      %AddNamedProperty(this, 'message', TO_STRING(m), DONT_ENUM);
922    }
923  });
924
925  %SetNativeFlag(error_function);
926  return error_function;
927};
928
929GlobalError = SetUpError(global.Error);
930GlobalEvalError = SetUpError(global.EvalError);
931GlobalRangeError = SetUpError(global.RangeError);
932GlobalReferenceError = SetUpError(global.ReferenceError);
933GlobalSyntaxError = SetUpError(global.SyntaxError);
934GlobalTypeError = SetUpError(global.TypeError);
935GlobalURIError = SetUpError(global.URIError);
936
937utils.InstallFunctions(GlobalError.prototype, DONT_ENUM,
938                       ['toString', ErrorToString]);
939
940function ErrorToString() {
941  if (!IS_RECEIVER(this)) {
942    throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
943  }
944
945  var name = this.name;
946  name = IS_UNDEFINED(name) ? "Error" : TO_STRING(name);
947
948  var message = this.message;
949  message = IS_UNDEFINED(message) ? "" : TO_STRING(message);
950
951  if (name == "") return message;
952  if (message == "") return name;
953  return `${name}: ${message}`
954}
955
956function MakeError(type, arg0, arg1, arg2) {
957  return MakeGenericError(GlobalError, type, arg0, arg1, arg2);
958}
959
960function MakeRangeError(type, arg0, arg1, arg2) {
961  return MakeGenericError(GlobalRangeError, type, arg0, arg1, arg2);
962}
963
964function MakeSyntaxError(type, arg0, arg1, arg2) {
965  return MakeGenericError(GlobalSyntaxError, type, arg0, arg1, arg2);
966}
967
968function MakeTypeError(type, arg0, arg1, arg2) {
969  return MakeGenericError(GlobalTypeError, type, arg0, arg1, arg2);
970}
971
972function MakeURIError() {
973  return MakeGenericError(GlobalURIError, kURIMalformed);
974}
975
976// Boilerplate for exceptions for stack overflows. Used from
977// Isolate::StackOverflow().
978var StackOverflowBoilerplate = MakeRangeError(kStackOverflow);
979utils.InstallGetterSetter(StackOverflowBoilerplate, 'stack',
980                          StackTraceGetter, StackTraceSetter)
981
982// Define actual captureStackTrace function after everything has been set up.
983captureStackTrace = function captureStackTrace(obj, cons_opt) {
984  // Define accessors first, as this may fail and throw.
985  ObjectDefineProperty(obj, 'stack', { get: StackTraceGetter,
986                                       set: StackTraceSetter,
987                                       configurable: true });
988  %CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace);
989};
990
991GlobalError.captureStackTrace = captureStackTrace;
992
993%InstallToContext([
994  "get_stack_trace_line_fun", GetStackTraceLine,
995  "make_error_function", MakeGenericError,
996  "make_range_error", MakeRangeError,
997  "make_type_error", MakeTypeError,
998  "message_get_column_number", GetColumnNumber,
999  "message_get_line_number", GetLineNumber,
1000  "message_get_source_line", GetSourceLine,
1001  "no_side_effects_to_string_fun", NoSideEffectsToString,
1002  "stack_overflow_boilerplate", StackOverflowBoilerplate,
1003]);
1004
1005utils.Export(function(to) {
1006  to.ErrorToString = ErrorToString;
1007  to.MakeError = MakeError;
1008  to.MakeRangeError = MakeRangeError;
1009  to.MakeSyntaxError = MakeSyntaxError;
1010  to.MakeTypeError = MakeTypeError;
1011  to.MakeURIError = MakeURIError;
1012});
1013
1014});
1015