blob: de6a362c337996e4510e9f921881514faffd4717 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29// -------------------------------------------------------------------
Andrei Popescu31002712010-02-23 13:46:05 +000030//
31// Matches Script::Type from objects.h
32var TYPE_NATIVE = 0;
33var TYPE_EXTENSION = 1;
34var TYPE_NORMAL = 2;
35
36// Matches Script::CompilationType from objects.h
37var COMPILATION_TYPE_HOST = 0;
38var COMPILATION_TYPE_EVAL = 1;
39var COMPILATION_TYPE_JSON = 2;
Steve Blocka7e24c12009-10-30 11:49:00 +000040
41// Lazily initialized.
42var kVowelSounds = 0;
43var kCapitalVowelSounds = 0;
44
45// If this object gets passed to an error constructor the error will
46// get an accessor for .message that constructs a descriptive error
47// message on access.
48var kAddMessageAccessorsMarker = { };
49
50
51function GetInstanceName(cons) {
52 if (cons.length == 0) {
53 return "";
54 }
55 var first = %StringToLowerCase(StringCharAt.call(cons, 0));
56 if (kVowelSounds === 0) {
57 kVowelSounds = {a: true, e: true, i: true, o: true, u: true, y: true};
58 kCapitalVowelSounds = {a: true, e: true, i: true, o: true, u: true, h: true,
59 f: true, l: true, m: true, n: true, r: true, s: true, x: true, y: true};
60 }
61 var vowel_mapping = kVowelSounds;
62 if (cons.length > 1 && (StringCharAt.call(cons, 0) != first)) {
63 // First char is upper case
64 var second = %StringToLowerCase(StringCharAt.call(cons, 1));
65 // Second char is upper case
66 if (StringCharAt.call(cons, 1) != second) {
67 vowel_mapping = kCapitalVowelSounds;
68 }
69 }
70 var s = vowel_mapping[first] ? "an " : "a ";
71 return s + cons;
72}
73
74
75var kMessages = 0;
76
77
78function FormatString(format, args) {
79 var result = format;
80 for (var i = 0; i < args.length; i++) {
81 var str;
82 try { str = ToDetailString(args[i]); }
83 catch (e) { str = "#<error>"; }
84 result = ArrayJoin.call(StringSplit.call(result, "%" + i), str);
85 }
86 return result;
87}
88
89
90function ToDetailString(obj) {
91 if (obj != null && IS_OBJECT(obj) && obj.toString === $Object.prototype.toString) {
92 var constructor = obj.constructor;
93 if (!constructor) return ToString(obj);
94 var constructorName = constructor.name;
95 if (!constructorName) return ToString(obj);
96 return "#<" + GetInstanceName(constructorName) + ">";
97 } else {
98 return ToString(obj);
99 }
100}
101
102
103function MakeGenericError(constructor, type, args) {
104 if (IS_UNDEFINED(args)) {
105 args = [];
106 }
107 var e = new constructor(kAddMessageAccessorsMarker);
108 e.type = type;
109 e.arguments = args;
110 return e;
111}
112
113
114/**
115 * Setup the Script function and constructor.
116 */
117%FunctionSetInstanceClassName(Script, 'Script');
118%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM);
119%SetCode(Script, function(x) {
120 // Script objects can only be created by the VM.
121 throw new $Error("Not supported");
122});
123
124
125// Helper functions; called from the runtime system.
126function FormatMessage(message) {
127 if (kMessages === 0) {
128 kMessages = {
129 // Error
130 cyclic_proto: "Cyclic __proto__ value",
131 // TypeError
132 unexpected_token: "Unexpected token %0",
133 unexpected_token_number: "Unexpected number",
134 unexpected_token_string: "Unexpected string",
135 unexpected_token_identifier: "Unexpected identifier",
136 unexpected_eos: "Unexpected end of input",
137 malformed_regexp: "Invalid regular expression: /%0/: %1",
138 unterminated_regexp: "Invalid regular expression: missing /",
139 regexp_flags: "Cannot supply flags when constructing one RegExp from another",
Steve Block6ded16b2010-05-10 14:33:55 +0100140 incompatible_method_receiver: "Method %0 called on incompatible receiver %1",
Steve Blocka7e24c12009-10-30 11:49:00 +0000141 invalid_lhs_in_assignment: "Invalid left-hand side in assignment",
142 invalid_lhs_in_for_in: "Invalid left-hand side in for-in",
143 invalid_lhs_in_postfix_op: "Invalid left-hand side expression in postfix operation",
144 invalid_lhs_in_prefix_op: "Invalid left-hand side expression in prefix operation",
145 multiple_defaults_in_switch: "More than one default clause in switch statement",
146 newline_after_throw: "Illegal newline after throw",
147 redeclaration: "%0 '%1' has already been declared",
148 no_catch_or_finally: "Missing catch or finally after try",
149 unknown_label: "Undefined label '%0'",
150 uncaught_exception: "Uncaught %0",
151 stack_trace: "Stack Trace:\n%0",
152 called_non_callable: "%0 is not a function",
153 undefined_method: "Object %1 has no method '%0'",
154 property_not_function: "Property '%0' of object %1 is not a function",
155 cannot_convert_to_primitive: "Cannot convert object to primitive value",
156 not_constructor: "%0 is not a constructor",
157 not_defined: "%0 is not defined",
158 non_object_property_load: "Cannot read property '%0' of %1",
159 non_object_property_store: "Cannot set property '%0' of %1",
160 non_object_property_call: "Cannot call method '%0' of %1",
161 with_expression: "%0 has no properties",
162 illegal_invocation: "Illegal invocation",
163 no_setter_in_callback: "Cannot set property %0 of %1 which has only a getter",
164 apply_non_function: "Function.prototype.apply was called on %0, which is a %1 and not a function",
165 apply_wrong_args: "Function.prototype.apply: Arguments list has wrong type",
166 invalid_in_operator_use: "Cannot use 'in' operator to search for '%0' in %1",
167 instanceof_function_expected: "Expecting a function in instanceof check, but got %0",
168 instanceof_nonobject_proto: "Function has non-object prototype '%0' in instanceof check",
169 null_to_object: "Cannot convert null to object",
170 reduce_no_initial: "Reduce of empty array with no initial value",
Leon Clarkee46be812010-01-19 14:06:41 +0000171 getter_must_be_callable: "Getter must be a function: %0",
172 setter_must_be_callable: "Setter must be a function: %0",
173 value_and_accessor: "Invalid property. A property cannot both have accessors and be writable or have a value: %0",
174 proto_object_or_null: "Object prototype may only be an Object or null",
175 property_desc_object: "Property description must be an object: %0",
Andrei Popescu31002712010-02-23 13:46:05 +0000176 redefine_disallowed: "Cannot redefine property: %0",
177 define_disallowed: "Cannot define property, object is not extensible: %0",
Steve Blocka7e24c12009-10-30 11:49:00 +0000178 // RangeError
179 invalid_array_length: "Invalid array length",
180 stack_overflow: "Maximum call stack size exceeded",
181 apply_overflow: "Function.prototype.apply cannot support %0 arguments",
182 // SyntaxError
183 unable_to_parse: "Parse error",
184 duplicate_regexp_flag: "Duplicate RegExp flag %0",
185 invalid_regexp: "Invalid RegExp pattern /%0/",
186 illegal_break: "Illegal break statement",
187 illegal_continue: "Illegal continue statement",
188 illegal_return: "Illegal return statement",
189 error_loading_debugger: "Error loading debugger",
190 no_input_to_regexp: "No input to %0",
191 result_not_primitive: "Result of %0 must be a primitive, was %1",
192 invalid_json: "String '%0' is not valid JSON",
193 circular_structure: "Converting circular structure to JSON",
Leon Clarkee46be812010-01-19 14:06:41 +0000194 obj_ctor_property_non_object: "Object.%0 called on non-object",
Steve Block6ded16b2010-05-10 14:33:55 +0100195 array_indexof_not_defined: "Array.getIndexOf: Argument undefined",
196 illegal_access: "illegal access"
Steve Blocka7e24c12009-10-30 11:49:00 +0000197 };
198 }
199 var format = kMessages[message.type];
200 if (!format) return "<unknown message " + message.type + ">";
201 return FormatString(format, message.args);
202}
203
204
205function GetLineNumber(message) {
206 if (message.startPos == -1) return -1;
207 var location = message.script.locationFromPosition(message.startPos, true);
208 if (location == null) return -1;
209 return location.line + 1;
210}
211
212
213// Returns the source code line containing the given source
214// position, or the empty string if the position is invalid.
215function GetSourceLine(message) {
216 var location = message.script.locationFromPosition(message.startPos, true);
217 if (location == null) return "";
218 location.restrict();
219 return location.sourceText();
220}
221
222
223function MakeTypeError(type, args) {
224 return MakeGenericError($TypeError, type, args);
225}
226
227
228function MakeRangeError(type, args) {
229 return MakeGenericError($RangeError, type, args);
230}
231
232
233function MakeSyntaxError(type, args) {
234 return MakeGenericError($SyntaxError, type, args);
235}
236
237
238function MakeReferenceError(type, args) {
239 return MakeGenericError($ReferenceError, type, args);
240}
241
242
243function MakeEvalError(type, args) {
244 return MakeGenericError($EvalError, type, args);
245}
246
247
248function MakeError(type, args) {
249 return MakeGenericError($Error, type, args);
250}
251
252/**
253 * Find a line number given a specific source position.
254 * @param {number} position The source position.
255 * @return {number} 0 if input too small, -1 if input too large,
256 else the line number.
257 */
258Script.prototype.lineFromPosition = function(position) {
259 var lower = 0;
260 var upper = this.lineCount() - 1;
Steve Blockd0582a62009-12-15 09:54:21 +0000261 var line_ends = this.line_ends;
Steve Blocka7e24c12009-10-30 11:49:00 +0000262
263 // We'll never find invalid positions so bail right away.
Steve Blockd0582a62009-12-15 09:54:21 +0000264 if (position > line_ends[upper]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000265 return -1;
266 }
267
268 // This means we don't have to safe-guard indexing line_ends[i - 1].
Steve Blockd0582a62009-12-15 09:54:21 +0000269 if (position <= line_ends[0]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000270 return 0;
271 }
272
273 // Binary search to find line # from position range.
274 while (upper >= 1) {
275 var i = (lower + upper) >> 1;
276
Steve Blockd0582a62009-12-15 09:54:21 +0000277 if (position > line_ends[i]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000278 lower = i + 1;
Steve Blockd0582a62009-12-15 09:54:21 +0000279 } else if (position <= line_ends[i - 1]) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000280 upper = i - 1;
281 } else {
282 return i;
283 }
284 }
285 return -1;
286}
287
288/**
289 * Get information on a specific source position.
290 * @param {number} position The source position
291 * @param {boolean} include_resource_offset Set to true to have the resource
292 * offset added to the location
293 * @return {SourceLocation}
294 * If line is negative or not in the source null is returned.
295 */
296Script.prototype.locationFromPosition = function (position,
297 include_resource_offset) {
298 var line = this.lineFromPosition(position);
299 if (line == -1) return null;
300
301 // Determine start, end and column.
Steve Blockd0582a62009-12-15 09:54:21 +0000302 var line_ends = this.line_ends;
303 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
304 var end = line_ends[line];
Steve Blocka7e24c12009-10-30 11:49:00 +0000305 if (end > 0 && StringCharAt.call(this.source, end - 1) == '\r') end--;
306 var column = position - start;
307
308 // Adjust according to the offset within the resource.
309 if (include_resource_offset) {
310 line += this.line_offset;
311 if (line == this.line_offset) {
312 column += this.column_offset;
313 }
314 }
315
316 return new SourceLocation(this, position, line, column, start, end);
317};
318
319
320/**
321 * Get information on a specific source line and column possibly offset by a
322 * fixed source position. This function is used to find a source position from
323 * a line and column position. The fixed source position offset is typically
324 * used to find a source position in a function based on a line and column in
325 * the source for the function alone. The offset passed will then be the
326 * start position of the source for the function within the full script source.
327 * @param {number} opt_line The line within the source. Default value is 0
328 * @param {number} opt_column The column in within the line. Default value is 0
329 * @param {number} opt_offset_position The offset from the begining of the
330 * source from where the line and column calculation starts. Default value is 0
331 * @return {SourceLocation}
332 * If line is negative or not in the source null is returned.
333 */
334Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) {
335 // Default is the first line in the script. Lines in the script is relative
336 // to the offset within the resource.
337 var line = 0;
338 if (!IS_UNDEFINED(opt_line)) {
339 line = opt_line - this.line_offset;
340 }
341
342 // Default is first column. If on the first line add the offset within the
343 // resource.
344 var column = opt_column || 0;
345 if (line == 0) {
346 column -= this.column_offset
347 }
348
349 var offset_position = opt_offset_position || 0;
350 if (line < 0 || column < 0 || offset_position < 0) return null;
351 if (line == 0) {
352 return this.locationFromPosition(offset_position + column, false);
353 } else {
354 // Find the line where the offset position is located.
355 var offset_line = this.lineFromPosition(offset_position);
356
357 if (offset_line == -1 || offset_line + line >= this.lineCount()) {
358 return null;
359 }
360
361 return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
362 }
363}
364
365
366/**
367 * Get a slice of source code from the script. The boundaries for the slice is
368 * specified in lines.
369 * @param {number} opt_from_line The first line (zero bound) in the slice.
370 * Default is 0
371 * @param {number} opt_to_column The last line (zero bound) in the slice (non
372 * inclusive). Default is the number of lines in the script
373 * @return {SourceSlice} The source slice or null of the parameters where
374 * invalid
375 */
376Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
377 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line;
378 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line
379
380 // Adjust according to the offset within the resource.
381 from_line -= this.line_offset;
382 to_line -= this.line_offset;
383 if (from_line < 0) from_line = 0;
384 if (to_line > this.lineCount()) to_line = this.lineCount();
385
386 // Check parameters.
387 if (from_line >= this.lineCount() ||
388 to_line < 0 ||
389 from_line > to_line) {
390 return null;
391 }
392
Steve Blockd0582a62009-12-15 09:54:21 +0000393 var line_ends = this.line_ends;
394 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
395 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
Steve Blocka7e24c12009-10-30 11:49:00 +0000396
397 // Return a source slice with line numbers re-adjusted to the resource.
398 return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset,
399 from_position, to_position);
400}
401
402
403Script.prototype.sourceLine = function (opt_line) {
404 // Default is the first line in the script. Lines in the script are relative
405 // to the offset within the resource.
406 var line = 0;
407 if (!IS_UNDEFINED(opt_line)) {
408 line = opt_line - this.line_offset;
409 }
410
411 // Check parameter.
412 if (line < 0 || this.lineCount() <= line) {
413 return null;
414 }
415
416 // Return the source line.
Steve Blockd0582a62009-12-15 09:54:21 +0000417 var line_ends = this.line_ends;
418 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
419 var end = line_ends[line];
Steve Blocka7e24c12009-10-30 11:49:00 +0000420 return StringSubstring.call(this.source, start, end);
421}
422
423
424/**
425 * Returns the number of source lines.
426 * @return {number}
427 * Number of source lines.
428 */
429Script.prototype.lineCount = function() {
430 // Return number of source lines.
431 return this.line_ends.length;
432};
433
434
435/**
Steve Block6ded16b2010-05-10 14:33:55 +0100436 * Returns the name of script if available, contents of sourceURL comment
437 * otherwise. See
438 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
439 * for details on using //@ sourceURL comment to identify scritps that don't
440 * have name.
441 *
442 * @return {?string} script name if present, value for //@ sourceURL comment
443 * otherwise.
444 */
445Script.prototype.nameOrSourceURL = function() {
446 if (this.name)
447 return this.name;
448 // TODO(608): the spaces in a regexp below had to be escaped as \040
449 // because this file is being processed by js2c whose handling of spaces
450 // in regexps is broken. Also, ['"] are excluded from allowed URLs to
451 // avoid matches against sources that invoke evals with sourceURL.
452 var sourceUrlPattern =
453 /\/\/@[\040\t]sourceURL=[\040\t]*([^\s'"]*)[\040\t]*$/m;
454 var match = sourceUrlPattern.exec(this.source);
455 return match ? match[1] : this.name;
456}
457
458
459/**
Steve Blocka7e24c12009-10-30 11:49:00 +0000460 * Class for source location. A source location is a position within some
461 * source with the following properties:
462 * script : script object for the source
463 * line : source line number
464 * column : source column within the line
465 * position : position within the source
466 * start : position of start of source context (inclusive)
467 * end : position of end of source context (not inclusive)
468 * Source text for the source context is the character interval [start, end[. In
469 * most cases end will point to a newline character. It might point just past
470 * the final position of the source if the last source line does not end with a
471 * newline character.
472 * @param {Script} script The Script object for which this is a location
473 * @param {number} position Source position for the location
474 * @param {number} line The line number for the location
475 * @param {number} column The column within the line for the location
476 * @param {number} start Source position for start of source context
477 * @param {number} end Source position for end of source context
478 * @constructor
479 */
480function SourceLocation(script, position, line, column, start, end) {
481 this.script = script;
482 this.position = position;
483 this.line = line;
484 this.column = column;
485 this.start = start;
486 this.end = end;
487}
488
489
490const kLineLengthLimit = 78;
491
492/**
493 * Restrict source location start and end positions to make the source slice
494 * no more that a certain number of characters wide.
495 * @param {number} opt_limit The with limit of the source text with a default
496 * of 78
497 * @param {number} opt_before The number of characters to prefer before the
498 * position with a default value of 10 less that the limit
499 */
500SourceLocation.prototype.restrict = function (opt_limit, opt_before) {
501 // Find the actual limit to use.
502 var limit;
503 var before;
504 if (!IS_UNDEFINED(opt_limit)) {
505 limit = opt_limit;
506 } else {
507 limit = kLineLengthLimit;
508 }
509 if (!IS_UNDEFINED(opt_before)) {
510 before = opt_before;
511 } else {
512 // If no before is specified center for small limits and perfer more source
513 // before the the position that after for longer limits.
514 if (limit <= 20) {
515 before = $floor(limit / 2);
516 } else {
517 before = limit - 10;
518 }
519 }
520 if (before >= limit) {
521 before = limit - 1;
522 }
523
524 // If the [start, end[ interval is too big we restrict
525 // it in one or both ends. We make sure to always produce
526 // restricted intervals of maximum allowed size.
527 if (this.end - this.start > limit) {
528 var start_limit = this.position - before;
529 var end_limit = this.position + limit - before;
530 if (this.start < start_limit && end_limit < this.end) {
531 this.start = start_limit;
532 this.end = end_limit;
533 } else if (this.start < start_limit) {
534 this.start = this.end - limit;
535 } else {
536 this.end = this.start + limit;
537 }
538 }
539};
540
541
542/**
543 * Get the source text for a SourceLocation
544 * @return {String}
545 * Source text for this location.
546 */
547SourceLocation.prototype.sourceText = function () {
548 return StringSubstring.call(this.script.source, this.start, this.end);
549};
550
551
552/**
553 * Class for a source slice. A source slice is a part of a script source with
554 * the following properties:
555 * script : script object for the source
556 * from_line : line number for the first line in the slice
557 * to_line : source line number for the last line in the slice
558 * from_position : position of the first character in the slice
559 * to_position : position of the last character in the slice
560 * The to_line and to_position are not included in the slice, that is the lines
561 * in the slice are [from_line, to_line[. Likewise the characters in the slice
562 * are [from_position, to_position[.
563 * @param {Script} script The Script object for the source slice
564 * @param {number} from_line
565 * @param {number} to_line
566 * @param {number} from_position
567 * @param {number} to_position
568 * @constructor
569 */
570function SourceSlice(script, from_line, to_line, from_position, to_position) {
571 this.script = script;
572 this.from_line = from_line;
573 this.to_line = to_line;
574 this.from_position = from_position;
575 this.to_position = to_position;
576}
577
578
579/**
580 * Get the source text for a SourceSlice
581 * @return {String} Source text for this slice. The last line will include
582 * the line terminating characters (if any)
583 */
584SourceSlice.prototype.sourceText = function () {
585 return StringSubstring.call(this.script.source, this.from_position, this.to_position);
586};
587
588
589// Returns the offset of the given position within the containing
590// line.
591function GetPositionInLine(message) {
592 var location = message.script.locationFromPosition(message.startPos, false);
593 if (location == null) return -1;
594 location.restrict();
595 return message.startPos - location.start;
596}
597
598
599function ErrorMessage(type, args, startPos, endPos, script, stackTrace) {
600 this.startPos = startPos;
601 this.endPos = endPos;
602 this.type = type;
603 this.args = args;
604 this.script = script;
605 this.stackTrace = stackTrace;
606}
607
608
609function MakeMessage(type, args, startPos, endPos, script, stackTrace) {
610 return new ErrorMessage(type, args, startPos, endPos, script, stackTrace);
611}
612
613
614function GetStackTraceLine(recv, fun, pos, isGlobal) {
615 return FormatSourcePosition(new CallSite(recv, fun, pos));
616}
617
618// ----------------------------------------------------------------------------
619// Error implementation
620
621// Defines accessors for a property that is calculated the first time
622// the property is read.
623function DefineOneShotAccessor(obj, name, fun) {
624 // Note that the accessors consistently operate on 'obj', not 'this'.
625 // Since the object may occur in someone else's prototype chain we
626 // can't rely on 'this' being the same as 'obj'.
627 var hasBeenSet = false;
628 var value;
629 obj.__defineGetter__(name, function () {
630 if (hasBeenSet) {
631 return value;
632 }
633 hasBeenSet = true;
634 value = fun(obj);
635 return value;
636 });
637 obj.__defineSetter__(name, function (v) {
638 hasBeenSet = true;
639 value = v;
640 });
641}
642
643function CallSite(receiver, fun, pos) {
644 this.receiver = receiver;
645 this.fun = fun;
646 this.pos = pos;
647}
648
649CallSite.prototype.getThis = function () {
650 return this.receiver;
651};
652
653CallSite.prototype.getTypeName = function () {
654 var constructor = this.receiver.constructor;
655 if (!constructor)
656 return $Object.prototype.toString.call(this.receiver);
657 var constructorName = constructor.name;
658 if (!constructorName)
659 return $Object.prototype.toString.call(this.receiver);
660 return constructorName;
661};
662
663CallSite.prototype.isToplevel = function () {
664 if (this.receiver == null)
665 return true;
666 return IS_GLOBAL(this.receiver);
667};
668
669CallSite.prototype.isEval = function () {
670 var script = %FunctionGetScript(this.fun);
Andrei Popescu31002712010-02-23 13:46:05 +0000671 return script && script.compilation_type == COMPILATION_TYPE_EVAL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000672};
673
674CallSite.prototype.getEvalOrigin = function () {
675 var script = %FunctionGetScript(this.fun);
Steve Blockd0582a62009-12-15 09:54:21 +0000676 return FormatEvalOrigin(script);
Steve Blocka7e24c12009-10-30 11:49:00 +0000677};
678
679CallSite.prototype.getFunction = function () {
680 return this.fun;
681};
682
683CallSite.prototype.getFunctionName = function () {
684 // See if the function knows its own name
685 var name = this.fun.name;
686 if (name) {
687 return name;
688 } else {
689 return %FunctionGetInferredName(this.fun);
690 }
691 // Maybe this is an evaluation?
692 var script = %FunctionGetScript(this.fun);
Andrei Popescu31002712010-02-23 13:46:05 +0000693 if (script && script.compilation_type == COMPILATION_TYPE_EVAL)
Steve Blocka7e24c12009-10-30 11:49:00 +0000694 return "eval";
695 return null;
696};
697
698CallSite.prototype.getMethodName = function () {
699 // See if we can find a unique property on the receiver that holds
700 // this function.
701 var ownName = this.fun.name;
702 if (ownName && this.receiver && this.receiver[ownName] === this.fun)
703 // To handle DontEnum properties we guess that the method has
704 // the same name as the function.
705 return ownName;
706 var name = null;
707 for (var prop in this.receiver) {
708 if (this.receiver[prop] === this.fun) {
709 // If we find more than one match bail out to avoid confusion
710 if (name)
711 return null;
712 name = prop;
713 }
714 }
715 if (name)
716 return name;
717 return null;
718};
719
720CallSite.prototype.getFileName = function () {
721 var script = %FunctionGetScript(this.fun);
722 return script ? script.name : null;
723};
724
725CallSite.prototype.getLineNumber = function () {
726 if (this.pos == -1)
727 return null;
728 var script = %FunctionGetScript(this.fun);
729 var location = null;
730 if (script) {
731 location = script.locationFromPosition(this.pos, true);
732 }
733 return location ? location.line + 1 : null;
734};
735
736CallSite.prototype.getColumnNumber = function () {
737 if (this.pos == -1)
738 return null;
739 var script = %FunctionGetScript(this.fun);
740 var location = null;
741 if (script) {
742 location = script.locationFromPosition(this.pos, true);
743 }
Steve Blockd0582a62009-12-15 09:54:21 +0000744 return location ? location.column + 1: null;
Steve Blocka7e24c12009-10-30 11:49:00 +0000745};
746
747CallSite.prototype.isNative = function () {
748 var script = %FunctionGetScript(this.fun);
Andrei Popescu31002712010-02-23 13:46:05 +0000749 return script ? (script.type == TYPE_NATIVE) : false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000750};
751
752CallSite.prototype.getPosition = function () {
753 return this.pos;
754};
755
756CallSite.prototype.isConstructor = function () {
757 var constructor = this.receiver ? this.receiver.constructor : null;
758 if (!constructor)
759 return false;
760 return this.fun === constructor;
761};
762
Steve Blockd0582a62009-12-15 09:54:21 +0000763function FormatEvalOrigin(script) {
764 var eval_origin = "";
765 if (script.eval_from_function_name) {
766 eval_origin += script.eval_from_function_name;
767 } else {
768 eval_origin += "<anonymous>";
769 }
Steve Block6ded16b2010-05-10 14:33:55 +0100770
Steve Blockd0582a62009-12-15 09:54:21 +0000771 var eval_from_script = script.eval_from_script;
772 if (eval_from_script) {
Andrei Popescu31002712010-02-23 13:46:05 +0000773 if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
Steve Blockd0582a62009-12-15 09:54:21 +0000774 // eval script originated from another eval.
775 eval_origin += " (eval at " + FormatEvalOrigin(eval_from_script) + ")";
776 } else {
777 // eval script originated from "real" scource.
778 if (eval_from_script.name) {
779 eval_origin += " (" + eval_from_script.name;
780 var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true);
781 if (location) {
782 eval_origin += ":" + (location.line + 1);
783 eval_origin += ":" + (location.column + 1);
784 }
785 eval_origin += ")"
786 } else {
787 eval_origin += " (unknown source)";
788 }
789 }
790 }
Steve Block6ded16b2010-05-10 14:33:55 +0100791
Steve Blockd0582a62009-12-15 09:54:21 +0000792 return eval_origin;
793};
794
Steve Blocka7e24c12009-10-30 11:49:00 +0000795function FormatSourcePosition(frame) {
796 var fileLocation = "";
797 if (frame.isNative()) {
798 fileLocation = "native";
799 } else if (frame.isEval()) {
Steve Blockd0582a62009-12-15 09:54:21 +0000800 fileLocation = "eval at " + frame.getEvalOrigin();
Steve Blocka7e24c12009-10-30 11:49:00 +0000801 } else {
802 var fileName = frame.getFileName();
803 if (fileName) {
804 fileLocation += fileName;
805 var lineNumber = frame.getLineNumber();
806 if (lineNumber != null) {
807 fileLocation += ":" + lineNumber;
808 var columnNumber = frame.getColumnNumber();
809 if (columnNumber) {
810 fileLocation += ":" + columnNumber;
811 }
812 }
813 }
814 }
815 if (!fileLocation) {
816 fileLocation = "unknown source";
817 }
818 var line = "";
819 var functionName = frame.getFunction().name;
820 var methodName = frame.getMethodName();
821 var addPrefix = true;
822 var isConstructor = frame.isConstructor();
823 var isMethodCall = !(frame.isToplevel() || isConstructor);
824 if (isMethodCall) {
825 line += frame.getTypeName() + ".";
826 if (functionName) {
827 line += functionName;
828 if (methodName && (methodName != functionName)) {
829 line += " [as " + methodName + "]";
830 }
831 } else {
832 line += methodName || "<anonymous>";
833 }
834 } else if (isConstructor) {
835 line += "new " + (functionName || "<anonymous>");
836 } else if (functionName) {
837 line += functionName;
838 } else {
839 line += fileLocation;
840 addPrefix = false;
841 }
842 if (addPrefix) {
843 line += " (" + fileLocation + ")";
844 }
845 return line;
846}
847
848function FormatStackTrace(error, frames) {
849 var lines = [];
850 try {
851 lines.push(error.toString());
852 } catch (e) {
853 try {
854 lines.push("<error: " + e + ">");
855 } catch (ee) {
856 lines.push("<error>");
857 }
858 }
859 for (var i = 0; i < frames.length; i++) {
860 var frame = frames[i];
861 var line;
862 try {
863 line = FormatSourcePosition(frame);
864 } catch (e) {
865 try {
866 line = "<error: " + e + ">";
867 } catch (ee) {
868 // Any code that reaches this point is seriously nasty!
869 line = "<error>";
870 }
871 }
872 lines.push(" at " + line);
873 }
874 return lines.join("\n");
875}
876
877function FormatRawStackTrace(error, raw_stack) {
878 var frames = [ ];
879 for (var i = 0; i < raw_stack.length; i += 3) {
880 var recv = raw_stack[i];
881 var fun = raw_stack[i+1];
882 var pc = raw_stack[i+2];
883 var pos = %FunctionGetPositionForOffset(fun, pc);
884 frames.push(new CallSite(recv, fun, pos));
885 }
886 if (IS_FUNCTION($Error.prepareStackTrace)) {
887 return $Error.prepareStackTrace(error, frames);
888 } else {
889 return FormatStackTrace(error, frames);
890 }
891}
892
893function DefineError(f) {
894 // Store the error function in both the global object
895 // and the runtime object. The function is fetched
896 // from the runtime object when throwing errors from
897 // within the runtime system to avoid strange side
898 // effects when overwriting the error functions from
899 // user code.
900 var name = f.name;
901 %SetProperty(global, name, f, DONT_ENUM);
902 this['$' + name] = f;
903 // Configure the error function.
904 if (name == 'Error') {
905 // The prototype of the Error object must itself be an error.
906 // However, it can't be an instance of the Error object because
907 // it hasn't been properly configured yet. Instead we create a
908 // special not-a-true-error-but-close-enough object.
909 function ErrorPrototype() {}
910 %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
911 %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
912 %FunctionSetPrototype(f, new ErrorPrototype());
913 } else {
914 %FunctionSetPrototype(f, new $Error());
915 }
916 %FunctionSetInstanceClassName(f, 'Error');
917 %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
918 f.prototype.name = name;
919 %SetCode(f, function(m) {
920 if (%_IsConstructCall()) {
921 if (m === kAddMessageAccessorsMarker) {
922 DefineOneShotAccessor(this, 'message', function (obj) {
923 return FormatMessage({type: obj.type, args: obj.arguments});
924 });
925 } else if (!IS_UNDEFINED(m)) {
926 this.message = ToString(m);
927 }
928 captureStackTrace(this, f);
929 } else {
930 return new f(m);
931 }
932 });
933}
934
935function captureStackTrace(obj, cons_opt) {
936 var stackTraceLimit = $Error.stackTraceLimit;
937 if (!stackTraceLimit) return;
938 if (stackTraceLimit < 0 || stackTraceLimit > 10000)
939 stackTraceLimit = 10000;
940 var raw_stack = %CollectStackTrace(cons_opt ? cons_opt : captureStackTrace,
941 stackTraceLimit);
942 DefineOneShotAccessor(obj, 'stack', function (obj) {
943 return FormatRawStackTrace(obj, raw_stack);
944 });
945};
946
947$Math.__proto__ = global.Object.prototype;
948
949DefineError(function Error() { });
950DefineError(function TypeError() { });
951DefineError(function RangeError() { });
952DefineError(function SyntaxError() { });
953DefineError(function ReferenceError() { });
954DefineError(function EvalError() { });
955DefineError(function URIError() { });
956
957$Error.captureStackTrace = captureStackTrace;
958
959// Setup extra properties of the Error.prototype object.
960$Error.prototype.message = '';
961
962%SetProperty($Error.prototype, 'toString', function toString() {
963 var type = this.type;
964 if (type && !this.hasOwnProperty("message")) {
965 return this.name + ": " + FormatMessage({ type: type, args: this.arguments });
966 }
967 var message = this.message;
968 return this.name + (message ? (": " + message) : "");
969}, DONT_ENUM);
970
971
972// Boilerplate for exceptions for stack overflows. Used from
973// Top::StackOverflow().
974const kStackOverflowBoilerplate = MakeRangeError('stack_overflow', []);