v8natives.js revision d0582a6c46733687d045e4188a1bcd0123c758a1
1// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// This file relies on the fact that the following declarations have been made
29//
30// in runtime.js:
31// const $Object = global.Object;
32// const $Boolean = global.Boolean;
33// const $Number = global.Number;
34// const $Function = global.Function;
35// const $Array = global.Array;
36// const $NaN = 0/0;
37//
38// in math.js:
39// const $floor = MathFloor
40
41const $isNaN = GlobalIsNaN;
42const $isFinite = GlobalIsFinite;
43
44// ----------------------------------------------------------------------------
45
46
47// Helper function used to install functions on objects.
48function InstallFunctions(object, attributes, functions) {
49  if (functions.length >= 8) {
50    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
51  }
52  for (var i = 0; i < functions.length; i += 2) {
53    var key = functions[i];
54    var f = functions[i + 1];
55    %FunctionSetName(f, key);
56    %SetProperty(object, key, f, attributes);
57  }
58  %TransformToFastProperties(object);
59}
60
61// Emulates JSC by installing functions on a hidden prototype that
62// lies above the current object/prototype.  This lets you override
63// functions on String.prototype etc. and then restore the old function
64// with delete.  See http://code.google.com/p/chromium/issues/detail?id=1717
65function InstallFunctionsOnHiddenPrototype(object, attributes, functions) {
66  var hidden_prototype = new $Object();
67  %SetHiddenPrototype(object, hidden_prototype);
68  InstallFunctions(hidden_prototype, attributes, functions);
69}
70
71
72// ----------------------------------------------------------------------------
73
74
75// ECMA 262 - 15.1.4
76function GlobalIsNaN(number) {
77  var n = ToNumber(number);
78  return NUMBER_IS_NAN(n);
79}
80
81
82// ECMA 262 - 15.1.5
83function GlobalIsFinite(number) {
84  return %NumberIsFinite(ToNumber(number));
85}
86
87
88// ECMA-262 - 15.1.2.2
89function GlobalParseInt(string, radix) {
90  if (radix === void 0) {
91    // Some people use parseInt instead of Math.floor.  This
92    // optimization makes parseInt on a Smi 12 times faster (60ns
93    // vs 800ns).  The following optimization makes parseInt on a
94    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
95    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
96    if (%_IsSmi(string)) return string;
97    if (IS_NUMBER(string) &&
98        ((0.01 < string && string < 1e9) ||
99            (-1e9 < string && string < -0.01))) {
100      // Truncate number.
101      return string | 0;
102    }
103    radix = 0;
104  } else {
105    radix = TO_INT32(radix);
106    if (!(radix == 0 || (2 <= radix && radix <= 36)))
107      return $NaN;
108  }
109  return %StringParseInt(ToString(string), radix);
110}
111
112
113// ECMA-262 - 15.1.2.3
114function GlobalParseFloat(string) {
115  return %StringParseFloat(ToString(string));
116}
117
118
119function GlobalEval(x) {
120  if (!IS_STRING(x)) return x;
121
122  var global_receiver = %GlobalReceiver(global);
123  var this_is_global_receiver = (this === global_receiver);
124  var global_is_detached = (global === global_receiver);
125
126  if (!this_is_global_receiver || global_is_detached) {
127    throw new $EvalError('The "this" object passed to eval must ' +
128                         'be the global object from which eval originated');
129  }
130
131  var f = %CompileString(x, false);
132  if (!IS_FUNCTION(f)) return f;
133
134  return f.call(this);
135}
136
137
138// execScript for IE compatibility.
139function GlobalExecScript(expr, lang) {
140  // NOTE: We don't care about the character casing.
141  if (!lang || /javascript/i.test(lang)) {
142    var f = %CompileString(ToString(expr), false);
143    f.call(%GlobalReceiver(global));
144  }
145  return null;
146}
147
148
149// ----------------------------------------------------------------------------
150
151
152function SetupGlobal() {
153  // ECMA 262 - 15.1.1.1.
154  %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE);
155
156  // ECMA-262 - 15.1.1.2.
157  %SetProperty(global, "Infinity", 1/0, DONT_ENUM | DONT_DELETE);
158
159  // ECMA-262 - 15.1.1.3.
160  %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE);
161
162  // Setup non-enumerable function on the global object.
163  InstallFunctions(global, DONT_ENUM, $Array(
164    "isNaN", GlobalIsNaN,
165    "isFinite", GlobalIsFinite,
166    "parseInt", GlobalParseInt,
167    "parseFloat", GlobalParseFloat,
168    "eval", GlobalEval,
169    "execScript", GlobalExecScript
170  ));
171}
172
173SetupGlobal();
174
175
176// ----------------------------------------------------------------------------
177// Boolean (first part of definition)
178
179
180%SetCode($Boolean, function(x) {
181  if (%_IsConstructCall()) {
182    %_SetValueOf(this, ToBoolean(x));
183  } else {
184    return ToBoolean(x);
185  }
186});
187
188%FunctionSetPrototype($Boolean, new $Boolean(false));
189
190%SetProperty($Boolean.prototype, "constructor", $Boolean, DONT_ENUM);
191
192// ----------------------------------------------------------------------------
193// Object
194
195$Object.prototype.constructor = $Object;
196
197// ECMA-262 - 15.2.4.2
198function ObjectToString() {
199  return "[object " + %_ClassOf(this) + "]";
200}
201
202
203// ECMA-262 - 15.2.4.3
204function ObjectToLocaleString() {
205  return this.toString();
206}
207
208
209// ECMA-262 - 15.2.4.4
210function ObjectValueOf() {
211  return this;
212}
213
214
215// ECMA-262 - 15.2.4.5
216function ObjectHasOwnProperty(V) {
217  return %HasLocalProperty(ToObject(this), ToString(V));
218}
219
220
221// ECMA-262 - 15.2.4.6
222function ObjectIsPrototypeOf(V) {
223  if (!IS_OBJECT(V) && !IS_FUNCTION(V)) return false;
224  return %IsInPrototypeChain(this, V);
225}
226
227
228// ECMA-262 - 15.2.4.6
229function ObjectPropertyIsEnumerable(V) {
230  if (this == null) return false;
231  if (!IS_OBJECT(this) && !IS_FUNCTION(this)) return false;
232  return %IsPropertyEnumerable(this, ToString(V));
233}
234
235
236// Extensions for providing property getters and setters.
237function ObjectDefineGetter(name, fun) {
238  if (this == null) {
239    throw new $TypeError('Object.prototype.__defineGetter__: this is Null');
240  }
241  if (!IS_FUNCTION(fun)) {
242    throw new $TypeError('Object.prototype.__defineGetter__: Expecting function');
243  }
244  return %DefineAccessor(ToObject(this), ToString(name), GETTER, fun);
245}
246
247
248function ObjectLookupGetter(name) {
249  if (this == null) {
250    throw new $TypeError('Object.prototype.__lookupGetter__: this is Null');
251  }
252  return %LookupAccessor(ToObject(this), ToString(name), GETTER);
253}
254
255
256function ObjectDefineSetter(name, fun) {
257  if (this == null) {
258    throw new $TypeError('Object.prototype.__defineSetter__: this is Null');
259  }
260  if (!IS_FUNCTION(fun)) {
261    throw new $TypeError(
262        'Object.prototype.__defineSetter__: Expecting function');
263  }
264  return %DefineAccessor(ToObject(this), ToString(name), SETTER, fun);
265}
266
267
268function ObjectLookupSetter(name) {
269  if (this == null) {
270    throw new $TypeError('Object.prototype.__lookupSetter__: this is Null');
271  }
272  return %LookupAccessor(ToObject(this), ToString(name), SETTER);
273}
274
275
276function ObjectKeys(obj) {
277  if ((!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj)) && !IS_FUNCTION(obj))
278    throw MakeTypeError('object_keys_non_object', [obj]);
279  return %LocalKeys(obj);
280}
281
282
283%SetCode($Object, function(x) {
284  if (%_IsConstructCall()) {
285    if (x == null) return this;
286    return ToObject(x);
287  } else {
288    if (x == null) return { };
289    return ToObject(x);
290  }
291});
292
293
294// ----------------------------------------------------------------------------
295
296
297function SetupObject() {
298  // Setup non-enumerable functions on the Object.prototype object.
299  InstallFunctions($Object.prototype, DONT_ENUM, $Array(
300    "toString", ObjectToString,
301    "toLocaleString", ObjectToLocaleString,
302    "valueOf", ObjectValueOf,
303    "hasOwnProperty", ObjectHasOwnProperty,
304    "isPrototypeOf", ObjectIsPrototypeOf,
305    "propertyIsEnumerable", ObjectPropertyIsEnumerable,
306    "__defineGetter__", ObjectDefineGetter,
307    "__lookupGetter__", ObjectLookupGetter,
308    "__defineSetter__", ObjectDefineSetter,
309    "__lookupSetter__", ObjectLookupSetter
310  ));
311  InstallFunctions($Object, DONT_ENUM, $Array(
312    "keys", ObjectKeys
313  ));
314}
315
316SetupObject();
317
318
319// ----------------------------------------------------------------------------
320// Boolean
321
322function BooleanToString() {
323  // NOTE: Both Boolean objects and values can enter here as
324  // 'this'. This is not as dictated by ECMA-262.
325  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
326    throw new $TypeError('Boolean.prototype.toString is not generic');
327  return ToString(%_ValueOf(this));
328}
329
330
331function BooleanValueOf() {
332  // NOTE: Both Boolean objects and values can enter here as
333  // 'this'. This is not as dictated by ECMA-262.
334  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this))
335    throw new $TypeError('Boolean.prototype.valueOf is not generic');
336  return %_ValueOf(this);
337}
338
339
340function BooleanToJSON(key) {
341  return CheckJSONPrimitive(this.valueOf());
342}
343
344
345// ----------------------------------------------------------------------------
346
347
348function SetupBoolean() {
349  InstallFunctions($Boolean.prototype, DONT_ENUM, $Array(
350    "toString", BooleanToString,
351    "valueOf", BooleanValueOf,
352    "toJSON", BooleanToJSON
353  ));
354}
355
356SetupBoolean();
357
358// ----------------------------------------------------------------------------
359// Number
360
361// Set the Number function and constructor.
362%SetCode($Number, function(x) {
363  var value = %_ArgumentsLength() == 0 ? 0 : ToNumber(x);
364  if (%_IsConstructCall()) {
365    %_SetValueOf(this, value);
366  } else {
367    return value;
368  }
369});
370
371%FunctionSetPrototype($Number, new $Number(0));
372
373// ECMA-262 section 15.7.4.2.
374function NumberToString(radix) {
375  // NOTE: Both Number objects and values can enter here as
376  // 'this'. This is not as dictated by ECMA-262.
377  var number = this;
378  if (!IS_NUMBER(this)) {
379    if (!IS_NUMBER_WRAPPER(this))
380      throw new $TypeError('Number.prototype.toString is not generic');
381    // Get the value of this number in case it's an object.
382    number = %_ValueOf(this);
383  }
384  // Fast case: Convert number in radix 10.
385  if (IS_UNDEFINED(radix) || radix === 10) {
386    return ToString(number);
387  }
388
389  // Convert the radix to an integer and check the range.
390  radix = TO_INTEGER(radix);
391  if (radix < 2 || radix > 36) {
392    throw new $RangeError('toString() radix argument must be between 2 and 36');
393  }
394  // Convert the number to a string in the given radix.
395  return %NumberToRadixString(number, radix);
396}
397
398
399// ECMA-262 section 15.7.4.3
400function NumberToLocaleString() {
401  return this.toString();
402}
403
404
405// ECMA-262 section 15.7.4.4
406function NumberValueOf() {
407  // NOTE: Both Number objects and values can enter here as
408  // 'this'. This is not as dictated by ECMA-262.
409  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this))
410    throw new $TypeError('Number.prototype.valueOf is not generic');
411  return %_ValueOf(this);
412}
413
414
415// ECMA-262 section 15.7.4.5
416function NumberToFixed(fractionDigits) {
417  var f = TO_INTEGER(fractionDigits);
418  if (f < 0 || f > 20) {
419    throw new $RangeError("toFixed() digits argument must be between 0 and 20");
420  }
421  var x = ToNumber(this);
422  return %NumberToFixed(x, f);
423}
424
425
426// ECMA-262 section 15.7.4.6
427function NumberToExponential(fractionDigits) {
428  var f = -1;
429  if (!IS_UNDEFINED(fractionDigits)) {
430    f = TO_INTEGER(fractionDigits);
431    if (f < 0 || f > 20) {
432      throw new $RangeError("toExponential() argument must be between 0 and 20");
433    }
434  }
435  var x = ToNumber(this);
436  return %NumberToExponential(x, f);
437}
438
439
440// ECMA-262 section 15.7.4.7
441function NumberToPrecision(precision) {
442  if (IS_UNDEFINED(precision)) return ToString(%_ValueOf(this));
443  var p = TO_INTEGER(precision);
444  if (p < 1 || p > 21) {
445    throw new $RangeError("toPrecision() argument must be between 1 and 21");
446  }
447  var x = ToNumber(this);
448  return %NumberToPrecision(x, p);
449}
450
451
452function CheckJSONPrimitive(val) {
453  if (!IsPrimitive(val))
454    throw MakeTypeError('result_not_primitive', ['toJSON', val]);
455  return val;
456}
457
458
459function NumberToJSON(key) {
460  return CheckJSONPrimitive(this.valueOf());
461}
462
463
464// ----------------------------------------------------------------------------
465
466function SetupNumber() {
467  %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8);
468  // Setup the constructor property on the Number prototype object.
469  %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM);
470
471  %OptimizeObjectForAddingMultipleProperties($Number, 5);
472  // ECMA-262 section 15.7.3.1.
473  %SetProperty($Number,
474               "MAX_VALUE",
475               1.7976931348623157e+308,
476               DONT_ENUM | DONT_DELETE | READ_ONLY);
477
478  // ECMA-262 section 15.7.3.2.
479  %SetProperty($Number, "MIN_VALUE", 5e-324, DONT_ENUM | DONT_DELETE | READ_ONLY);
480
481  // ECMA-262 section 15.7.3.3.
482  %SetProperty($Number, "NaN", $NaN, DONT_ENUM | DONT_DELETE | READ_ONLY);
483
484  // ECMA-262 section 15.7.3.4.
485  %SetProperty($Number,
486               "NEGATIVE_INFINITY",
487               -1/0,
488               DONT_ENUM | DONT_DELETE | READ_ONLY);
489
490  // ECMA-262 section 15.7.3.5.
491  %SetProperty($Number,
492               "POSITIVE_INFINITY",
493               1/0,
494               DONT_ENUM | DONT_DELETE | READ_ONLY);
495  %TransformToFastProperties($Number);
496
497  // Setup non-enumerable functions on the Number prototype object.
498  InstallFunctions($Number.prototype, DONT_ENUM, $Array(
499    "toString", NumberToString,
500    "toLocaleString", NumberToLocaleString,
501    "valueOf", NumberValueOf,
502    "toFixed", NumberToFixed,
503    "toExponential", NumberToExponential,
504    "toPrecision", NumberToPrecision,
505    "toJSON", NumberToJSON
506  ));
507}
508
509SetupNumber();
510
511
512
513// ----------------------------------------------------------------------------
514// Function
515
516$Function.prototype.constructor = $Function;
517
518function FunctionSourceString(func) {
519  if (!IS_FUNCTION(func)) {
520    throw new $TypeError('Function.prototype.toString is not generic');
521  }
522
523  var source = %FunctionGetSourceCode(func);
524  if (!IS_STRING(source) || %FunctionIsBuiltin(func)) {
525    var name = %FunctionGetName(func);
526    if (name) {
527      // Mimic what KJS does.
528      return 'function ' + name + '() { [native code] }';
529    } else {
530      return 'function () { [native code] }';
531    }
532  }
533
534  var name = %FunctionGetName(func);
535  return 'function ' + name + source;
536}
537
538
539function FunctionToString() {
540  return FunctionSourceString(this);
541}
542
543
544function NewFunction(arg1) {  // length == 1
545  var n = %_ArgumentsLength();
546  var p = '';
547  if (n > 1) {
548    p = new $Array(n - 1);
549    // Explicitly convert all parameters to strings.
550    // Array.prototype.join replaces null with empty strings which is
551    // not appropriate.
552    for (var i = 0; i < n - 1; i++) p[i] = ToString(%_Arguments(i));
553    p = p.join(',');
554    // If the formal parameters string include ) - an illegal
555    // character - it may make the combined function expression
556    // compile. We avoid this problem by checking for this early on.
557    if (p.indexOf(')') != -1) throw MakeSyntaxError('unable_to_parse',[]);
558  }
559  var body = (n > 0) ? ToString(%_Arguments(n - 1)) : '';
560  var source = '(function(' + p + ') {\n' + body + '\n})';
561
562  // The call to SetNewFunctionAttributes will ensure the prototype
563  // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
564  var f = %CompileString(source, false)();
565  %FunctionSetName(f, "anonymous");
566  return %SetNewFunctionAttributes(f);
567}
568
569%SetCode($Function, NewFunction);
570
571// ----------------------------------------------------------------------------
572
573function SetupFunction() {
574  InstallFunctions($Function.prototype, DONT_ENUM, $Array(
575    "toString", FunctionToString
576  ));
577}
578
579SetupFunction();
580