Upgrade V8 to 2.4.4
authorRyan Dahl <ry@tinyclouds.org>
Fri, 17 Sep 2010 04:33:32 +0000 (21:33 -0700)
committerRyan Dahl <ry@tinyclouds.org>
Fri, 17 Sep 2010 04:33:32 +0000 (21:33 -0700)
66 files changed:
deps/v8/AUTHORS
deps/v8/ChangeLog
deps/v8/benchmarks/crypto.js
deps/v8/benchmarks/deltablue.js
deps/v8/benchmarks/earley-boyer.js
deps/v8/benchmarks/raytrace.js
deps/v8/benchmarks/regexp.js
deps/v8/benchmarks/richards.js
deps/v8/benchmarks/splay.js
deps/v8/include/v8-profiler.h
deps/v8/include/v8.h
deps/v8/src/api.cc
deps/v8/src/arm/code-stubs-arm.cc
deps/v8/src/arm/codegen-arm.cc
deps/v8/src/arm/codegen-arm.h
deps/v8/src/arm/full-codegen-arm.cc
deps/v8/src/arm/macro-assembler-arm.cc
deps/v8/src/arm/macro-assembler-arm.h
deps/v8/src/arm/stub-cache-arm.cc
deps/v8/src/array.js
deps/v8/src/bootstrapper.cc
deps/v8/src/code-stubs.h
deps/v8/src/codegen.cc
deps/v8/src/codegen.h
deps/v8/src/compiler.cc
deps/v8/src/dateparser-inl.h
deps/v8/src/flag-definitions.h
deps/v8/src/full-codegen.cc
deps/v8/src/full-codegen.h
deps/v8/src/global-handles.cc
deps/v8/src/global-handles.h
deps/v8/src/handles.cc
deps/v8/src/heap-inl.h
deps/v8/src/heap.cc
deps/v8/src/heap.h
deps/v8/src/ia32/assembler-ia32.cc
deps/v8/src/ia32/assembler-ia32.h
deps/v8/src/ia32/code-stubs-ia32.cc
deps/v8/src/ia32/codegen-ia32.cc
deps/v8/src/ia32/codegen-ia32.h
deps/v8/src/ia32/full-codegen-ia32.cc
deps/v8/src/ia32/stub-cache-ia32.cc
deps/v8/src/liveedit.cc
deps/v8/src/parser.cc
deps/v8/src/parser.h
deps/v8/src/profile-generator.cc
deps/v8/src/profile-generator.h
deps/v8/src/runtime.cc
deps/v8/src/runtime.h
deps/v8/src/string-search.h [new file with mode: 0644]
deps/v8/src/stub-cache.cc
deps/v8/src/stub-cache.h
deps/v8/src/unicode.h
deps/v8/src/utils.h
deps/v8/src/utils.h.orig [new file with mode: 0644]
deps/v8/src/version.cc
deps/v8/src/x64/assembler-x64.cc
deps/v8/src/x64/code-stubs-x64.cc
deps/v8/src/x64/codegen-x64.cc
deps/v8/src/x64/codegen-x64.h
deps/v8/src/x64/full-codegen-x64.cc
deps/v8/src/x64/stub-cache-x64.cc
deps/v8/test/cctest/test-heap-profiler.cc
deps/v8/test/mjsunit/array-indexing.js
deps/v8/test/mjsunit/regress/regress-857.js [new file with mode: 0644]
deps/v8/test/mjsunit/string-fromcharcode.js [new file with mode: 0644]

index 65b8965..2403fbb 100644 (file)
@@ -9,6 +9,8 @@ ARM Ltd.
 
 Alexander Botero-Lowry <alexbl@FreeBSD.org>
 Alexandre Vassalotti <avassalotti@gmail.com>
+Andreas Anyuru <andreas.anyuru@gmail.com>
+Burcu Dogan <burcujdogan@gmail.com>
 Craig Schlenter <craig.schlenter@gmail.com>
 Daniel Andersson <kodandersson@gmail.com>
 Daniel James <dnljms@gmail.com>
@@ -21,6 +23,7 @@ John Jozwiak <jjozwiak@codeaurora.org>
 Kun Zhang <zhangk@codeaurora.org>
 Matt Hanselman <mjhanselman@gmail.com>
 Martyn Capewell <martyn.capewell@arm.com>
+Michael Smith <mike@w3.org>
 Paolo Giarrusso <p.giarrusso@gmail.com>
 Patrick Gansterer <paroga@paroga.com>
 Rafal Krypa <rafal@krypa.net>
@@ -28,6 +31,4 @@ Rene Rebe <rene@exactcode.de>
 Rodolph Perfetta <rodolph.perfetta@arm.com>
 Ryan Dahl <coldredlemur@gmail.com>
 Subrato K De <subratokde@codeaurora.org>
-Burcu Dogan <burcujdogan@gmail.com>
 Vlad Burlik <vladbph@gmail.com>
-
index 95a3640..c31f5fc 100644 (file)
@@ -1,3 +1,21 @@
+2010-09-15: Version 2.4.4
+
+        Fix bug with hangs on very large sparse arrays.
+
+        Try harder to free up memory when running out of space.
+
+        Add heap snapshots to JSON format to API.
+
+        Recalibrate benchmarks.
+
+
+2010-09-13: Version 2.4.3
+
+        Made Date.parse properly handle TZ offsets (issue 857).
+
+        Performance improvements on all platforms.
+
+
 2010-09-08: Version 2.4.2
 
         Fixed GC crash bug.
index 7e9829d..ffa69b5 100644 (file)
@@ -31,7 +31,7 @@
 
 
 // The code has been adapted for use as a benchmark by Google.
-var Crypto = new BenchmarkSuite('Crypto', 110465, [
+var Crypto = new BenchmarkSuite('Crypto', 266181, [
   new Benchmark("Encrypt", encrypt),
   new Benchmark("Decrypt", decrypt)
 ]);
index 4af8387..548fd96 100644 (file)
@@ -23,7 +23,7 @@
 // more like a JavaScript program.
 
 
-var DeltaBlue = new BenchmarkSuite('DeltaBlue', 30282, [
+var DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [
   new Benchmark('DeltaBlue', deltaBlue)
 ]);
 
index b1efe4a..1be480e 100644 (file)
@@ -1,7 +1,7 @@
 // This file is automatically generated by scheme2js, except for the
 // benchmark harness code at the beginning and end of the file.
 
-var EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 280581, [
+var EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 666463, [
   new Benchmark("Earley", function () { BgL_earleyzd2benchmarkzd2(); }),
   new Benchmark("Boyer", function () { BgL_nboyerzd2benchmarkzd2(); })
 ]);
index eaf61a1..971ef72 100644 (file)
@@ -8,7 +8,7 @@
 // untouched. This file also contains a copy of parts of the Prototype
 // JavaScript framework which is used by the ray tracer.
 
-var RayTrace = new BenchmarkSuite('RayTrace', 533115, [
+var RayTrace = new BenchmarkSuite('RayTrace', 739989, [
   new Benchmark('RayTrace', renderScene)
 ]);
 
index f9f816c..f760866 100644 (file)
@@ -35,7 +35,7 @@
 // letters in the data are encoded using ROT13 in a way that does not
 // affect how the regexps match their input.
 
-var RegRxp = new BenchmarkSuite('RegExp', 601250, [
+var RegRxp = new BenchmarkSuite('RegExp', 910985, [
   new Benchmark("RegExp", runRegExpBenchmark)
 ]);
 
index b5736f7..054928d 100644 (file)
@@ -35,7 +35,7 @@
 // Martin Richards.
 
 
-var Richards = new BenchmarkSuite('Richards', 20687, [
+var Richards = new BenchmarkSuite('Richards', 35302, [
   new Benchmark("Richards", runRichards)
 ]);
 
index d63ab8b..6b4f56d 100644 (file)
@@ -33,7 +33,7 @@
 // also has to deal with a lot of changes to the large tree object
 // graph.
 
-var Splay = new BenchmarkSuite('Splay', 21915, [
+var Splay = new BenchmarkSuite('Splay', 81491, [
   new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
 ]);
 
index dd1b8ca..27da418 100644 (file)
@@ -323,7 +323,10 @@ class V8EXPORT HeapSnapshot {
   enum Type {
     kFull = 0,       // Heap snapshot with all instances and references.
     kAggregated = 1  // Snapshot doesn't contain individual heap entries,
-                     //instead they are grouped by constructor name.
+                     // instead they are grouped by constructor name.
+  };
+  enum SerializationFormat {
+    kJSON = 0  // See format description near 'Serialize' method.
   };
 
   /** Returns heap snapshot type. */
@@ -343,6 +346,30 @@ class V8EXPORT HeapSnapshot {
    * of the same type can be compared.
    */
   const HeapSnapshotsDiff* CompareWith(const HeapSnapshot* snapshot) const;
+
+  /**
+   * Prepare a serialized representation of the snapshot. The result
+   * is written into the stream provided in chunks of specified size.
+   * The total length of the serialized snapshot is unknown in
+   * advance, it is can be roughly equal to JS heap size (that means,
+   * it can be really big - tens of megabytes).
+   *
+   * For the JSON format, heap contents are represented as an object
+   * with the following structure:
+   *
+   *  {
+   *    snapshot: {title: "...", uid: nnn},
+   *    nodes: [
+   *      meta-info (JSON string),
+   *      nodes themselves
+   *    ],
+   *    strings: [strings]
+   *  }
+   *
+   * Outgoing node links are stored after each node. Nodes reference strings
+   * and other nodes by their indexes in corresponding arrays.
+   */
+  void Serialize(OutputStream* stream, SerializationFormat format) const;
 };
 
 
index b89c244..0613d58 100644 (file)
@@ -3196,6 +3196,34 @@ class V8EXPORT Locker {
 };
 
 
+/**
+ * An interface for exporting data from V8, using "push" model.
+ */
+class V8EXPORT OutputStream {
+public:
+  enum OutputEncoding {
+    kAscii = 0  // 7-bit ASCII.
+  };
+  enum WriteResult {
+    kContinue = 0,
+    kAbort = 1
+  };
+  virtual ~OutputStream() {}
+  /** Notify about the end of stream. */
+  virtual void EndOfStream() = 0;
+  /** Get preferred output chunk size. Called only once. */
+  virtual int GetChunkSize() { return 1024; }
+  /** Get preferred output encoding. Called only once. */
+  virtual OutputEncoding GetOutputEncoding() { return kAscii; }
+  /**
+   * Writes the next chunk of snapshot data into the stream. Writing
+   * can be stopped by returning kAbort as function result. EndOfStream
+   * will not be called in case writing was aborted.
+   */
+  virtual WriteResult WriteAsciiChunk(char* data, int size) = 0;
+};
+
+
 
 // --- I m p l e m e n t a t i o n ---
 
index 0d01fcc..e09d4c9 100644 (file)
@@ -4739,6 +4739,23 @@ const HeapSnapshotsDiff* HeapSnapshot::CompareWith(
 }
 
 
+void HeapSnapshot::Serialize(OutputStream* stream,
+                             HeapSnapshot::SerializationFormat format) const {
+  IsDeadCheck("v8::HeapSnapshot::Serialize");
+  ApiCheck(format == kJSON,
+           "v8::HeapSnapshot::Serialize",
+           "Unknown serialization format");
+  ApiCheck(stream->GetOutputEncoding() == OutputStream::kAscii,
+           "v8::HeapSnapshot::Serialize",
+           "Unsupported output encoding");
+  ApiCheck(stream->GetChunkSize() > 0,
+           "v8::HeapSnapshot::Serialize",
+           "Invalid stream chunk size");
+  i::HeapSnapshotJSONSerializer serializer(ToInternal(this));
+  serializer.Serialize(stream);
+}
+
+
 int HeapProfiler::GetSnapshotsCount() {
   IsDeadCheck("v8::HeapProfiler::GetSnapshotsCount");
   return i::HeapProfiler::GetSnapshotsCount();
index fa93030..8f801cf 100644 (file)
@@ -930,6 +930,24 @@ void CompareStub::Generate(MacroAssembler* masm) {
   Label slow;  // Call builtin.
   Label not_smis, both_loaded_as_doubles, lhs_not_nan;
 
+  if (include_smi_compare_) {
+    Label not_two_smis, smi_done;
+    __ orr(r2, r1, r0);
+    __ tst(r2, Operand(kSmiTagMask));
+    __ b(ne, &not_two_smis);
+    __ sub(r0, r1, r0);
+    __ b(vc, &smi_done);
+    // Correct the sign in case of overflow.
+    __ rsb(r0, r0, Operand(0, RelocInfo::NONE));
+    __ bind(&smi_done);
+    __ Ret();
+    __ bind(&not_two_smis);
+  } else if (FLAG_debug_code) {
+    __ orr(r2, r1, r0);
+    __ tst(r2, Operand(kSmiTagMask));
+    __ Assert(nz, "CompareStub: unexpected smi operands.");
+  }
+
   // NOTICE! This code is only reached after a smi-fast-case check, so
   // it is certain that at least one operand isn't a smi.
 
@@ -2288,7 +2306,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
   __ push(r0);
   __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
 
-  __ StubReturn(1);
+  __ Ret();
 }
 
 
@@ -2299,32 +2317,37 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
   __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
 
   if (op_ == Token::SUB) {
-    // Check whether the value is a smi.
-    Label try_float;
-    __ tst(r0, Operand(kSmiTagMask));
-    __ b(ne, &try_float);
-
-    // Go slow case if the value of the expression is zero
-    // to make sure that we switch between 0 and -0.
-    if (negative_zero_ == kStrictNegativeZero) {
-      // If we have to check for zero, then we can check for the max negative
-      // smi while we are at it.
-      __ bic(ip, r0, Operand(0x80000000), SetCC);
-      __ b(eq, &slow);
-      __ rsb(r0, r0, Operand(0, RelocInfo::NONE));
-      __ StubReturn(1);
-    } else {
-      // The value of the expression is a smi and 0 is OK for -0.  Try
-      // optimistic subtraction '0 - value'.
-      __ rsb(r0, r0, Operand(0, RelocInfo::NONE), SetCC);
-      __ StubReturn(1, vc);
-      // We don't have to reverse the optimistic neg since the only case
-      // where we fall through is the minimum negative Smi, which is the case
-      // where the neg leaves the register unchanged.
-      __ jmp(&slow);  // Go slow on max negative Smi.
+    if (include_smi_code_) {
+      // Check whether the value is a smi.
+      Label try_float;
+      __ tst(r0, Operand(kSmiTagMask));
+      __ b(ne, &try_float);
+
+      // Go slow case if the value of the expression is zero
+      // to make sure that we switch between 0 and -0.
+      if (negative_zero_ == kStrictNegativeZero) {
+        // If we have to check for zero, then we can check for the max negative
+        // smi while we are at it.
+        __ bic(ip, r0, Operand(0x80000000), SetCC);
+        __ b(eq, &slow);
+        __ rsb(r0, r0, Operand(0, RelocInfo::NONE));
+        __ Ret();
+      } else {
+        // The value of the expression is a smi and 0 is OK for -0.  Try
+        // optimistic subtraction '0 - value'.
+        __ rsb(r0, r0, Operand(0, RelocInfo::NONE), SetCC);
+        __ Ret(vc);
+        // We don't have to reverse the optimistic neg since the only case
+        // where we fall through is the minimum negative Smi, which is the case
+        // where the neg leaves the register unchanged.
+        __ jmp(&slow);  // Go slow on max negative Smi.
+      }
+      __ bind(&try_float);
+    } else if (FLAG_debug_code) {
+      __ tst(r0, Operand(kSmiTagMask));
+      __ Assert(ne, "Unexpected smi operand.");
     }
 
-    __ bind(&try_float);
     __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
     __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
     __ cmp(r1, heap_number_map);
@@ -2344,6 +2367,19 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
       __ mov(r0, Operand(r1));
     }
   } else if (op_ == Token::BIT_NOT) {
+    if (include_smi_code_) {
+      Label non_smi;
+      __ BranchOnNotSmi(r0, &non_smi);
+      __ mvn(r0, Operand(r0));
+      // Bit-clear inverted smi-tag.
+      __ bic(r0, r0, Operand(kSmiTagMask));
+      __ Ret();
+      __ bind(&non_smi);
+    } else if (FLAG_debug_code) {
+      __ tst(r0, Operand(kSmiTagMask));
+      __ Assert(ne, "Unexpected smi operand.");
+    }
+
     // Check if the operand is a heap number.
     __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
     __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
@@ -2391,7 +2427,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
   }
 
   __ bind(&done);
-  __ StubReturn(1);
+  __ Ret();
 
   // Handle the slow case by jumping to the JavaScript builtin.
   __ bind(&slow);
@@ -3499,6 +3535,11 @@ const char* CompareStub::GetName() {
     include_number_compare_name = "_NO_NUMBER";
   }
 
+  const char* include_smi_compare_name = "";
+  if (!include_smi_compare_) {
+    include_smi_compare_name = "_NO_SMI";
+  }
+
   OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
                "CompareStub_%s%s%s%s%s%s",
                cc_name,
@@ -3506,7 +3547,8 @@ const char* CompareStub::GetName() {
                rhs_name,
                strict_name,
                never_nan_nan_name,
-               include_number_compare_name);
+               include_number_compare_name,
+               include_smi_compare_name);
   return name_;
 }
 
@@ -3522,7 +3564,8 @@ int CompareStub::MinorKey() {
          | RegisterField::encode(lhs_.is(r0))
          | StrictField::encode(strict_)
          | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
-         | IncludeNumberCompareField::encode(include_number_compare_);
+         | IncludeNumberCompareField::encode(include_number_compare_)
+         | IncludeSmiCompareField::encode(include_smi_compare_);
 }
 
 
@@ -4144,17 +4187,21 @@ void SubStringStub::Generate(MacroAssembler* masm) {
 
 
   // Check bounds and smi-ness.
-  __ ldr(r7, MemOperand(sp, kToOffset));
-  __ ldr(r6, MemOperand(sp, kFromOffset));
+  Register to = r6;
+  Register from = r7;
+  __ Ldrd(to, from, MemOperand(sp, kToOffset));
+  STATIC_ASSERT(kFromOffset == kToOffset + 4);
   STATIC_ASSERT(kSmiTag == 0);
   STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
   // I.e., arithmetic shift right by one un-smi-tags.
-  __ mov(r2, Operand(r7, ASR, 1), SetCC);
-  __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
-  // If either r2 or r6 had the smi tag bit set, then carry is set now.
+  __ mov(r2, Operand(to, ASR, 1), SetCC);
+  __ mov(r3, Operand(from, ASR, 1), SetCC, cc);
+  // If either to or from had the smi tag bit set, then carry is set now.
   __ b(cs, &runtime);  // Either "from" or "to" is not a smi.
   __ b(mi, &runtime);  // From is negative.
 
+  // Both to and from are smis.
+
   __ sub(r2, r2, Operand(r3), SetCC);
   __ b(mi, &runtime);  // Fail if from > to.
   // Special handling of sub-strings of length 1 and 2. One character strings
@@ -4165,8 +4212,8 @@ void SubStringStub::Generate(MacroAssembler* masm) {
 
   // r2: length
   // r3: from index (untaged smi)
-  // r6: from (smi)
-  // r7: to (smi)
+  // r6 (a.k.a. to): to (smi)
+  // r7 (a.k.a. from): from offset (smi)
 
   // Make sure first argument is a sequential (or flat) string.
   __ ldr(r5, MemOperand(sp, kStringOffset));
@@ -4178,10 +4225,10 @@ void SubStringStub::Generate(MacroAssembler* masm) {
 
   // r1: instance type
   // r2: length
-  // r3: from index (untaged smi)
+  // r3: from index (untagged smi)
   // r5: string
-  // r6: from (smi)
-  // r7: to (smi)
+  // r6 (a.k.a. to): to (smi)
+  // r7 (a.k.a. from): from offset (smi)
   Label seq_string;
   __ and_(r4, r1, Operand(kStringRepresentationMask));
   STATIC_ASSERT(kSeqStringTag < kConsStringTag);
@@ -4207,17 +4254,18 @@ void SubStringStub::Generate(MacroAssembler* masm) {
   // r2: length
   // r3: from index (untaged smi)
   // r5: string
-  // r6: from (smi)
-  // r7: to (smi)
+  // r6 (a.k.a. to): to (smi)
+  // r7 (a.k.a. from): from offset (smi)
   __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
-  __ cmp(r4, Operand(r7));
+  __ cmp(r4, Operand(to));
   __ b(lt, &runtime);  // Fail if to > length.
+  to = no_reg;
 
   // r1: instance type.
   // r2: result string length.
   // r3: from index (untaged smi)
   // r5: string.
-  // r6: from offset (smi)
+  // r7 (a.k.a. from): from offset (smi)
   // Check for flat ascii string.
   Label non_ascii_flat;
   __ tst(r1, Operand(kStringEncodingMask));
@@ -4259,12 +4307,12 @@ void SubStringStub::Generate(MacroAssembler* masm) {
   // r0: result string.
   // r2: result string length.
   // r5: string.
-  // r6: from offset (smi)
+  // r7 (a.k.a. from): from offset (smi)
   // Locate first character of result.
   __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
   // Locate 'from' character of string.
   __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
-  __ add(r5, r5, Operand(r6, ASR, 1));
+  __ add(r5, r5, Operand(from, ASR, 1));
 
   // r0: result string.
   // r1: first character of result string.
@@ -4280,7 +4328,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
   __ bind(&non_ascii_flat);
   // r2: result string length.
   // r5: string.
-  // r6: from offset (smi)
+  // r7 (a.k.a. from): from offset (smi)
   // Check for flat two byte string.
 
   // Allocate the result.
@@ -4292,18 +4340,19 @@ void SubStringStub::Generate(MacroAssembler* masm) {
   // Locate first character of result.
   __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
   // Locate 'from' character of string.
-    __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
+  __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
   // As "from" is a smi it is 2 times the value which matches the size of a two
   // byte character.
-  __ add(r5, r5, Operand(r6));
+  __ add(r5, r5, Operand(from));
+  from = no_reg;
 
   // r0: result string.
   // r1: first character of result.
   // r2: result length.
   // r5: first character of string to copy.
   STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
-  StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
-                                           DEST_ALWAYS_ALIGNED);
+  StringHelper::GenerateCopyCharactersLong(
+      masm, r1, r5, r2, r3, r4, r6, r7, r9, DEST_ALWAYS_ALIGNED);
   __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
   __ add(sp, sp, Operand(3 * kPointerSize));
   __ Ret();
@@ -4379,8 +4428,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
   // Stack frame on entry.
   //  sp[0]: right string
   //  sp[4]: left string
-  __ ldr(r0, MemOperand(sp, 1 * kPointerSize));  // left
-  __ ldr(r1, MemOperand(sp, 0 * kPointerSize));  // right
+  __ Ldrd(r0 , r1, MemOperand(sp));  // Load right in r0, left in r1.
 
   Label not_same;
   __ cmp(r0, r1);
@@ -4395,12 +4443,12 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
   __ bind(&not_same);
 
   // Check that both objects are sequential ascii strings.
-  __ JumpIfNotBothSequentialAsciiStrings(r0, r1, r2, r3, &runtime);
+  __ JumpIfNotBothSequentialAsciiStrings(r1, r0, r2, r3, &runtime);
 
   // Compare flat ascii strings natively. Remove arguments from stack first.
   __ IncrementCounter(&Counters::string_compare_native, 1, r2, r3);
   __ add(sp, sp, Operand(2 * kPointerSize));
-  GenerateCompareFlatAsciiStrings(masm, r0, r1, r2, r3, r4, r5);
+  GenerateCompareFlatAsciiStrings(masm, r1, r0, r2, r3, r4, r5);
 
   // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
   // tagged as a small integer.
index f985fb4..6ba166f 100644 (file)
@@ -1651,7 +1651,7 @@ void CodeGenerator::Comparison(Condition cc,
     // Perform non-smi comparison by stub.
     // CompareStub takes arguments in r0 and r1, returns <0, >0 or 0 in r0.
     // We call with 0 args because there are 0 on the stack.
-    CompareStub stub(cc, strict, kBothCouldBeNaN, true, lhs, rhs);
+    CompareStub stub(cc, strict, NO_SMI_COMPARE_IN_STUB, lhs, rhs);
     frame_->CallStub(&stub, 0);
     __ cmp(r0, Operand(0, RelocInfo::NONE));
     exit.Jump();
@@ -5985,6 +5985,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
         GenericUnaryOpStub stub(
             Token::SUB,
             overwrite,
+            NO_UNARY_FLAGS,
             no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
         frame_->CallStub(&stub, 0);
         frame_->EmitPush(r0);  // r0 has result
@@ -6009,7 +6010,9 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
         not_smi_label.Bind();
         frame_->SpillAll();
         __ Move(r0, tos);
-        GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
+        GenericUnaryOpStub stub(Token::BIT_NOT,
+                                overwrite,
+                                NO_UNARY_SMI_CODE_IN_STUB);
         frame_->CallStub(&stub, 0);
         frame_->EmitPush(r0);
 
index 162d97f..d4c3522 100644 (file)
@@ -271,10 +271,6 @@ class CodeGenerator: public AstVisitor {
 
   void AddDeferred(DeferredCode* code) { deferred_.Add(code); }
 
-  // If the name is an inline runtime function call return the number of
-  // expected arguments. Otherwise return -1.
-  static int InlineRuntimeCallArgumentsCount(Handle<String> name);
-
   // Constants related to patching of inlined load/store.
   static int GetInlinedKeyedLoadInstructionsAfterPatch() {
     return FLAG_debug_code ? 32 : 13;
@@ -290,6 +286,12 @@ class CodeGenerator: public AstVisitor {
   }
 
  private:
+  // Type of a member function that generates inline code for a native function.
+  typedef void (CodeGenerator::*InlineFunctionGenerator)
+      (ZoneList<Expression*>*);
+
+  static const InlineFunctionGenerator kInlineFunctionGenerators[];
+
   // Construction/Destruction
   explicit CodeGenerator(MacroAssembler* masm);
 
@@ -447,13 +449,9 @@ class CodeGenerator: public AstVisitor {
   void Branch(bool if_true, JumpTarget* target);
   void CheckStack();
 
-  struct InlineRuntimeLUT {
-    void (CodeGenerator::*method)(ZoneList<Expression*>*);
-    const char* name;
-    int nargs;
-  };
+  static InlineFunctionGenerator FindInlineFunctionGenerator(
+      Runtime::FunctionId function_id);
 
-  static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name);
   bool CheckForInlineRuntimeCall(CallRuntime* node);
 
   static Handle<Code> ComputeLazyCompile(int argc);
@@ -599,8 +597,6 @@ class CodeGenerator: public AstVisitor {
   // Size of inlined write barriers generated by EmitNamedStore.
   static int inlined_write_barrier_size_;
 
-  static InlineRuntimeLUT kInlineRuntimeLUT[];
-
   friend class VirtualFrame;
   friend class JumpTarget;
   friend class Reference;
index f32da6d..c776d67 100644 (file)
@@ -493,7 +493,7 @@ MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) {
       int context_chain_length =
           scope()->ContextChainLength(slot->var()->scope());
       __ LoadContext(scratch, context_chain_length);
-      return CodeGenerator::ContextOperand(scratch, slot->index());
+      return ContextOperand(scratch, slot->index());
     }
     case Slot::LOOKUP:
       UNREACHABLE();
@@ -557,19 +557,17 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
         ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
         if (FLAG_debug_code) {
           // Check if we have the correct context pointer.
-          __ ldr(r1,
-                 CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX));
+          __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX));
           __ cmp(r1, cp);
           __ Check(eq, "Unexpected declaration in current context.");
         }
         if (mode == Variable::CONST) {
           __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
-          __ str(ip, CodeGenerator::ContextOperand(cp, slot->index()));
+          __ str(ip, ContextOperand(cp, slot->index()));
           // No write barrier since the_hole_value is in old space.
         } else if (function != NULL) {
           VisitForValue(function, kAccumulator);
-          __ str(result_register(),
-                 CodeGenerator::ContextOperand(cp, slot->index()));
+          __ str(result_register(), ContextOperand(cp, slot->index()));
           int offset = Context::SlotOffset(slot->index());
           // We know that we have written a function, which is not a smi.
           __ mov(r1, Operand(cp));
@@ -674,7 +672,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
 
     // Perform the comparison as if via '==='.
     __ ldr(r1, MemOperand(sp, 0));  // Switch value.
-    if (ShouldInlineSmiCase(Token::EQ_STRICT)) {
+    bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+    if (inline_smi_code) {
       Label slow_case;
       __ orr(r2, r1, r0);
       __ tst(r2, Operand(kSmiTagMask));
@@ -686,7 +685,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
     __ bind(&slow_case);
     }
 
-    CompareStub stub(eq, true, kBothCouldBeNaN, true, r1, r0);
+    CompareFlags flags = inline_smi_code
+        ? NO_SMI_COMPARE_IN_STUB
+        : NO_COMPARE_FLAGS;
+    CompareStub stub(eq, true, flags, r1, r0);
     __ CallStub(&stub);
     __ cmp(r0, Operand(0, RelocInfo::NONE));
     __ b(ne, &next_test);
@@ -746,11 +748,10 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
   __ bind(&done_convert);
   __ push(r0);
 
-  // TODO(kasperl): Check cache validity in generated code. This is a
-  // fast case for the JSObject::IsSimpleEnum cache validity
-  // checks. If we cannot guarantee cache validity, call the runtime
-  // system to check cache validity or get the property names in a
-  // fixed array.
+  // BUG(867): Check cache validity in generated code. This is a fast
+  // case for the JSObject::IsSimpleEnum cache validity checks. If we
+  // cannot guarantee cache validity, call the runtime system to check
+  // cache validity or get the property names in a fixed array.
 
   // Get the set of properties to enumerate.
   __ push(r0);  // Duplicate the enumerable object on the stack.
@@ -881,6 +882,150 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
 }
 
 
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
+    Slot* slot,
+    Label* slow) {
+  ASSERT(slot->type() == Slot::CONTEXT);
+  Register current = cp;
+  Register next = r3;
+  Register temp = r4;
+
+  for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
+    if (s->num_heap_slots() > 0) {
+      if (s->calls_eval()) {
+        // Check that extension is NULL.
+        __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+        __ tst(temp, temp);
+        __ b(ne, slow);
+      }
+      __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX));
+      __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+      // Walk the rest of the chain without clobbering cp.
+      current = next;
+    }
+  }
+  // Check that last extension is NULL.
+  __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+  __ tst(temp, temp);
+  __ b(ne, slow);
+  __ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX));
+  return ContextOperand(temp, slot->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
+    Slot* slot,
+    TypeofState typeof_state,
+    Label* slow,
+    Label* done) {
+  // Generate fast-case code for variables that might be shadowed by
+  // eval-introduced variables.  Eval is used a lot without
+  // introducing variables.  In those cases, we do not want to
+  // perform a runtime call for all variables in the scope
+  // containing the eval.
+  if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
+    EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow);
+    __ jmp(done);
+  } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
+    Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
+    Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
+    if (potential_slot != NULL) {
+      // Generate fast case for locals that rewrite to slots.
+      __ ldr(r0, ContextSlotOperandCheckExtensions(potential_slot, slow));
+      if (potential_slot->var()->mode() == Variable::CONST) {
+        __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+        __ cmp(r0, ip);
+        __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
+      }
+      __ jmp(done);
+    } else if (rewrite != NULL) {
+      // Generate fast case for calls of an argument function.
+      Property* property = rewrite->AsProperty();
+      if (property != NULL) {
+        VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+        Literal* key_literal = property->key()->AsLiteral();
+        if (obj_proxy != NULL &&
+            key_literal != NULL &&
+            obj_proxy->IsArguments() &&
+            key_literal->handle()->IsSmi()) {
+          // Load arguments object if there are no eval-introduced
+          // variables. Then load the argument from the arguments
+          // object using keyed load.
+          __ ldr(r1,
+                 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                   slow));
+          __ mov(r0, Operand(key_literal->handle()));
+          Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+          __ Call(ic, RelocInfo::CODE_TARGET);
+          __ jmp(done);
+        }
+      }
+    }
+  }
+}
+
+
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
+    Slot* slot,
+    TypeofState typeof_state,
+    Label* slow) {
+  Register current = cp;
+  Register next = r1;
+  Register temp = r2;
+
+  Scope* s = scope();
+  while (s != NULL) {
+    if (s->num_heap_slots() > 0) {
+      if (s->calls_eval()) {
+        // Check that extension is NULL.
+        __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+        __ tst(temp, temp);
+        __ b(ne, slow);
+      }
+      // Load next context in chain.
+      __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX));
+      __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+      // Walk the rest of the chain without clobbering cp.
+      current = next;
+    }
+    // If no outer scope calls eval, we do not need to check more
+    // context extensions.
+    if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
+    s = s->outer_scope();
+  }
+
+  if (s->is_eval_scope()) {
+    Label loop, fast;
+    if (!current.is(next)) {
+      __ Move(next, current);
+    }
+    __ bind(&loop);
+    // Terminate at global context.
+    __ ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset));
+    __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
+    __ cmp(temp, ip);
+    __ b(eq, &fast);
+    // Check that extension is NULL.
+    __ ldr(temp, ContextOperand(next, Context::EXTENSION_INDEX));
+    __ tst(temp, temp);
+    __ b(ne, slow);
+    // Load next context in chain.
+    __ ldr(next, ContextOperand(next, Context::CLOSURE_INDEX));
+    __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+    __ b(&loop);
+    __ bind(&fast);
+  }
+
+  __ ldr(r0, CodeGenerator::GlobalObject());
+  __ mov(r2, Operand(slot->var()->name()));
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+      ? RelocInfo::CODE_TARGET
+      : RelocInfo::CODE_TARGET_CONTEXT;
+  Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+  __ Call(ic, mode);
+}
+
+
 void FullCodeGenerator::EmitVariableLoad(Variable* var,
                                          Expression::Context context) {
   // Four cases: non-this global variables, lookup slots, all other
@@ -900,10 +1045,19 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
     Apply(context, r0);
 
   } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
+    Label done, slow;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done);
+
+    __ bind(&slow);
     Comment cmnt(masm_, "Lookup slot");
     __ mov(r1, Operand(var->name()));
     __ Push(cp, r1);  // Context and name.
     __ CallRuntime(Runtime::kLoadContextSlot, 2);
+    __ bind(&done);
+
     Apply(context, r0);
 
   } else if (slot != NULL) {
@@ -913,14 +1067,11 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
     if (var->mode() == Variable::CONST) {
        // Constants may be the hole value if they have not been initialized.
        // Unhole them.
-       Label done;
        MemOperand slot_operand = EmitSlotSearch(slot, r0);
        __ ldr(r0, slot_operand);
        __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
        __ cmp(r0, ip);
-       __ b(ne, &done);
-       __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
-       __ bind(&done);
+       __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
        Apply(context, r0);
      } else {
        Apply(context, slot);
@@ -1647,15 +1798,41 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
   } else if (var != NULL && var->slot() != NULL &&
              var->slot()->type() == Slot::LOOKUP) {
-    // Call to a lookup slot (dynamically introduced variable).  Call the
-    // runtime to find the function to call (returned in eax) and the object
-    // holding it (returned in edx).
+    // Call to a lookup slot (dynamically introduced variable).
+    Label slow, done;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    EmitDynamicLoadFromSlotFastCase(var->slot(),
+                                    NOT_INSIDE_TYPEOF,
+                                    &slow,
+                                    &done);
+
+    __ bind(&slow);
+    // Call the runtime to find the function to call (returned in eax)
+    // and the object holding it (returned in edx).
     __ push(context_register());
     __ mov(r2, Operand(var->name()));
     __ push(r2);
     __ CallRuntime(Runtime::kLoadContextSlot, 2);
-    __ push(r0);  // Function.
-    __ push(r1);  // Receiver.
+    __ Push(r0, r1);  // Function, receiver.
+
+    // If fast case code has been generated, emit code to push the
+    // function and receiver and have the slow path jump around this
+    // code.
+    if (done.is_linked()) {
+      Label call;
+      __ b(&call);
+      __ bind(&done);
+      // Push function.
+      __ push(r0);
+      // Push global receiver.
+      __ ldr(r1, CodeGenerator::GlobalObject());
+      __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+      __ push(r1);
+      __ bind(&call);
+    }
+
     EmitCallWithStub(expr);
   } else if (fun->AsProperty() != NULL) {
     // Call to an object property.
@@ -1678,12 +1855,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
 
         Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
         __ Call(ic, RelocInfo::CODE_TARGET);
-        // Push result (function).
-        __ push(r0);
-        // Push Global receiver.
         __ ldr(r1, CodeGenerator::GlobalObject());
         __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
-        __ push(r1);
+        __ Push(r0, r1);  // Function, receiver.
         EmitCallWithStub(expr);
       } else {
         EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
@@ -2464,11 +2638,9 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
 
   Register key = r0;
   Register cache = r1;
-  __ ldr(cache, CodeGenerator::ContextOperand(cp, Context::GLOBAL_INDEX));
+  __ ldr(cache, ContextOperand(cp, Context::GLOBAL_INDEX));
   __ ldr(cache, FieldMemOperand(cache, GlobalObject::kGlobalContextOffset));
-  __ ldr(cache,
-         CodeGenerator::ContextOperand(
-             cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+  __ ldr(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
   __ ldr(cache,
          FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
 
@@ -2720,7 +2892,9 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
       UnaryOverwriteMode overwrite =
           can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-      GenericUnaryOpStub stub(Token::SUB, overwrite);
+      GenericUnaryOpStub stub(Token::SUB,
+                              overwrite,
+                              NO_UNARY_FLAGS);
       // GenericUnaryOpStub expects the argument to be in the
       // accumulator register r0.
       VisitForValue(expr->expression(), kAccumulator);
@@ -2735,7 +2909,8 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       // in the accumulator register r0.
       VisitForValue(expr->expression(), kAccumulator);
       Label done;
-      if (ShouldInlineSmiCase(expr->op())) {
+      bool inline_smi_code = ShouldInlineSmiCase(expr->op());
+      if (inline_smi_code) {
         Label call_stub;
         __ BranchOnNotSmi(r0, &call_stub);
         __ mvn(r0, Operand(r0));
@@ -2745,9 +2920,12 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
         __ bind(&call_stub);
       }
       bool overwrite = expr->expression()->ResultOverwriteAllowed();
+      UnaryOpFlags flags = inline_smi_code
+          ? NO_UNARY_SMI_CODE_IN_STUB
+          : NO_UNARY_FLAGS;
       UnaryOverwriteMode mode =
           overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-      GenericUnaryOpStub stub(Token::BIT_NOT, mode);
+      GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
       __ CallStub(&stub);
       __ bind(&done);
       Apply(context_, r0);
@@ -2929,9 +3107,19 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) {
   } else if (proxy != NULL &&
              proxy->var()->slot() != NULL &&
              proxy->var()->slot()->type() == Slot::LOOKUP) {
+    Label done, slow;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    Slot* slot = proxy->var()->slot();
+    EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done);
+
+    __ bind(&slow);
     __ mov(r0, Operand(proxy->name()));
     __ Push(cp, r0);
     __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+    __ bind(&done);
+
     if (where == kStack) __ push(r0);
   } else {
     // This expression cannot throw a reference error at the top level.
@@ -3114,7 +3302,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
           UNREACHABLE();
       }
 
-      if (ShouldInlineSmiCase(op)) {
+      bool inline_smi_code = ShouldInlineSmiCase(op);
+      if (inline_smi_code) {
         Label slow_case;
         __ orr(r2, r0, Operand(r1));
         __ BranchOnNotSmi(r2, &slow_case);
@@ -3122,8 +3311,10 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
         Split(cc, if_true, if_false, NULL);
         __ bind(&slow_case);
       }
-
-      CompareStub stub(cc, strict, kBothCouldBeNaN, true, r1, r0);
+      CompareFlags flags = inline_smi_code
+          ? NO_SMI_COMPARE_IN_STUB
+          : NO_COMPARE_FLAGS;
+      CompareStub stub(cc, strict, flags, r1, r0);
       __ CallStub(&stub);
       __ cmp(r0, Operand(0, RelocInfo::NONE));
       Split(cc, if_true, if_false, fall_through);
@@ -3187,7 +3378,7 @@ void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
 
 
 void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
-  __ ldr(dst, CodeGenerator::ContextOperand(cp, context_index));
+  __ ldr(dst, ContextOperand(cp, context_index));
 }
 
 
index 3554431..0e2c49e 100644 (file)
@@ -1242,15 +1242,6 @@ void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) {
 }
 
 
-void MacroAssembler::StubReturn(int argc, Condition cond) {
-  ASSERT(argc >= 1 && generating_stub());
-  if (argc > 1) {
-    add(sp, sp, Operand((argc - 1) * kPointerSize), LeaveCC, cond);
-  }
-  Ret(cond);
-}
-
-
 void MacroAssembler::IllegalOperation(int num_arguments) {
   if (num_arguments > 0) {
     add(sp, sp, Operand(num_arguments * kPointerSize));
index febd87e..48a8059 100644 (file)
@@ -531,9 +531,6 @@ class MacroAssembler: public Assembler {
   // Call a code stub.
   void TailCallStub(CodeStub* stub, Condition cond = al);
 
-  // Return from a code stub after popping its arguments.
-  void StubReturn(int argc, Condition cond = al);
-
   // Call a runtime routine.
   void CallRuntime(Runtime::Function* f, int num_arguments);
 
index 344cb6f..0da5f64 100644 (file)
@@ -1220,6 +1220,62 @@ void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
 }
 
 
+void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
+                                                   JSObject* holder,
+                                                   String* name,
+                                                   Label* miss) {
+  ASSERT(holder->IsGlobalObject());
+
+  // Get the number of arguments.
+  const int argc = arguments().immediate();
+
+  // Get the receiver from the stack.
+  __ ldr(r0, MemOperand(sp, argc * kPointerSize));
+
+  // If the object is the holder then we know that it's a global
+  // object which can only happen for contextual calls. In this case,
+  // the receiver cannot be a smi.
+  if (object != holder) {
+    __ tst(r0, Operand(kSmiTagMask));
+    __ b(eq, miss);
+  }
+
+  // Check that the maps haven't changed.
+  CheckPrototypes(object, r0, holder, r3, r1, r4, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
+                                                    JSFunction* function,
+                                                    Label* miss) {
+  // Get the value from the cell.
+  __ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
+  __ ldr(r1, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset));
+
+  // Check that the cell contains the same function.
+  if (Heap::InNewSpace(function)) {
+    // We can't embed a pointer to a function in new space so we have
+    // to verify that the shared function info is unchanged. This has
+    // the nice side effect that multiple closures based on the same
+    // function can all use this call IC. Before we load through the
+    // function, we have to verify that it still is a function.
+    __ tst(r1, Operand(kSmiTagMask));
+    __ b(eq, miss);
+    __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+    __ b(ne, miss);
+
+    // Check the shared function info. Make sure it hasn't changed.
+    __ Move(r3, Handle<SharedFunctionInfo>(function->shared()));
+    __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+    __ cmp(r4, r3);
+    __ b(ne, miss);
+  } else {
+    __ cmp(r1, Operand(Handle<JSFunction>(function)));
+    __ b(ne, miss);
+  }
+}
+
+
 Object* CallStubCompiler::GenerateMissBranch() {
   Object* obj = StubCache::ComputeCallMiss(arguments().immediate(), kind_);
   if (obj->IsFailure()) return obj;
@@ -1266,21 +1322,18 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
 
 Object* CallStubCompiler::CompileArrayPushCall(Object* object,
                                                JSObject* holder,
+                                               JSGlobalPropertyCell* cell,
                                                JSFunction* function,
-                                               String* name,
-                                               CheckType check) {
+                                               String* name) {
   // ----------- S t a t e -------------
   //  -- r2    : name
   //  -- lr    : return address
   // -----------------------------------
 
-  // If object is not an array, bail out to regular call.
-  if (!object->IsJSArray()) {
-    return Heap::undefined_value();
-  }
-
   // TODO(639): faster implementation.
-  ASSERT(check == RECEIVER_MAP_CHECK);
+
+  // If object is not an array, bail out to regular call.
+  if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
 
   Label miss;
 
@@ -1313,21 +1366,18 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
 
 Object* CallStubCompiler::CompileArrayPopCall(Object* object,
                                               JSObject* holder,
+                                              JSGlobalPropertyCell* cell,
                                               JSFunction* function,
-                                              String* name,
-                                              CheckType check) {
+                                              String* name) {
   // ----------- S t a t e -------------
   //  -- r2    : name
   //  -- lr    : return address
   // -----------------------------------
 
-  // If object is not an array, bail out to regular call.
-  if (!object->IsJSArray()) {
-    return Heap::undefined_value();
-  }
-
   // TODO(642): faster implementation.
-  ASSERT(check == RECEIVER_MAP_CHECK);
+
+  // If object is not an array, bail out to regular call.
+  if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
 
   Label miss;
 
@@ -1358,11 +1408,12 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
 }
 
 
-Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
-                                                      JSObject* holder,
-                                                      JSFunction* function,
-                                                      String* name,
-                                                      CheckType check) {
+Object* CallStubCompiler::CompileStringCharCodeAtCall(
+    Object* object,
+    JSObject* holder,
+    JSGlobalPropertyCell* cell,
+    JSFunction* function,
+    String* name) {
   // ----------- S t a t e -------------
   //  -- r2                     : function name
   //  -- lr                     : return address
@@ -1372,7 +1423,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
   // -----------------------------------
 
   // If object is not a string, bail out to regular call.
-  if (!object->IsString()) return Heap::undefined_value();
+  if (!object->IsString() || cell != NULL) return Heap::undefined_value();
 
   const int argc = arguments().immediate();
 
@@ -1430,9 +1481,9 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
 
 Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
                                                   JSObject* holder,
+                                                  JSGlobalPropertyCell* cell,
                                                   JSFunction* function,
-                                                  String* name,
-                                                  CheckType check) {
+                                                  String* name) {
   // ----------- S t a t e -------------
   //  -- r2                     : function name
   //  -- lr                     : return address
@@ -1442,7 +1493,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
   // -----------------------------------
 
   // If object is not a string, bail out to regular call.
-  if (!object->IsString()) return Heap::undefined_value();
+  if (!object->IsString() || cell != NULL) return Heap::undefined_value();
 
   const int argc = arguments().immediate();
 
@@ -1501,6 +1552,80 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
 }
 
 
+Object* CallStubCompiler::CompileStringFromCharCodeCall(
+    Object* object,
+    JSObject* holder,
+    JSGlobalPropertyCell* cell,
+    JSFunction* function,
+    String* name) {
+  // ----------- S t a t e -------------
+  //  -- r2                     : function name
+  //  -- lr                     : return address
+  //  -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
+  //  -- ...
+  //  -- sp[argc * 4]           : receiver
+  // -----------------------------------
+
+  const int argc = arguments().immediate();
+
+  // If the object is not a JSObject or we got an unexpected number of
+  // arguments, bail out to the regular call.
+  if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
+
+  Label miss;
+  GenerateNameCheck(name, &miss);
+
+  if (cell == NULL) {
+    __ ldr(r1, MemOperand(sp, 1 * kPointerSize));
+
+    STATIC_ASSERT(kSmiTag == 0);
+    __ tst(r1, Operand(kSmiTagMask));
+    __ b(eq, &miss);
+
+    CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name,
+                    &miss);
+  } else {
+    ASSERT(cell->value() == function);
+    GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+    GenerateLoadFunctionFromCell(cell, function, &miss);
+  }
+
+  // Load the char code argument.
+  Register code = r1;
+  __ ldr(code, MemOperand(sp, 0 * kPointerSize));
+
+  // Check the code is a smi.
+  Label slow;
+  STATIC_ASSERT(kSmiTag == 0);
+  __ tst(code, Operand(kSmiTagMask));
+  __ b(ne, &slow);
+
+  // Convert the smi code to uint16.
+  __ and_(code, code, Operand(Smi::FromInt(0xffff)));
+
+  StringCharFromCodeGenerator char_from_code_generator(code, r0);
+  char_from_code_generator.GenerateFast(masm());
+  __ Drop(argc + 1);
+  __ Ret();
+
+  ICRuntimeCallHelper call_helper;
+  char_from_code_generator.GenerateSlow(masm(), call_helper);
+
+  // Tail call the full function. We do not have to patch the receiver
+  // because the function makes no use of it.
+  __ bind(&slow);
+  __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+  __ bind(&miss);
+  // r2: function name.
+  Object* obj = GenerateMissBranch();
+  if (obj->IsFailure()) return obj;
+
+  // Return the generated code.
+  return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
+}
+
+
 Object* CallStubCompiler::CompileCallConstant(Object* object,
                                               JSObject* holder,
                                               JSFunction* function,
@@ -1513,8 +1638,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
   SharedFunctionInfo* function_info = function->shared();
   if (function_info->HasCustomCallGenerator()) {
     const int id = function_info->custom_call_generator_id();
-    Object* result =
-        CompileCustomCall(id, object, holder, function, name, check);
+    Object* result = CompileCustomCall(
+        id, object, holder, NULL, function, name);
     // undefined means bail out to regular compiler.
     if (!result->IsUndefined()) {
       return result;
@@ -1714,6 +1839,16 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   //  -- r2    : name
   //  -- lr    : return address
   // -----------------------------------
+
+  SharedFunctionInfo* function_info = function->shared();
+  if (function_info->HasCustomCallGenerator()) {
+    const int id = function_info->custom_call_generator_id();
+    Object* result = CompileCustomCall(
+        id, object, holder, cell, function, name);
+    // undefined means bail out to regular compiler.
+    if (!result->IsUndefined()) return result;
+  }
+
   Label miss;
 
   GenerateNameCheck(name, &miss);
@@ -1721,45 +1856,9 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // Get the number of arguments.
   const int argc = arguments().immediate();
 
-  // Get the receiver from the stack.
-  __ ldr(r0, MemOperand(sp, argc * kPointerSize));
-
-  // If the object is the holder then we know that it's a global
-  // object which can only happen for contextual calls. In this case,
-  // the receiver cannot be a smi.
-  if (object != holder) {
-    __ tst(r0, Operand(kSmiTagMask));
-    __ b(eq, &miss);
-  }
-
-  // Check that the maps haven't changed.
-  CheckPrototypes(object, r0, holder, r3, r1, r4, name, &miss);
-
-  // Get the value from the cell.
-  __ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
-  __ ldr(r1, FieldMemOperand(r3, JSGlobalPropertyCell::kValueOffset));
-
-  // Check that the cell contains the same function.
-  if (Heap::InNewSpace(function)) {
-    // We can't embed a pointer to a function in new space so we have
-    // to verify that the shared function info is unchanged. This has
-    // the nice side effect that multiple closures based on the same
-    // function can all use this call IC. Before we load through the
-    // function, we have to verify that it still is a function.
-    __ tst(r1, Operand(kSmiTagMask));
-    __ b(eq, &miss);
-    __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
-    __ b(ne, &miss);
+  GenerateGlobalReceiverCheck(object, holder, name, &miss);
 
-    // Check the shared function info. Make sure it hasn't changed.
-    __ mov(r3, Operand(Handle<SharedFunctionInfo>(function->shared())));
-    __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
-    __ cmp(r4, r3);
-    __ b(ne, &miss);
-  } else {
-    __ cmp(r1, Operand(Handle<JSFunction>(function)));
-    __ b(ne, &miss);
-  }
+  GenerateLoadFunctionFromCell(cell, function, &miss);
 
   // Patch the receiver on the stack with the global proxy if
   // necessary.
index e12df64..b2ebece 100644 (file)
@@ -957,14 +957,41 @@ function ArrayIndexOf(element, index) {
     // If index is still negative, search the entire array.
     if (index < 0) index = 0;
   }
+  var min = index;
+  var max = length;
+  if (UseSparseVariant(this, length, true)) {
+    var intervals = %GetArrayKeys(this, length);
+    if (intervals.length == 2 && intervals[0] < 0) {
+      // A single interval.
+      var intervalMin = -(intervals[0] + 1);
+      var intervalMax = intervalMin + intervals[1];
+      min = MAX(min, intervalMin);
+      max = intervalMax;  // Capped by length already.
+      // Fall through to loop below.
+    } else {
+      if (intervals.length == 0) return -1;
+      // Get all the keys in sorted order.
+      var sortedKeys = GetSortedArrayKeys(this, intervals);
+      var n = sortedKeys.length;
+      var i = 0;
+      while (i < n && sortedKeys[i] < index) i++;
+      while (i < n) {
+        var key = sortedKeys[i];
+        if (!IS_UNDEFINED(key) && this[key] === element) return key;
+        i++;
+      }
+      return -1;
+    }
+  }
   // Lookup through the array.
   if (!IS_UNDEFINED(element)) {
-    for (var i = index; i < length; i++) {
+    for (var i = min; i < max; i++) {
       if (this[i] === element) return i;
     }
     return -1;
   }
-  for (var i = index; i < length; i++) {
+  // Lookup through the array.
+  for (var i = min; i < max; i++) {
     if (IS_UNDEFINED(this[i]) && i in this) {
       return i;
     }
@@ -981,19 +1008,43 @@ function ArrayLastIndexOf(element, index) {
   } else {
     index = TO_INTEGER(index);
     // If index is negative, index from end of the array.
-    if (index < 0) index = length + index;
+    if (index < 0) index += length;
     // If index is still negative, do not search the array.
-    if (index < 0) index = -1;
+    if (index < 0) return -1;
     else if (index >= length) index = length - 1;
   }
+  var min = 0;
+  var max = index;
+  if (UseSparseVariant(this, length, true)) {
+    var intervals = %GetArrayKeys(this, index + 1);
+    if (intervals.length == 2 && intervals[0] < 0) {
+      // A single interval.
+      var intervalMin = -(intervals[0] + 1);
+      var intervalMax = intervalMin + intervals[1];
+      min = MAX(min, intervalMin);
+      max = intervalMax;  // Capped by index already.
+      // Fall through to loop below.
+    } else {
+      if (intervals.length == 0) return -1;
+      // Get all the keys in sorted order.
+      var sortedKeys = GetSortedArrayKeys(this, intervals);
+      var i = sortedKeys.length - 1;
+      while (i >= 0) {
+        var key = sortedKeys[i];
+        if (!IS_UNDEFINED(key) && this[key] === element) return key;
+        i--;
+      }
+      return -1;
+    }
+  }
   // Lookup through the array.
   if (!IS_UNDEFINED(element)) {
-    for (var i = index; i >= 0; i--) {
+    for (var i = max; i >= min; i--) {
       if (this[i] === element) return i;
     }
     return -1;
   }
-  for (var i = index; i >= 0; i--) {
+  for (var i = max; i >= min; i--) {
     if (IS_UNDEFINED(this[i]) && i in this) {
       return i;
     }
index a82d1d6..6e6c2c6 100644 (file)
@@ -1344,23 +1344,33 @@ bool Genesis::InstallNatives() {
 }
 
 
-static void InstallCustomCallGenerator(Handle<JSFunction> holder_function,
-                                       const char* function_name,
-                                       int id) {
-  Handle<JSObject> proto(JSObject::cast(holder_function->instance_prototype()));
+static void InstallCustomCallGenerator(
+    Handle<JSFunction> holder_function,
+    CallStubCompiler::CustomGeneratorOwner owner_flag,
+    const char* function_name,
+    int id) {
+  Handle<JSObject> owner;
+  if (owner_flag == CallStubCompiler::FUNCTION) {
+    owner = Handle<JSObject>::cast(holder_function);
+  } else {
+    ASSERT(owner_flag == CallStubCompiler::INSTANCE_PROTOTYPE);
+    owner = Handle<JSObject>(
+        JSObject::cast(holder_function->instance_prototype()));
+  }
   Handle<String> name = Factory::LookupAsciiSymbol(function_name);
-  Handle<JSFunction> function(JSFunction::cast(proto->GetProperty(*name)));
+  Handle<JSFunction> function(JSFunction::cast(owner->GetProperty(*name)));
   function->shared()->set_function_data(Smi::FromInt(id));
 }
 
 
 void Genesis::InstallCustomCallGenerators() {
   HandleScope scope;
-#define INSTALL_CALL_GENERATOR(holder_fun, fun_name, name)                \
+#define INSTALL_CALL_GENERATOR(holder_fun, owner_flag, fun_name, name)    \
   {                                                                       \
     Handle<JSFunction> holder(global_context()->holder_fun##_function()); \
     const int id = CallStubCompiler::k##name##CallGenerator;              \
-    InstallCustomCallGenerator(holder, #fun_name, id);                    \
+    InstallCustomCallGenerator(holder, CallStubCompiler::owner_flag,      \
+                               #fun_name, id);                            \
   }
   CUSTOM_CALL_IC_GENERATORS(INSTALL_CALL_GENERATOR)
 #undef INSTALL_CALL_GENERATOR
index 98a5cf6..912d43d 100644 (file)
@@ -340,27 +340,40 @@ enum NegativeZeroHandling {
 };
 
 
+enum UnaryOpFlags {
+  NO_UNARY_FLAGS = 0,
+  NO_UNARY_SMI_CODE_IN_STUB = 1 << 0
+};
+
+
 class GenericUnaryOpStub : public CodeStub {
  public:
   GenericUnaryOpStub(Token::Value op,
                      UnaryOverwriteMode overwrite,
+                     UnaryOpFlags flags,
                      NegativeZeroHandling negative_zero = kStrictNegativeZero)
-      : op_(op), overwrite_(overwrite), negative_zero_(negative_zero) { }
+      : op_(op),
+        overwrite_(overwrite),
+        include_smi_code_((flags & NO_UNARY_SMI_CODE_IN_STUB) == 0),
+        negative_zero_(negative_zero) { }
 
  private:
   Token::Value op_;
   UnaryOverwriteMode overwrite_;
+  bool include_smi_code_;
   NegativeZeroHandling negative_zero_;
 
   class OverwriteField: public BitField<UnaryOverwriteMode, 0, 1> {};
-  class NegativeZeroField: public BitField<NegativeZeroHandling, 1, 1> {};
-  class OpField: public BitField<Token::Value, 2, kMinorBits - 2> {};
+  class IncludeSmiCodeField: public BitField<bool, 1, 1> {};
+  class NegativeZeroField: public BitField<NegativeZeroHandling, 2, 1> {};
+  class OpField: public BitField<Token::Value, 3, kMinorBits - 3> {};
 
   Major MajorKey() { return GenericUnaryOp; }
   int MinorKey() {
     return OpField::encode(op_) |
-           OverwriteField::encode(overwrite_) |
-           NegativeZeroField::encode(negative_zero_);
+        OverwriteField::encode(overwrite_) |
+        IncludeSmiCodeField::encode(include_smi_code_) |
+        NegativeZeroField::encode(negative_zero_);
   }
 
   void Generate(MacroAssembler* masm);
@@ -375,22 +388,43 @@ enum NaNInformation {
 };
 
 
+// Flags that control the compare stub code generation.
+enum CompareFlags {
+  NO_COMPARE_FLAGS = 0,
+  NO_SMI_COMPARE_IN_STUB = 1 << 0,
+  NO_NUMBER_COMPARE_IN_STUB = 1 << 1,
+  CANT_BOTH_BE_NAN = 1 << 2
+};
+
+
 class CompareStub: public CodeStub {
  public:
   CompareStub(Condition cc,
               bool strict,
-              NaNInformation nan_info = kBothCouldBeNaN,
-              bool include_number_compare = true,
-              Register lhs = no_reg,
-              Register rhs = no_reg) :
+              CompareFlags flags,
+              Register lhs,
+              Register rhs) :
       cc_(cc),
       strict_(strict),
-      never_nan_nan_(nan_info == kCantBothBeNaN),
-      include_number_compare_(include_number_compare),
+      never_nan_nan_((flags & CANT_BOTH_BE_NAN) != 0),
+      include_number_compare_((flags & NO_NUMBER_COMPARE_IN_STUB) == 0),
+      include_smi_compare_((flags & NO_SMI_COMPARE_IN_STUB) == 0),
       lhs_(lhs),
       rhs_(rhs),
       name_(NULL) { }
 
+  CompareStub(Condition cc,
+              bool strict,
+              CompareFlags flags) :
+      cc_(cc),
+      strict_(strict),
+      never_nan_nan_((flags & CANT_BOTH_BE_NAN) != 0),
+      include_number_compare_((flags & NO_NUMBER_COMPARE_IN_STUB) == 0),
+      include_smi_compare_((flags & NO_SMI_COMPARE_IN_STUB) == 0),
+      lhs_(no_reg),
+      rhs_(no_reg),
+      name_(NULL) { }
+
   void Generate(MacroAssembler* masm);
 
  private:
@@ -406,6 +440,10 @@ class CompareStub: public CodeStub {
   // comparison code is used when the number comparison has been inlined, and
   // the stub will be called if one of the operands is not a number.
   bool include_number_compare_;
+
+  // Generate the comparison code for two smi operands in the stub.
+  bool include_smi_compare_;
+
   // Register holding the left hand side of the comparison if the stub gives
   // a choice, no_reg otherwise.
   Register lhs_;
@@ -413,12 +451,13 @@ class CompareStub: public CodeStub {
   // a choice, no_reg otherwise.
   Register rhs_;
 
-  // Encoding of the minor key CCCCCCCCCCCCRCNS.
+  // Encoding of the minor key in 16 bits.
   class StrictField: public BitField<bool, 0, 1> {};
   class NeverNanNanField: public BitField<bool, 1, 1> {};
   class IncludeNumberCompareField: public BitField<bool, 2, 1> {};
-  class RegisterField: public BitField<bool, 3, 1> {};
-  class ConditionField: public BitField<int, 4, 12> {};
+  class IncludeSmiCompareField: public  BitField<bool, 3, 1> {};
+  class RegisterField: public BitField<bool, 4, 1> {};
+  class ConditionField: public BitField<int, 5, 11> {};
 
   Major MajorKey() { return Compare; }
 
@@ -436,11 +475,13 @@ class CompareStub: public CodeStub {
   const char* GetName();
 #ifdef DEBUG
   void Print() {
-    PrintF("CompareStub (cc %d), (strict %s), "
-           "(never_nan_nan %s), (number_compare %s) ",
+    PrintF("CompareStub (minor %d) (cc %d), (strict %s), "
+           "(never_nan_nan %s), (smi_compare %s) (number_compare %s) ",
+           MinorKey(),
            static_cast<int>(cc_),
            strict_ ? "true" : "false",
            never_nan_nan_ ? "true" : "false",
+           include_smi_compare_ ? "inluded" : "not included",
            include_number_compare_ ? "included" : "not included");
 
     if (!lhs_.is(no_reg) && !rhs_.is(no_reg)) {
index 148cefc..daf1c0d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -344,40 +344,35 @@ void CodeGenerator::VisitIncrementOperation(IncrementOperation* expr) {
 }
 
 
-// List of special runtime calls which are generated inline. For some of these
-// functions the code will be generated inline, and for others a call to a code
-// stub will be inlined.
+// Lookup table for code generators for special runtime calls which are
+// generated inline.
+#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize)          \
+    &CodeGenerator::Generate##Name,
 
-#define INLINE_RUNTIME_ENTRY(Name, argc, ressize)                             \
-    {&CodeGenerator::Generate##Name,  "_" #Name, argc},                       \
-
-CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
-  INLINE_RUNTIME_FUNCTION_LIST(INLINE_RUNTIME_ENTRY)
+const CodeGenerator::InlineFunctionGenerator
+    CodeGenerator::kInlineFunctionGenerators[] = {
+        INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+        INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
 };
+#undef INLINE_FUNCTION_GENERATOR_ADDRESS
 
-#undef INLINE_RUNTIME_ENTRY
 
-CodeGenerator::InlineRuntimeLUT* CodeGenerator::FindInlineRuntimeLUT(
-    Handle<String> name) {
-  const int entries_count =
-      sizeof(kInlineRuntimeLUT) / sizeof(InlineRuntimeLUT);
-  for (int i = 0; i < entries_count; i++) {
-    InlineRuntimeLUT* entry = &kInlineRuntimeLUT[i];
-    if (name->IsEqualTo(CStrVector(entry->name))) {
-      return entry;
-    }
-  }
-  return NULL;
+CodeGenerator::InlineFunctionGenerator
+  CodeGenerator::FindInlineFunctionGenerator(Runtime::FunctionId id) {
+    return kInlineFunctionGenerators[
+      static_cast<int>(id) - static_cast<int>(Runtime::kFirstInlineFunction)];
 }
 
 
 bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
   ZoneList<Expression*>* args = node->arguments();
   Handle<String> name = node->name();
-  if (name->length() > 0 && name->Get(0) == '_') {
-    InlineRuntimeLUT* entry = FindInlineRuntimeLUT(name);
-    if (entry != NULL) {
-      ((*this).*(entry->method))(args);
+  Runtime::Function* function = node->function();
+  if (function != NULL && function->intrinsic_type == Runtime::INLINE) {
+    InlineFunctionGenerator generator =
+        FindInlineFunctionGenerator(function->function_id);
+    if (generator != NULL) {
+      ((*this).*(generator))(args);
       return true;
     }
   }
@@ -385,14 +380,6 @@ bool CodeGenerator::CheckForInlineRuntimeCall(CallRuntime* node) {
 }
 
 
-int CodeGenerator::InlineRuntimeCallArgumentsCount(Handle<String> name) {
-  CodeGenerator::InlineRuntimeLUT* f =
-      CodeGenerator::FindInlineRuntimeLUT(name);
-  if (f != NULL) return f->nargs;
-  return -1;
-}
-
-
 // Simple condition analysis.  ALWAYS_TRUE and ALWAYS_FALSE represent a
 // known result for the test expression, with no side effects.
 CodeGenerator::ConditionAnalysis CodeGenerator::AnalyzeCondition(
index aa2d442..2a4d9d4 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
 //   CodeForDoWhileConditionPosition
 //   CodeForSourcePosition
 
-
-#define INLINE_RUNTIME_FUNCTION_LIST(F) \
-  F(IsSmi, 1, 1)                                                             \
-  F(IsNonNegativeSmi, 1, 1)                                                  \
-  F(IsArray, 1, 1)                                                           \
-  F(IsRegExp, 1, 1)                                                          \
-  F(CallFunction, -1 /* receiver + n args + function */, 1)                  \
-  F(IsConstructCall, 0, 1)                                                   \
-  F(ArgumentsLength, 0, 1)                                                   \
-  F(Arguments, 1, 1)                                                         \
-  F(ClassOf, 1, 1)                                                           \
-  F(ValueOf, 1, 1)                                                           \
-  F(SetValueOf, 2, 1)                                                        \
-  F(StringCharCodeAt, 2, 1)                                                  \
-  F(StringCharFromCode, 1, 1)                                                \
-  F(StringCharAt, 2, 1)                                                      \
-  F(ObjectEquals, 2, 1)                                                      \
-  F(Log, 3, 1)                                                               \
-  F(RandomHeapNumber, 0, 1)                                                  \
-  F(IsObject, 1, 1)                                                          \
-  F(IsFunction, 1, 1)                                                        \
-  F(IsUndetectableObject, 1, 1)                                              \
-  F(IsSpecObject, 1, 1)                                                      \
-  F(IsStringWrapperSafeForDefaultValueOf, 1, 1)                              \
-  F(StringAdd, 2, 1)                                                         \
-  F(SubString, 3, 1)                                                         \
-  F(StringCompare, 2, 1)                                                     \
-  F(RegExpExec, 4, 1)                                                        \
-  F(RegExpConstructResult, 3, 1)                                             \
-  F(RegExpCloneResult, 1, 1)                                                 \
-  F(GetFromCache, 2, 1)                                                      \
-  F(NumberToString, 1, 1)                                                    \
-  F(SwapElements, 3, 1)                                                      \
-  F(MathPow, 2, 1)                                                           \
-  F(MathSin, 1, 1)                                                           \
-  F(MathCos, 1, 1)                                                           \
-  F(MathSqrt, 1, 1)                                                          \
-  F(IsRegExpEquivalent, 2, 1)                                                \
-  F(HasCachedArrayIndex, 1, 1)                                               \
-  F(GetCachedArrayIndex, 1, 1)
-
-
 #if V8_TARGET_ARCH_IA32
 #include "ia32/codegen-ia32.h"
 #elif V8_TARGET_ARCH_X64
index bf6d41d..f65f941 100755 (executable)
@@ -269,10 +269,19 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
   }
 
   if (result.is_null()) {
-    // No cache entry found. Do pre-parsing and compile the script.
+    // No cache entry found. Do pre-parsing, if it makes sense, and compile
+    // the script.
+    // Building preparse data that is only used immediately after is only a
+    // saving if we might skip building the AST for lazily compiled functions.
+    // I.e., preparse data isn't relevant when the lazy flag is off, and
+    // for small sources, odds are that there aren't many functions
+    // that would be compiled lazily anyway, so we skip the preparse step
+    // in that case too.
     ScriptDataImpl* pre_data = input_pre_data;
-    if (pre_data == NULL && source_length >= FLAG_min_preparse_length) {
-      pre_data = PreParse(source, NULL, extension);
+    if (pre_data == NULL
+        && FLAG_lazy
+        && source_length >= FLAG_min_preparse_length) {
+      pre_data = PartialPreParse(source, NULL, extension);
     }
 
     // Create a script object describing the script to be compiled.
index be353a3..e52cc94 100644 (file)
@@ -65,8 +65,10 @@ bool DateParser::Parse(Vector<Char> str, FixedArray* out) {
         tz.SetAbsoluteMinute(n);
       } else if (time.IsExpecting(n)) {
         time.AddFinal(n);
-        // Require end, white space or Z immediately after finalizing time.
-        if (!in.IsEnd() && !in.SkipWhiteSpace() && !in.Is('Z')) return false;
+        // Require end, white space, "Z", "+" or "-" immediately after
+        // finalizing time.
+        if (!in.IsEnd() && !in.SkipWhiteSpace() && !in.Is('Z') &&
+            !in.IsAsciiSign()) return false;
       } else {
         if (!day.Add(n)) return false;
         in.Skip('-');  // Ignore suffix '-' for year, month, or day.
index a63088d..263a2a4 100644 (file)
@@ -174,6 +174,10 @@ DEFINE_bool(enable_liveedit, true, "enable liveedit experimental feature")
 DEFINE_int(max_stack_trace_source_length, 300,
            "maximum length of function source code printed in a stack trace.")
 
+// full-codegen.cc
+DEFINE_bool(always_inline_smi_code, false,
+            "always inline smi code in non-opt code")
+
 // heap.cc
 DEFINE_int(max_new_space_size, 0, "max size of the new generation")
 DEFINE_int(max_old_space_size, 0, "max size of the old generation")
index 5ffebfb..a1c5ec3 100644 (file)
@@ -298,6 +298,11 @@ Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
 }
 
 
+MemOperand FullCodeGenerator::ContextOperand(Register context, int index) {
+  return CodeGenerator::ContextOperand(context, index);
+}
+
+
 int FullCodeGenerator::SlotOffset(Slot* slot) {
   ASSERT(slot != NULL);
   // Offset is negative because higher indexes are at lower addresses.
@@ -319,15 +324,11 @@ int FullCodeGenerator::SlotOffset(Slot* slot) {
 
 
 bool FullCodeGenerator::ShouldInlineSmiCase(Token::Value op) {
-  // TODO(kasperl): Once the compare stub allows leaving out the
-  // inlined smi case, we should get rid of this check.
-  if (Token::IsCompareOp(op)) return true;
-  // TODO(kasperl): Once the unary bit not stub allows leaving out
-  // the inlined smi case, we should get rid of this check.
-  if (op == Token::BIT_NOT) return true;
   // Inline smi case inside loops, but not division and modulo which
   // are too complicated and take up too much space.
-  return (op != Token::DIV) && (op != Token::MOD) && (loop_depth_ > 0);
+  if (op == Token::DIV ||op == Token::MOD) return false;
+  if (FLAG_always_inline_smi_code) return true;
+  return loop_depth_ > 0;
 }
 
 
@@ -500,18 +501,36 @@ void FullCodeGenerator::SetSourcePosition(int pos) {
 }
 
 
-void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
-  Handle<String> name = expr->name();
-  SmartPointer<char> cstring = name->ToCString();
+// Lookup table for code generators for  special runtime calls which are
+// generated inline.
+#define INLINE_FUNCTION_GENERATOR_ADDRESS(Name, argc, ressize)          \
+    &FullCodeGenerator::Emit##Name,
 
-#define CHECK_EMIT_INLINE_CALL(name, x, y) \
-  if (strcmp("_"#name, *cstring) == 0) {   \
-    Emit##name(expr->arguments());         \
-    return;                                \
-  }
-  INLINE_RUNTIME_FUNCTION_LIST(CHECK_EMIT_INLINE_CALL)
-#undef CHECK_EMIT_INLINE_CALL
-  UNREACHABLE();
+const FullCodeGenerator::InlineFunctionGenerator
+  FullCodeGenerator::kInlineFunctionGenerators[] = {
+    INLINE_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+    INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_ADDRESS)
+  };
+#undef INLINE_FUNCTION_GENERATOR_ADDRESS
+
+
+FullCodeGenerator::InlineFunctionGenerator
+  FullCodeGenerator::FindInlineFunctionGenerator(Runtime::FunctionId id) {
+    return kInlineFunctionGenerators[
+      static_cast<int>(id) - static_cast<int>(Runtime::kFirstInlineFunction)];
+}
+
+
+void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* node) {
+  ZoneList<Expression*>* args = node->arguments();
+  Handle<String> name = node->name();
+  Runtime::Function* function = node->function();
+  ASSERT(function != NULL);
+  ASSERT(function->intrinsic_type == Runtime::INLINE);
+  InlineFunctionGenerator generator =
+      FindInlineFunctionGenerator(function->function_id);
+  ASSERT(generator != NULL);
+  ((*this).*(generator))(args);
 }
 
 
index 840c825..9db233c 100644 (file)
@@ -243,6 +243,12 @@ class FullCodeGenerator: public AstVisitor {
     kRightConstant
   };
 
+  // Type of a member function that generates inline code for a native function.
+  typedef void (FullCodeGenerator::*InlineFunctionGenerator)
+      (ZoneList<Expression*>*);
+
+  static const InlineFunctionGenerator kInlineFunctionGenerators[];
+
   // Compute the frame pointer relative offset for a given local or
   // parameter slot.
   int SlotOffset(Slot* slot);
@@ -373,14 +379,25 @@ class FullCodeGenerator: public AstVisitor {
   void EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode);
 
   // Platform-specific code for inline runtime calls.
+  InlineFunctionGenerator FindInlineFunctionGenerator(Runtime::FunctionId id);
+
   void EmitInlineRuntimeCall(CallRuntime* expr);
 
 #define EMIT_INLINE_RUNTIME_CALL(name, x, y) \
   void Emit##name(ZoneList<Expression*>* arguments);
+  INLINE_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL)
   INLINE_RUNTIME_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL)
 #undef EMIT_INLINE_RUNTIME_CALL
 
   // Platform-specific code for loading variables.
+  void EmitLoadGlobalSlotCheckExtensions(Slot* slot,
+                                         TypeofState typeof_state,
+                                         Label* slow);
+  MemOperand ContextSlotOperandCheckExtensions(Slot* slot, Label* slow);
+  void EmitDynamicLoadFromSlotFastCase(Slot* slot,
+                                       TypeofState typeof_state,
+                                       Label* slow,
+                                       Label* done);
   void EmitVariableLoad(Variable* expr, Expression::Context context);
 
   // Platform-specific support for allocating a new closure based on
@@ -500,6 +517,9 @@ class FullCodeGenerator: public AstVisitor {
   // in v8::internal::Context.
   void LoadContextField(Register dst, int context_index);
 
+  // Create an operand for a context field.
+  MemOperand ContextOperand(Register context, int context_index);
+
   // AST node visit functions.
 #define DECLARE_VISIT(type) virtual void Visit##type(type* node);
   AST_NODE_LIST(DECLARE_VISIT)
index f089b85..a909caf 100644 (file)
@@ -372,13 +372,14 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
 
 int post_gc_processing_count = 0;
 
-void GlobalHandles::PostGarbageCollectionProcessing() {
+bool GlobalHandles::PostGarbageCollectionProcessing() {
   // Process weak global handle callbacks. This must be done after the
   // GC is completely done, because the callbacks may invoke arbitrary
   // API functions.
   // At the same time deallocate all DESTROYED nodes.
   ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
   const int initial_post_gc_processing_count = ++post_gc_processing_count;
+  bool weak_callback_invoked = false;
   Node** p = &head_;
   while (*p != NULL) {
     if ((*p)->PostGarbageCollectionProcessing()) {
@@ -389,6 +390,7 @@ void GlobalHandles::PostGarbageCollectionProcessing() {
         // restart the processing).
         break;
       }
+      weak_callback_invoked = true;
     }
     if ((*p)->state_ == Node::DESTROYED) {
       // Delete the link.
@@ -407,6 +409,7 @@ void GlobalHandles::PostGarbageCollectionProcessing() {
   if (first_deallocated()) {
     first_deallocated()->set_next(head());
   }
+  return weak_callback_invoked;
 }
 
 
index 659f86e..c4c59fd 100644 (file)
@@ -95,8 +95,9 @@ class GlobalHandles : public AllStatic {
   // Tells whether global handle is weak.
   static bool IsWeak(Object** location);
 
-  // Process pending weak handles.
-  static void PostGarbageCollectionProcessing();
+  // Process pending weak handles.  Returns true if any weak handle
+  // callback has been invoked.
+  static bool PostGarbageCollectionProcessing();
 
   // Iterates over all strong handles.
   static void IterateStrongRoots(ObjectVisitor* v);
index 0146401..655254c 100644 (file)
@@ -31,7 +31,6 @@
 #include "api.h"
 #include "arguments.h"
 #include "bootstrapper.h"
-#include "codegen.h"
 #include "compiler.h"
 #include "debug.h"
 #include "execution.h"
index 0d1ad5a..8f7dd3b 100644 (file)
 namespace v8 {
 namespace internal {
 
+void Heap::UpdateOldSpaceLimits() {
+  int old_gen_size = PromotedSpaceSize();
+  old_gen_promotion_limit_ =
+      old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3);
+  old_gen_allocation_limit_ =
+      old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2);
+  old_gen_exhausted_ = false;
+}
+
+
 int Heap::MaxObjectSizeInPagedSpace() {
   return Page::kMaxHeapObjectSize;
 }
@@ -403,7 +413,7 @@ void Heap::SetLastScriptId(Object* last_script_id) {
     }                                                                     \
     if (!__object__->IsRetryAfterGC()) RETURN_EMPTY;                      \
     Counters::gc_last_resort_from_handles.Increment();                    \
-    Heap::CollectAllGarbage(false);                                       \
+    Heap::CollectAllAvailableGarbage();                                   \
     {                                                                     \
       AlwaysAllocateScope __scope__;                                      \
       __object__ = FUNCTION_CALL;                                         \
index 443c926..650800f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -55,7 +55,6 @@ namespace internal {
 String* Heap::hidden_symbol_;
 Object* Heap::roots_[Heap::kRootListLength];
 
-
 NewSpace Heap::new_space_;
 OldSpace* Heap::old_pointer_space_ = NULL;
 OldSpace* Heap::old_data_space_ = NULL;
@@ -64,9 +63,6 @@ MapSpace* Heap::map_space_ = NULL;
 CellSpace* Heap::cell_space_ = NULL;
 LargeObjectSpace* Heap::lo_space_ = NULL;
 
-static const int kMinimumPromotionLimit = 2*MB;
-static const int kMinimumAllocationLimit = 8*MB;
-
 int Heap::old_gen_promotion_limit_ = kMinimumPromotionLimit;
 int Heap::old_gen_allocation_limit_ = kMinimumAllocationLimit;
 
@@ -405,17 +401,26 @@ void Heap::GarbageCollectionEpilogue() {
 }
 
 
-void Heap::CollectAllGarbage(bool force_compaction) {
+void Heap::CollectAllGarbage(bool force_compaction,
+                             CollectionPolicy collectionPolicy) {
   // Since we are ignoring the return value, the exact choice of space does
   // not matter, so long as we do not specify NEW_SPACE, which would not
   // cause a full GC.
   MarkCompactCollector::SetForceCompaction(force_compaction);
-  CollectGarbage(0, OLD_POINTER_SPACE);
+  CollectGarbage(0, OLD_POINTER_SPACE, collectionPolicy);
   MarkCompactCollector::SetForceCompaction(false);
 }
 
 
-bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {
+void Heap::CollectAllAvailableGarbage() {
+  CompilationCache::Clear();
+  CollectAllGarbage(true, AGGRESSIVE);
+}
+
+
+bool Heap::CollectGarbage(int requested_size,
+                          AllocationSpace space,
+                          CollectionPolicy collectionPolicy) {
   // The VM is in the GC state until exiting this function.
   VMState state(GC);
 
@@ -442,7 +447,7 @@ bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {
         ? &Counters::gc_scavenger
         : &Counters::gc_compactor;
     rate->Start();
-    PerformGarbageCollection(space, collector, &tracer);
+    PerformGarbageCollection(collector, &tracer, collectionPolicy);
     rate->Stop();
 
     GarbageCollectionEpilogue();
@@ -475,7 +480,7 @@ bool Heap::CollectGarbage(int requested_size, AllocationSpace space) {
 
 void Heap::PerformScavenge() {
   GCTracer tracer;
-  PerformGarbageCollection(NEW_SPACE, SCAVENGER, &tracer);
+  PerformGarbageCollection(SCAVENGER, &tracer, NORMAL);
 }
 
 
@@ -664,9 +669,9 @@ void Heap::UpdateSurvivalRateTrend(int start_new_space_size) {
   survival_rate_ = survival_rate;
 }
 
-void Heap::PerformGarbageCollection(AllocationSpace space,
-                                    GarbageCollector collector,
-                                    GCTracer* tracer) {
+void Heap::PerformGarbageCollection(GarbageCollector collector,
+                                    GCTracer* tracer,
+                                    CollectionPolicy collectionPolicy) {
   VerifySymbolTable();
   if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) {
     ASSERT(!allocation_allowed_);
@@ -696,25 +701,45 @@ void Heap::PerformGarbageCollection(AllocationSpace space,
 
     UpdateSurvivalRateTrend(start_new_space_size);
 
-    int old_gen_size = PromotedSpaceSize();
-    old_gen_promotion_limit_ =
-        old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3);
-    old_gen_allocation_limit_ =
-        old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2);
-
-    if (high_survival_rate_during_scavenges &&
-        IsStableOrIncreasingSurvivalTrend()) {
-      // Stable high survival rates of young objects both during partial and
-      // full collection indicate that mutator is either building or modifying
-      // a structure with a long lifetime.
-      // In this case we aggressively raise old generation memory limits to
-      // postpone subsequent mark-sweep collection and thus trade memory
-      // space for the mutation speed.
-      old_gen_promotion_limit_ *= 2;
-      old_gen_allocation_limit_ *= 2;
+    UpdateOldSpaceLimits();
+
+    // Major GC would invoke weak handle callbacks on weakly reachable
+    // handles, but won't collect weakly reachable objects until next
+    // major GC.  Therefore if we collect aggressively and weak handle callback
+    // has been invoked, we rerun major GC to release objects which become
+    // garbage.
+    if (collectionPolicy == AGGRESSIVE) {
+      // Note: as weak callbacks can execute arbitrary code, we cannot
+      // hope that eventually there will be no weak callbacks invocations.
+      // Therefore stop recollecting after several attempts.
+      const int kMaxNumberOfAttempts = 7;
+      for (int attempt = 0; attempt < kMaxNumberOfAttempts; attempt++) {
+        { DisableAssertNoAllocation allow_allocation;
+          GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+          if (!GlobalHandles::PostGarbageCollectionProcessing()) break;
+        }
+        MarkCompact(tracer);
+        // Weak handle callbacks can allocate data, so keep limits correct.
+        UpdateOldSpaceLimits();
+      }
+    } else {
+      if (high_survival_rate_during_scavenges &&
+          IsStableOrIncreasingSurvivalTrend()) {
+        // Stable high survival rates of young objects both during partial and
+        // full collection indicate that mutator is either building or modifying
+        // a structure with a long lifetime.
+        // In this case we aggressively raise old generation memory limits to
+        // postpone subsequent mark-sweep collection and thus trade memory
+        // space for the mutation speed.
+        old_gen_promotion_limit_ *= 2;
+        old_gen_allocation_limit_ *= 2;
+      }
     }
 
-    old_gen_exhausted_ = false;
+    { DisableAssertNoAllocation allow_allocation;
+      GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+      GlobalHandles::PostGarbageCollectionProcessing();
+    }
   } else {
     tracer_ = tracer;
     Scavenge();
@@ -725,12 +750,6 @@ void Heap::PerformGarbageCollection(AllocationSpace space,
 
   Counters::objs_since_last_young.Set(0);
 
-  if (collector == MARK_COMPACTOR) {
-    DisableAssertNoAllocation allow_allocation;
-    GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
-    GlobalHandles::PostGarbageCollectionProcessing();
-  }
-
   // Update relocatables.
   Relocatable::PostGarbageCollectionProcessing();
 
@@ -1834,6 +1853,13 @@ bool Heap::CreateInitialObjects() {
 
   CreateFixedStubs();
 
+  // Allocate the dictionary of intrinsic function names.
+  obj = StringDictionary::Allocate(Runtime::kNumFunctions);
+  if (obj->IsFailure()) return false;
+  obj = Runtime::InitializeIntrinsicFunctionNames(obj);
+  if (obj->IsFailure()) return false;
+  set_intrinsic_function_names(StringDictionary::cast(obj));
+
   if (InitializeNumberStringCache()->IsFailure()) return false;
 
   // Allocate cache for single character ASCII strings.
index 484cd22..cfb3b6a 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -114,6 +114,7 @@ namespace internal {
   V(Object, last_script_id, LastScriptId)                                      \
   V(Script, empty_script, EmptyScript)                                         \
   V(Smi, real_stack_limit, RealStackLimit)                                     \
+  V(StringDictionary, intrinsic_function_names, IntrinsicFunctionNames)        \
 
 #if V8_TARGET_ARCH_ARM && !V8_INTERPRETED_REGEXP
 #define STRONG_ROOT_LIST(V)                                                    \
@@ -686,13 +687,21 @@ class Heap : public AllStatic {
   static void GarbageCollectionPrologue();
   static void GarbageCollectionEpilogue();
 
+  enum CollectionPolicy { NORMAL, AGGRESSIVE };
+
   // Performs garbage collection operation.
   // Returns whether required_space bytes are available after the collection.
-  static bool CollectGarbage(int required_space, AllocationSpace space);
+  static bool CollectGarbage(int required_space,
+                             AllocationSpace space,
+                             CollectionPolicy collectionPolicy = NORMAL);
 
   // Performs a full garbage collection. Force compaction if the
   // parameter is true.
-  static void CollectAllGarbage(bool force_compaction);
+  static void CollectAllGarbage(bool force_compaction,
+                                CollectionPolicy collectionPolicy = NORMAL);
+
+  // Last hope GC, should try to squeeze as much as possible.
+  static void CollectAllAvailableGarbage();
 
   // Notify the heap that a context has been disposed.
   static int NotifyContextDisposed() { return ++contexts_disposed_; }
@@ -1213,9 +1222,14 @@ class Heap : public AllStatic {
   static GarbageCollector SelectGarbageCollector(AllocationSpace space);
 
   // Performs garbage collection
-  static void PerformGarbageCollection(AllocationSpace space,
-                                       GarbageCollector collector,
-                                       GCTracer* tracer);
+  static void PerformGarbageCollection(GarbageCollector collector,
+                                       GCTracer* tracer,
+                                       CollectionPolicy collectionPolicy);
+
+  static const int kMinimumPromotionLimit = 2 * MB;
+  static const int kMinimumAllocationLimit = 8 * MB;
+
+  inline static void UpdateOldSpaceLimits();
 
   // Allocate an uninitialized object in map space.  The behavior is identical
   // to Heap::AllocateRaw(size_in_bytes, MAP_SPACE), except that (a) it doesn't
index 2565acb..eef307d 100644 (file)
@@ -860,9 +860,14 @@ void Assembler::add(const Operand& dst, const Immediate& x) {
 
 
 void Assembler::and_(Register dst, int32_t imm32) {
+  and_(dst, Immediate(imm32));
+}
+
+
+void Assembler::and_(Register dst, const Immediate& x) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
-  emit_arith(4, Operand(dst), Immediate(imm32));
+  emit_arith(4, Operand(dst), x);
 }
 
 
index 8a5a4c5..928f172 100644 (file)
@@ -577,6 +577,7 @@ class Assembler : public Malloced {
   void add(const Operand& dst, const Immediate& x);
 
   void and_(Register dst, int32_t imm32);
+  void and_(Register dst, const Immediate& x);
   void and_(Register dst, const Operand& src);
   void and_(const Operand& src, Register dst);
   void and_(const Operand& dst, const Immediate& x);
index 366b91e..dccf36b 100644 (file)
@@ -1879,36 +1879,36 @@ void FloatingPointHelper::CheckFloatOperands(MacroAssembler* masm,
 
 
 void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
-  Label slow, done;
+  Label slow, done, undo;
 
   if (op_ == Token::SUB) {
-    // Check whether the value is a smi.
-    Label try_float;
-    __ test(eax, Immediate(kSmiTagMask));
-    __ j(not_zero, &try_float, not_taken);
-
-    if (negative_zero_ == kStrictNegativeZero) {
-      // Go slow case if the value of the expression is zero
-      // to make sure that we switch between 0 and -0.
-      __ test(eax, Operand(eax));
-      __ j(zero, &slow, not_taken);
-    }
+    if (include_smi_code_) {
+      // Check whether the value is a smi.
+      Label try_float;
+      __ test(eax, Immediate(kSmiTagMask));
+      __ j(not_zero, &try_float, not_taken);
 
-    // The value of the expression is a smi that is not zero.  Try
-    // optimistic subtraction '0 - value'.
-    Label undo;
-    __ mov(edx, Operand(eax));
-    __ Set(eax, Immediate(0));
-    __ sub(eax, Operand(edx));
-    __ j(no_overflow, &done, taken);
+      if (negative_zero_ == kStrictNegativeZero) {
+        // Go slow case if the value of the expression is zero
+        // to make sure that we switch between 0 and -0.
+        __ test(eax, Operand(eax));
+        __ j(zero, &slow, not_taken);
+      }
 
-    // Restore eax and go slow case.
-    __ bind(&undo);
-    __ mov(eax, Operand(edx));
-    __ jmp(&slow);
+      // The value of the expression is a smi that is not zero.  Try
+      // optimistic subtraction '0 - value'.
+      __ mov(edx, Operand(eax));
+      __ Set(eax, Immediate(0));
+      __ sub(eax, Operand(edx));
+      __ j(overflow, &undo, not_taken);
+      __ StubReturn(1);
+
+      // Try floating point case.
+      __ bind(&try_float);
+    } else if (FLAG_debug_code) {
+      __ AbortIfSmi(eax);
+    }
 
-    // Try floating point case.
-    __ bind(&try_float);
     __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
     __ cmp(edx, Factory::heap_number_map());
     __ j(not_equal, &slow);
@@ -1928,6 +1928,18 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
       __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx);
     }
   } else if (op_ == Token::BIT_NOT) {
+    if (include_smi_code_) {
+      Label non_smi;
+      __ test(eax, Immediate(kSmiTagMask));
+      __ j(not_zero, &non_smi);
+      __ not_(eax);
+      __ and_(eax, ~kSmiTagMask);  // Remove inverted smi-tag.
+      __ ret(0);
+      __ bind(&non_smi);
+    } else if (FLAG_debug_code) {
+      __ AbortIfSmi(eax);
+    }
+
     // Check if the operand is a heap number.
     __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
     __ cmp(edx, Factory::heap_number_map());
@@ -1978,6 +1990,10 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
   __ bind(&done);
   __ StubReturn(1);
 
+  // Restore eax and go slow case.
+  __ bind(&undo);
+  __ mov(eax, Operand(edx));
+
   // Handle the slow case by jumping to the JavaScript builtin.
   __ bind(&slow);
   __ pop(ecx);  // pop return address.
@@ -2613,6 +2629,27 @@ void CompareStub::Generate(MacroAssembler* masm) {
 
   Label check_unequal_objects, done;
 
+  // Compare two smis if required.
+  if (include_smi_compare_) {
+    Label non_smi, smi_done;
+    __ mov(ecx, Operand(edx));
+    __ or_(ecx, Operand(eax));
+    __ test(ecx, Immediate(kSmiTagMask));
+    __ j(not_zero, &non_smi, not_taken);
+    __ sub(edx, Operand(eax));  // Return on the result of the subtraction.
+    __ j(no_overflow, &smi_done);
+    __ neg(edx);  // Correct sign in case of overflow.
+    __ bind(&smi_done);
+    __ mov(eax, edx);
+    __ ret(0);
+    __ bind(&non_smi);
+  } else if (FLAG_debug_code) {
+    __ mov(ecx, Operand(edx));
+    __ or_(ecx, Operand(eax));
+    __ test(ecx, Immediate(kSmiTagMask));
+    __ Assert(not_zero, "Unexpected smi operands.");
+  }
+
   // NOTICE! This code is only reached after a smi-fast-case check, so
   // it is certain that at least one operand isn't a smi.
 
@@ -3501,7 +3538,8 @@ int CompareStub::MinorKey() {
          | RegisterField::encode(false)   // lhs_ and rhs_ are not used
          | StrictField::encode(strict_)
          | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
-         | IncludeNumberCompareField::encode(include_number_compare_);
+         | IncludeNumberCompareField::encode(include_number_compare_)
+         | IncludeSmiCompareField::encode(include_smi_compare_);
 }
 
 
@@ -3541,12 +3579,18 @@ const char* CompareStub::GetName() {
     include_number_compare_name = "_NO_NUMBER";
   }
 
+  const char* include_smi_compare_name = "";
+  if (!include_smi_compare_) {
+    include_smi_compare_name = "_NO_SMI";
+  }
+
   OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
-               "CompareStub_%s%s%s%s",
+               "CompareStub_%s%s%s%s%s",
                cc_name,
                strict_name,
                never_nan_nan_name,
-               include_number_compare_name);
+               include_number_compare_name,
+               include_smi_compare_name);
   return name_;
 }
 
index 854052a..86f3877 100644 (file)
@@ -2646,6 +2646,19 @@ static Condition DoubleCondition(Condition cc) {
 }
 
 
+static CompareFlags ComputeCompareFlags(NaNInformation nan_info,
+                                        bool inline_number_compare) {
+  CompareFlags flags = NO_SMI_COMPARE_IN_STUB;
+  if (nan_info == kCantBothBeNaN) {
+    flags = static_cast<CompareFlags>(flags | CANT_BOTH_BE_NAN);
+  }
+  if (inline_number_compare) {
+    flags = static_cast<CompareFlags>(flags | NO_NUMBER_COMPARE_IN_STUB);
+  }
+  return flags;
+}
+
+
 void CodeGenerator::Comparison(AstNode* node,
                                Condition cc,
                                bool strict,
@@ -2773,7 +2786,9 @@ void CodeGenerator::Comparison(AstNode* node,
 
       // Setup and call the compare stub.
       is_not_string.Bind(&left_side);
-      CompareStub stub(cc, strict, kCantBothBeNaN);
+      CompareFlags flags =
+          static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_COMPARE_IN_STUB);
+      CompareStub stub(cc, strict, flags);
       Result result = frame_->CallStub(&stub, &left_side, &right_side);
       result.ToRegister();
       __ cmp(result.reg(), 0);
@@ -2867,7 +2882,8 @@ void CodeGenerator::Comparison(AstNode* node,
 
       // End of in-line compare, call out to the compare stub. Don't include
       // number comparison in the stub if it was inlined.
-      CompareStub stub(cc, strict, nan_info, !inline_number_compare);
+      CompareFlags flags = ComputeCompareFlags(nan_info, inline_number_compare);
+      CompareStub stub(cc, strict, flags);
       Result answer = frame_->CallStub(&stub, &left_side, &right_side);
       __ test(answer.reg(), Operand(answer.reg()));
       answer.Unuse();
@@ -2900,7 +2916,9 @@ void CodeGenerator::Comparison(AstNode* node,
 
         // End of in-line compare, call out to the compare stub. Don't include
         // number comparison in the stub if it was inlined.
-        CompareStub stub(cc, strict, nan_info, !inline_number_compare);
+        CompareFlags flags =
+            ComputeCompareFlags(nan_info, inline_number_compare);
+        CompareStub stub(cc, strict, flags);
         Result answer = frame_->CallStub(&stub, &left_side, &right_side);
         __ test(answer.reg(), Operand(answer.reg()));
         answer.Unuse();
@@ -2994,7 +3012,6 @@ void CodeGenerator::ConstantSmiComparison(Condition cc,
         dest->false_target()->Branch(zero);
       } else {
         // Do the smi check, then the comparison.
-        JumpTarget is_not_smi;
         __ test(left_reg, Immediate(kSmiTagMask));
         is_smi.Branch(zero, left_side, right_side);
       }
@@ -3031,7 +3048,9 @@ void CodeGenerator::ConstantSmiComparison(Condition cc,
       }
 
       // Setup and call the compare stub.
-      CompareStub stub(cc, strict, kCantBothBeNaN);
+      CompareFlags flags =
+          static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_CODE_IN_STUB);
+      CompareStub stub(cc, strict, flags);
       Result result = frame_->CallStub(&stub, left_side, right_side);
       result.ToRegister();
       __ test(result.reg(), Operand(result.reg()));
@@ -8146,6 +8165,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
           GenericUnaryOpStub stub(
               Token::SUB,
               overwrite,
+              NO_UNARY_FLAGS,
               no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
           Result operand = frame_->Pop();
           Result answer = frame_->CallStub(&stub, &operand);
@@ -8173,7 +8193,9 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
             __ test(operand.reg(), Immediate(kSmiTagMask));
             smi_label.Branch(zero, &operand, taken);
 
-            GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
+            GenericUnaryOpStub stub(Token::BIT_NOT,
+                                    overwrite,
+                                    NO_UNARY_SMI_CODE_IN_STUB);
             Result answer = frame_->CallStub(&stub, &operand);
             continue_label.Jump(&answer);
 
index adc0005..c4a03d1 100644 (file)
@@ -345,10 +345,6 @@ class CodeGenerator: public AstVisitor {
   bool in_spilled_code() const { return in_spilled_code_; }
   void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; }
 
-  // If the name is an inline runtime function call return the number of
-  // expected arguments. Otherwise return -1.
-  static int InlineRuntimeCallArgumentsCount(Handle<String> name);
-
   // Return a position of the element at |index_as_smi| + |additional_offset|
   // in FixedArray pointer to which is held in |array|.  |index_as_smi| is Smi.
   static Operand FixedArrayElementOperand(Register array,
@@ -363,6 +359,12 @@ class CodeGenerator: public AstVisitor {
   }
 
  private:
+  // Type of a member function that generates inline code for a native function.
+  typedef void (CodeGenerator::*InlineFunctionGenerator)
+      (ZoneList<Expression*>*);
+
+  static const InlineFunctionGenerator kInlineFunctionGenerators[];
+
   // Construction/Destruction
   explicit CodeGenerator(MacroAssembler* masm);
 
@@ -624,13 +626,9 @@ class CodeGenerator: public AstVisitor {
 
   void CheckStack();
 
-  struct InlineRuntimeLUT {
-    void (CodeGenerator::*method)(ZoneList<Expression*>*);
-    const char* name;
-    int nargs;
-  };
+  static InlineFunctionGenerator FindInlineFunctionGenerator(
+      Runtime::FunctionId function_id);
 
-  static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name);
   bool CheckForInlineRuntimeCall(CallRuntime* node);
 
   void ProcessDeclarations(ZoneList<Declaration*>* declarations);
@@ -792,8 +790,6 @@ class CodeGenerator: public AstVisitor {
   // in a spilled state.
   bool in_spilled_code_;
 
-  static InlineRuntimeLUT kInlineRuntimeLUT[];
-
   friend class VirtualFrame;
   friend class JumpTarget;
   friend class Reference;
index 1631b04..1e65c4b 100644 (file)
@@ -514,7 +514,7 @@ MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) {
       int context_chain_length =
           scope()->ContextChainLength(slot->var()->scope());
       __ LoadContext(scratch, context_chain_length);
-      return CodeGenerator::ContextOperand(scratch, slot->index());
+      return ContextOperand(scratch, slot->index());
     }
     case Slot::LOOKUP:
       UNREACHABLE();
@@ -574,19 +574,17 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
         ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
         if (FLAG_debug_code) {
           // Check if we have the correct context pointer.
-          __ mov(ebx,
-                 CodeGenerator::ContextOperand(esi, Context::FCONTEXT_INDEX));
+          __ mov(ebx, ContextOperand(esi, Context::FCONTEXT_INDEX));
           __ cmp(ebx, Operand(esi));
           __ Check(equal, "Unexpected declaration in current context.");
         }
         if (mode == Variable::CONST) {
-          __ mov(CodeGenerator::ContextOperand(esi, slot->index()),
+          __ mov(ContextOperand(esi, slot->index()),
                  Immediate(Factory::the_hole_value()));
           // No write barrier since the hole value is in old space.
         } else if (function != NULL) {
           VisitForValue(function, kAccumulator);
-          __ mov(CodeGenerator::ContextOperand(esi, slot->index()),
-                 result_register());
+          __ mov(ContextOperand(esi, slot->index()), result_register());
           int offset = Context::SlotOffset(slot->index());
           __ mov(ebx, esi);
           __ RecordWrite(ebx, offset, result_register(), ecx);
@@ -686,7 +684,8 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
 
     // Perform the comparison as if via '==='.
     __ mov(edx, Operand(esp, 0));  // Switch value.
-    if (ShouldInlineSmiCase(Token::EQ_STRICT)) {
+    bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+    if (inline_smi_code) {
       Label slow_case;
       __ mov(ecx, edx);
       __ or_(ecx, Operand(eax));
@@ -699,7 +698,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
       __ bind(&slow_case);
     }
 
-    CompareStub stub(equal, true);
+    CompareFlags flags = inline_smi_code
+        ? NO_SMI_COMPARE_IN_STUB
+        : NO_COMPARE_FLAGS;
+    CompareStub stub(equal, true, flags);
     __ CallStub(&stub);
     __ test(eax, Operand(eax));
     __ j(not_equal, &next_test);
@@ -758,13 +760,57 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
   __ bind(&done_convert);
   __ push(eax);
 
-  // TODO(kasperl): Check cache validity in generated code. This is a
-  // fast case for the JSObject::IsSimpleEnum cache validity
-  // checks. If we cannot guarantee cache validity, call the runtime
-  // system to check cache validity or get the property names in a
-  // fixed array.
+  // Check cache validity in generated code. This is a fast case for
+  // the JSObject::IsSimpleEnum cache validity checks. If we cannot
+  // guarantee cache validity, call the runtime system to check cache
+  // validity or get the property names in a fixed array.
+  Label next, call_runtime;
+  __ mov(ecx, eax);
+  __ bind(&next);
+
+  // Check that there are no elements.  Register ecx contains the
+  // current JS object we've reached through the prototype chain.
+  __ cmp(FieldOperand(ecx, JSObject::kElementsOffset),
+         Factory::empty_fixed_array());
+  __ j(not_equal, &call_runtime);
+
+  // Check that instance descriptors are not empty so that we can
+  // check for an enum cache.  Leave the map in ebx for the subsequent
+  // prototype load.
+  __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
+  __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset));
+  __ cmp(edx, Factory::empty_descriptor_array());
+  __ j(equal, &call_runtime);
+
+  // Check that there in an enum cache in the non-empty instance
+  // descriptors (edx).  This is the case if the next enumeration
+  // index field does not contain a smi.
+  __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset));
+  __ test(edx, Immediate(kSmiTagMask));
+  __ j(zero, &call_runtime);
+
+  // For all objects but the receiver, check that the cache is empty.
+  Label check_prototype;
+  __ cmp(ecx, Operand(eax));
+  __ j(equal, &check_prototype);
+  __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset));
+  __ cmp(edx, Factory::empty_fixed_array());
+  __ j(not_equal, &call_runtime);
+
+  // Load the prototype from the map and loop if non-null.
+  __ bind(&check_prototype);
+  __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset));
+  __ cmp(ecx, Factory::null_value());
+  __ j(not_equal, &next);
+
+  // The enum cache is valid.  Load the map of the object being
+  // iterated over and use the cache for the iteration.
+  Label use_cache;
+  __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
+  __ jmp(&use_cache);
 
   // Get the set of properties to enumerate.
+  __ bind(&call_runtime);
   __ push(eax);  // Duplicate the enumerable object on the stack.
   __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
 
@@ -776,6 +822,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
   __ j(not_equal, &fixed_array);
 
   // We got a map in register eax. Get the enumeration cache from it.
+  __ bind(&use_cache);
   __ mov(ecx, FieldOperand(eax, Map::kInstanceDescriptorsOffset));
   __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
   __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
@@ -885,6 +932,152 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
 }
 
 
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
+    Slot* slot,
+    TypeofState typeof_state,
+    Label* slow) {
+  Register context = esi;
+  Register temp = edx;
+
+  Scope* s = scope();
+  while (s != NULL) {
+    if (s->num_heap_slots() > 0) {
+      if (s->calls_eval()) {
+        // Check that extension is NULL.
+        __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
+               Immediate(0));
+        __ j(not_equal, slow);
+      }
+      // Load next context in chain.
+      __ mov(temp, ContextOperand(context, Context::CLOSURE_INDEX));
+      __ mov(temp, FieldOperand(temp, JSFunction::kContextOffset));
+      // Walk the rest of the chain without clobbering esi.
+      context = temp;
+    }
+    // If no outer scope calls eval, we do not need to check more
+    // context extensions.  If we have reached an eval scope, we check
+    // all extensions from this point.
+    if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
+    s = s->outer_scope();
+  }
+
+  if (s != NULL && s->is_eval_scope()) {
+    // Loop up the context chain.  There is no frame effect so it is
+    // safe to use raw labels here.
+    Label next, fast;
+    if (!context.is(temp)) {
+      __ mov(temp, context);
+    }
+    __ bind(&next);
+    // Terminate at global context.
+    __ cmp(FieldOperand(temp, HeapObject::kMapOffset),
+           Immediate(Factory::global_context_map()));
+    __ j(equal, &fast);
+    // Check that extension is NULL.
+    __ cmp(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0));
+    __ j(not_equal, slow);
+    // Load next context in chain.
+    __ mov(temp, ContextOperand(temp, Context::CLOSURE_INDEX));
+    __ mov(temp, FieldOperand(temp, JSFunction::kContextOffset));
+    __ jmp(&next);
+    __ bind(&fast);
+  }
+
+  // All extension objects were empty and it is safe to use a global
+  // load IC call.
+  __ mov(eax, CodeGenerator::GlobalObject());
+  __ mov(ecx, slot->var()->name());
+  Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+      ? RelocInfo::CODE_TARGET
+      : RelocInfo::CODE_TARGET_CONTEXT;
+  __ call(ic, mode);
+  __ nop();  // Signal no inlined code.
+}
+
+
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
+    Slot* slot,
+    Label* slow) {
+  ASSERT(slot->type() == Slot::CONTEXT);
+  Register context = esi;
+  Register temp = ebx;
+
+  for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
+    if (s->num_heap_slots() > 0) {
+      if (s->calls_eval()) {
+        // Check that extension is NULL.
+        __ cmp(ContextOperand(context, Context::EXTENSION_INDEX),
+               Immediate(0));
+        __ j(not_equal, slow);
+      }
+      __ mov(temp, ContextOperand(context, Context::CLOSURE_INDEX));
+      __ mov(temp, FieldOperand(temp, JSFunction::kContextOffset));
+      // Walk the rest of the chain without clobbering esi.
+      context = temp;
+    }
+  }
+  // Check that last extension is NULL.
+  __ cmp(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
+  __ j(not_equal, slow);
+  __ mov(temp, ContextOperand(context, Context::FCONTEXT_INDEX));
+  return ContextOperand(temp, slot->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
+    Slot* slot,
+    TypeofState typeof_state,
+    Label* slow,
+    Label* done) {
+  // Generate fast-case code for variables that might be shadowed by
+  // eval-introduced variables.  Eval is used a lot without
+  // introducing variables.  In those cases, we do not want to
+  // perform a runtime call for all variables in the scope
+  // containing the eval.
+  if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
+    EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow);
+    __ jmp(done);
+  } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
+    Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
+    Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
+    if (potential_slot != NULL) {
+      // Generate fast case for locals that rewrite to slots.
+      __ mov(eax,
+             ContextSlotOperandCheckExtensions(potential_slot, slow));
+      if (potential_slot->var()->mode() == Variable::CONST) {
+        __ cmp(eax, Factory::the_hole_value());
+        __ j(not_equal, done);
+        __ mov(eax, Factory::undefined_value());
+      }
+      __ jmp(done);
+    } else if (rewrite != NULL) {
+      // Generate fast case for calls of an argument function.
+      Property* property = rewrite->AsProperty();
+      if (property != NULL) {
+        VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+        Literal* key_literal = property->key()->AsLiteral();
+        if (obj_proxy != NULL &&
+            key_literal != NULL &&
+            obj_proxy->IsArguments() &&
+            key_literal->handle()->IsSmi()) {
+          // Load arguments object if there are no eval-introduced
+          // variables. Then load the argument from the arguments
+          // object using keyed load.
+          __ mov(edx,
+                 ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                   slow));
+          __ mov(eax, Immediate(key_literal->handle()));
+          Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+          __ call(ic, RelocInfo::CODE_TARGET);
+          __ jmp(done);
+        }
+      }
+    }
+  }
+}
+
+
 void FullCodeGenerator::EmitVariableLoad(Variable* var,
                                          Expression::Context context) {
   // Four cases: non-this global variables, lookup slots, all other
@@ -909,10 +1102,19 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
     Apply(context, eax);
 
   } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
+    Label done, slow;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done);
+
+    __ bind(&slow);
     Comment cmnt(masm_, "Lookup slot");
     __ push(esi);  // Context.
     __ push(Immediate(var->name()));
     __ CallRuntime(Runtime::kLoadContextSlot, 2);
+    __ bind(&done);
+
     Apply(context, eax);
 
   } else if (slot != NULL) {
@@ -1953,14 +2155,40 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
   } else if (var != NULL && var->slot() != NULL &&
              var->slot()->type() == Slot::LOOKUP) {
-    // Call to a lookup slot (dynamically introduced variable).  Call the
-    // runtime to find the function to call (returned in eax) and the object
-    // holding it (returned in edx).
+    // Call to a lookup slot (dynamically introduced variable).
+    Label slow, done;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    EmitDynamicLoadFromSlotFastCase(var->slot(),
+                                    NOT_INSIDE_TYPEOF,
+                                    &slow,
+                                    &done);
+
+    __ bind(&slow);
+    // Call the runtime to find the function to call (returned in eax)
+    // and the object holding it (returned in edx).
     __ push(context_register());
     __ push(Immediate(var->name()));
     __ CallRuntime(Runtime::kLoadContextSlot, 2);
     __ push(eax);  // Function.
     __ push(edx);  // Receiver.
+
+    // If fast case code has been generated, emit code to push the
+    // function and receiver and have the slow path jump around this
+    // code.
+    if (done.is_linked()) {
+      Label call;
+      __ jmp(&call);
+      __ bind(&done);
+      // Push function.
+      __ push(eax);
+      // Push global receiver.
+      __ mov(ebx, CodeGenerator::GlobalObject());
+      __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
+      __ bind(&call);
+    }
+
     EmitCallWithStub(expr);
   } else if (fun->AsProperty() != NULL) {
     // Call to an object property.
@@ -2781,12 +3009,10 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
   Register key = eax;
   Register cache = ebx;
   Register tmp = ecx;
-  __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX));
+  __ mov(cache, ContextOperand(esi, Context::GLOBAL_INDEX));
   __ mov(cache,
          FieldOperand(cache, GlobalObject::kGlobalContextOffset));
-  __ mov(cache,
-         CodeGenerator::ContextOperand(
-             cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+  __ mov(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
   __ mov(cache,
          FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
 
@@ -2917,7 +3143,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
     InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
     Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
     __ call(ic, RelocInfo::CODE_TARGET);
-      // Restore context register.
+    // Restore context register.
     __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
   } else {
     // Call the C runtime function.
@@ -3036,7 +3262,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
       UnaryOverwriteMode overwrite =
           can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-      GenericUnaryOpStub stub(Token::SUB, overwrite);
+      GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
       // GenericUnaryOpStub expects the argument to be in the
       // accumulator register eax.
       VisitForValue(expr->expression(), kAccumulator);
@@ -3051,7 +3277,8 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       // in the accumulator register eax.
       VisitForValue(expr->expression(), kAccumulator);
       Label done;
-      if (ShouldInlineSmiCase(expr->op())) {
+      bool inline_smi_case = ShouldInlineSmiCase(expr->op());
+      if (inline_smi_case) {
         Label call_stub;
         __ test(eax, Immediate(kSmiTagMask));
         __ j(not_zero, &call_stub);
@@ -3063,7 +3290,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       bool overwrite = expr->expression()->ResultOverwriteAllowed();
       UnaryOverwriteMode mode =
           overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-      GenericUnaryOpStub stub(Token::BIT_NOT, mode);
+      UnaryOpFlags flags = inline_smi_case
+          ? NO_UNARY_SMI_CODE_IN_STUB
+          : NO_UNARY_FLAGS;
+      GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
       __ CallStub(&stub);
       __ bind(&done);
       Apply(context_, eax);
@@ -3262,13 +3492,24 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) {
     // Use a regular load, not a contextual load, to avoid a reference
     // error.
     __ call(ic, RelocInfo::CODE_TARGET);
+    __ nop();  // Signal no inlined code.
     if (where == kStack) __ push(eax);
   } else if (proxy != NULL &&
              proxy->var()->slot() != NULL &&
              proxy->var()->slot()->type() == Slot::LOOKUP) {
+    Label done, slow;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    Slot* slot = proxy->var()->slot();
+    EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done);
+
+    __ bind(&slow);
     __ push(esi);
     __ push(Immediate(proxy->name()));
     __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+    __ bind(&done);
+
     if (where == kStack) __ push(eax);
   } else {
     // This expression cannot throw a reference error at the top level.
@@ -3441,7 +3682,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
           UNREACHABLE();
       }
 
-      if (ShouldInlineSmiCase(op)) {
+      bool inline_smi_code = ShouldInlineSmiCase(op);
+      if (inline_smi_code) {
         Label slow_case;
         __ mov(ecx, Operand(edx));
         __ or_(ecx, Operand(eax));
@@ -3452,7 +3694,10 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
         __ bind(&slow_case);
       }
 
-      CompareStub stub(cc, strict);
+      CompareFlags flags = inline_smi_code
+          ? NO_SMI_COMPARE_IN_STUB
+          : NO_COMPARE_FLAGS;
+      CompareStub stub(cc, strict, flags);
       __ CallStub(&stub);
       __ test(eax, Operand(eax));
       Split(cc, if_true, if_false, fall_through);
@@ -3512,7 +3757,7 @@ void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
 
 
 void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
-  __ mov(dst, CodeGenerator::ContextOperand(esi, context_index));
+  __ mov(dst, ContextOperand(esi, context_index));
 }
 
 
index 7fc3f81..828e71a 100644 (file)
@@ -1255,6 +1255,61 @@ void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
 }
 
 
+void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
+                                                   JSObject* holder,
+                                                   String* name,
+                                                   Label* miss) {
+  ASSERT(holder->IsGlobalObject());
+
+  // Get the number of arguments.
+  const int argc = arguments().immediate();
+
+  // Get the receiver from the stack.
+  __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+  // If the object is the holder then we know that it's a global
+  // object which can only happen for contextual calls. In this case,
+  // the receiver cannot be a smi.
+  if (object != holder) {
+    __ test(edx, Immediate(kSmiTagMask));
+    __ j(zero, miss, not_taken);
+  }
+
+  // Check that the maps haven't changed.
+  CheckPrototypes(object, edx, holder, ebx, eax, edi, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
+                                                    JSFunction* function,
+                                                    Label* miss) {
+  // Get the value from the cell.
+  __ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell)));
+  __ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));
+
+  // Check that the cell contains the same function.
+  if (Heap::InNewSpace(function)) {
+    // We can't embed a pointer to a function in new space so we have
+    // to verify that the shared function info is unchanged. This has
+    // the nice side effect that multiple closures based on the same
+    // function can all use this call IC. Before we load through the
+    // function, we have to verify that it still is a function.
+    __ test(edi, Immediate(kSmiTagMask));
+    __ j(zero, miss, not_taken);
+    __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
+    __ j(not_equal, miss, not_taken);
+
+    // Check the shared function info. Make sure it hasn't changed.
+    __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
+           Immediate(Handle<SharedFunctionInfo>(function->shared())));
+    __ j(not_equal, miss, not_taken);
+  } else {
+    __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
+    __ j(not_equal, miss, not_taken);
+  }
+}
+
+
 Object* CallStubCompiler::GenerateMissBranch() {
   Object* obj = StubCache::ComputeCallMiss(arguments().immediate(), kind_);
   if (obj->IsFailure()) return obj;
@@ -1320,9 +1375,9 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
 
 Object* CallStubCompiler::CompileArrayPushCall(Object* object,
                                                JSObject* holder,
+                                               JSGlobalPropertyCell* cell,
                                                JSFunction* function,
-                                               String* name,
-                                               CheckType check) {
+                                               String* name) {
   // ----------- S t a t e -------------
   //  -- ecx                 : name
   //  -- esp[0]              : return address
@@ -1330,12 +1385,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
   //  -- ...
   //  -- esp[(argc + 1) * 4] : receiver
   // -----------------------------------
-  ASSERT(check == RECEIVER_MAP_CHECK);
 
   // If object is not an array, bail out to regular call.
-  if (!object->IsJSArray()) {
-    return Heap::undefined_value();
-  }
+  if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
 
   Label miss;
 
@@ -1469,9 +1521,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
 
 Object* CallStubCompiler::CompileArrayPopCall(Object* object,
                                               JSObject* holder,
+                                              JSGlobalPropertyCell* cell,
                                               JSFunction* function,
-                                              String* name,
-                                              CheckType check) {
+                                              String* name) {
   // ----------- S t a t e -------------
   //  -- ecx                 : name
   //  -- esp[0]              : return address
@@ -1479,12 +1531,9 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
   //  -- ...
   //  -- esp[(argc + 1) * 4] : receiver
   // -----------------------------------
-  ASSERT(check == RECEIVER_MAP_CHECK);
 
   // If object is not an array, bail out to regular call.
-  if (!object->IsJSArray()) {
-    return Heap::undefined_value();
-  }
+  if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
 
   Label miss, return_undefined, call_builtin;
 
@@ -1551,11 +1600,12 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
 }
 
 
-Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
-                                                      JSObject* holder,
-                                                      JSFunction* function,
-                                                      String* name,
-                                                      CheckType check) {
+Object* CallStubCompiler::CompileStringCharCodeAtCall(
+    Object* object,
+    JSObject* holder,
+    JSGlobalPropertyCell* cell,
+    JSFunction* function,
+    String* name) {
   // ----------- S t a t e -------------
   //  -- ecx                 : function name
   //  -- esp[0]              : return address
@@ -1565,7 +1615,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
   // -----------------------------------
 
   // If object is not a string, bail out to regular call.
-  if (!object->IsString()) return Heap::undefined_value();
+  if (!object->IsString() || cell != NULL) return Heap::undefined_value();
 
   const int argc = arguments().immediate();
 
@@ -1621,9 +1671,9 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
 
 Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
                                                   JSObject* holder,
+                                                  JSGlobalPropertyCell* cell,
                                                   JSFunction* function,
-                                                  String* name,
-                                                  CheckType check) {
+                                                  String* name) {
   // ----------- S t a t e -------------
   //  -- ecx                 : function name
   //  -- esp[0]              : return address
@@ -1633,7 +1683,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
   // -----------------------------------
 
   // If object is not a string, bail out to regular call.
-  if (!object->IsString()) return Heap::undefined_value();
+  if (!object->IsString() || cell != NULL) return Heap::undefined_value();
 
   const int argc = arguments().immediate();
 
@@ -1690,6 +1740,79 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
 }
 
 
+Object* CallStubCompiler::CompileStringFromCharCodeCall(
+    Object* object,
+    JSObject* holder,
+    JSGlobalPropertyCell* cell,
+    JSFunction* function,
+    String* name) {
+  // ----------- S t a t e -------------
+  //  -- ecx                 : function name
+  //  -- esp[0]              : return address
+  //  -- esp[(argc - n) * 4] : arg[n] (zero-based)
+  //  -- ...
+  //  -- esp[(argc + 1) * 4] : receiver
+  // -----------------------------------
+
+  const int argc = arguments().immediate();
+
+  // If the object is not a JSObject or we got an unexpected number of
+  // arguments, bail out to the regular call.
+  if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
+
+  Label miss;
+  GenerateNameCheck(name, &miss);
+
+  if (cell == NULL) {
+    __ mov(edx, Operand(esp, 2 * kPointerSize));
+
+    STATIC_ASSERT(kSmiTag == 0);
+    __ test(edx, Immediate(kSmiTagMask));
+    __ j(zero, &miss);
+
+    CheckPrototypes(JSObject::cast(object), edx, holder, ebx, eax, edi, name,
+                    &miss);
+  } else {
+    ASSERT(cell->value() == function);
+    GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+    GenerateLoadFunctionFromCell(cell, function, &miss);
+  }
+
+  // Load the char code argument.
+  Register code = ebx;
+  __ mov(code, Operand(esp, 1 * kPointerSize));
+
+  // Check the code is a smi.
+  Label slow;
+  STATIC_ASSERT(kSmiTag == 0);
+  __ test(code, Immediate(kSmiTagMask));
+  __ j(not_zero, &slow);
+
+  // Convert the smi code to uint16.
+  __ and_(code, Immediate(Smi::FromInt(0xffff)));
+
+  StringCharFromCodeGenerator char_from_code_generator(code, eax);
+  char_from_code_generator.GenerateFast(masm());
+  __ ret(2 * kPointerSize);
+
+  ICRuntimeCallHelper call_helper;
+  char_from_code_generator.GenerateSlow(masm(), call_helper);
+
+  // Tail call the full function. We do not have to patch the receiver
+  // because the function makes no use of it.
+  __ bind(&slow);
+  __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+  __ bind(&miss);
+  // ecx: function name.
+  Object* obj = GenerateMissBranch();
+  if (obj->IsFailure()) return obj;
+
+  // Return the generated code.
+  return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
+}
+
+
 Object* CallStubCompiler::CompileCallConstant(Object* object,
                                               JSObject* holder,
                                               JSFunction* function,
@@ -1706,12 +1829,10 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
   SharedFunctionInfo* function_info = function->shared();
   if (function_info->HasCustomCallGenerator()) {
     const int id = function_info->custom_call_generator_id();
-    Object* result =
-        CompileCustomCall(id, object, holder, function, name, check);
+    Object* result = CompileCustomCall(
+        id, object, holder, NULL, function, name);
     // undefined means bail out to regular compiler.
-    if (!result->IsUndefined()) {
-      return result;
-    }
+    if (!result->IsUndefined()) return result;
   }
 
   Label miss_in_smi_check;
@@ -1922,6 +2043,16 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   //  -- ...
   //  -- esp[(argc + 1) * 4] : receiver
   // -----------------------------------
+
+  SharedFunctionInfo* function_info = function->shared();
+  if (function_info->HasCustomCallGenerator()) {
+    const int id = function_info->custom_call_generator_id();
+    Object* result = CompileCustomCall(
+        id, object, holder, cell, function, name);
+    // undefined means bail out to regular compiler.
+    if (!result->IsUndefined()) return result;
+  }
+
   Label miss;
 
   GenerateNameCheck(name, &miss);
@@ -1929,44 +2060,9 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // Get the number of arguments.
   const int argc = arguments().immediate();
 
-  // Get the receiver from the stack.
-  __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+  GenerateGlobalReceiverCheck(object, holder, name, &miss);
 
-  // If the object is the holder then we know that it's a global
-  // object which can only happen for contextual calls. In this case,
-  // the receiver cannot be a smi.
-  if (object != holder) {
-    __ test(edx, Immediate(kSmiTagMask));
-    __ j(zero, &miss, not_taken);
-  }
-
-  // Check that the maps haven't changed.
-  CheckPrototypes(object, edx, holder, ebx, eax, edi, name, &miss);
-
-  // Get the value from the cell.
-  __ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell)));
-  __ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));
-
-  // Check that the cell contains the same function.
-  if (Heap::InNewSpace(function)) {
-    // We can't embed a pointer to a function in new space so we have
-    // to verify that the shared function info is unchanged. This has
-    // the nice side effect that multiple closures based on the same
-    // function can all use this call IC. Before we load through the
-    // function, we have to verify that it still is a function.
-    __ test(edi, Immediate(kSmiTagMask));
-    __ j(zero, &miss, not_taken);
-    __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
-    __ j(not_equal, &miss, not_taken);
-
-    // Check the shared function info. Make sure it hasn't changed.
-    __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
-           Immediate(Handle<SharedFunctionInfo>(function->shared())));
-    __ j(not_equal, &miss, not_taken);
-  } else {
-    __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
-    __ j(not_equal, &miss, not_taken);
-  }
+  GenerateLoadFunctionFromCell(cell, function, &miss);
 
   // Patch the receiver on the stack with the global proxy.
   if (object->IsGlobalObject()) {
index 5a8749e..41523a8 100644 (file)
@@ -617,9 +617,33 @@ class FunctionInfoListener {
     current_parent_index_ = info.GetParentIndex();
   }
 
-// TODO(LiveEdit): Move private method below.
-//     This private section was created here to avoid moving the function
-//      to keep already complex diff simpler.
+ public:
+  // Saves only function code, because for a script function we
+  // may never create a SharedFunctionInfo object.
+  void FunctionCode(Handle<Code> function_code) {
+    FunctionInfoWrapper info =
+        FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
+    info.SetFunctionCode(function_code, Handle<Object>(Heap::null_value()));
+  }
+
+  // Saves full information about a function: its code, its scope info
+  // and a SharedFunctionInfo object.
+  void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope) {
+    if (!shared->IsSharedFunctionInfo()) {
+      return;
+    }
+    FunctionInfoWrapper info =
+        FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
+    info.SetFunctionCode(Handle<Code>(shared->code()),
+        Handle<Object>(shared->scope_info()));
+    info.SetSharedFunctionInfo(shared);
+
+    Handle<Object> scope_info_list(SerializeFunctionScope(scope));
+    info.SetOuterScopeInfo(scope_info_list);
+  }
+
+  Handle<JSArray> GetResult() { return result_; }
+
  private:
   Object* SerializeFunctionScope(Scope* scope) {
     HandleScope handle_scope;
@@ -676,36 +700,6 @@ class FunctionInfoListener {
     return *scope_info_list;
   }
 
- public:
-  // Saves only function code, because for a script function we
-  // may never create a SharedFunctionInfo object.
-  void FunctionCode(Handle<Code> function_code) {
-    FunctionInfoWrapper info =
-        FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
-    info.SetFunctionCode(function_code, Handle<Object>(Heap::null_value()));
-  }
-
-  // Saves full information about a function: its code, its scope info
-  // and a SharedFunctionInfo object.
-  void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope) {
-    if (!shared->IsSharedFunctionInfo()) {
-      return;
-    }
-    FunctionInfoWrapper info =
-        FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
-    info.SetFunctionCode(Handle<Code>(shared->code()),
-        Handle<Object>(shared->scope_info()));
-    info.SetSharedFunctionInfo(shared);
-
-    Handle<Object> scope_info_list(SerializeFunctionScope(scope));
-    info.SetOuterScopeInfo(scope_info_list);
-  }
-
-  Handle<JSArray> GetResult() {
-    return result_;
-  }
-
- private:
   Handle<JSArray> result_;
   int len_;
   int current_parent_index_;
index 7667e89..856c474 100644 (file)
@@ -872,11 +872,14 @@ class ParserLog BASE_EMBEDDED {
   // Records the occurrence of a function.
   virtual FunctionEntry LogFunction(int start) { return FunctionEntry(); }
   virtual void LogSymbol(int start, Vector<const char> symbol) {}
+  virtual void LogError() { }
   // Return the current position in the function entry log.
   virtual int function_position() { return 0; }
   virtual int symbol_position() { return 0; }
   virtual int symbol_ids() { return 0; }
-  virtual void LogError() { }
+  virtual Vector<unsigned> ExtractData() {
+    return Vector<unsigned>();
+  };
 };
 
 
@@ -889,9 +892,14 @@ class AstBuildingParserFactory : public ParserFactory {
 
   virtual Handle<String> LookupSymbol(int symbol_id,
                                       Vector<const char> string) {
-    // If there is no preparse data, we have no simpler way to identify similar
-    // symbols.
-    if (symbol_id < 0) return Factory::LookupSymbol(string);
+    // Length of symbol cache is the number of identified symbols.
+    // If we are larger than that, or negative, it's not a cached symbol.
+    // This might also happen if there is no preparser symbol data, even
+    // if there is some preparser data.
+    if (static_cast<unsigned>(symbol_id)
+        >= static_cast<unsigned>(symbol_cache_.length())) {
+      return Factory::LookupSymbol(string);
+    }
     return LookupCachedSymbol(symbol_id, string);
   }
 
@@ -933,34 +941,78 @@ class AstBuildingParserFactory : public ParserFactory {
 };
 
 
-class ParserRecorder: public ParserLog {
+// Record only functions.
+class PartialParserRecorder: public ParserLog {
  public:
-  ParserRecorder();
+  PartialParserRecorder();
   virtual FunctionEntry LogFunction(int start);
+
+  virtual int function_position() { return function_store_.size(); }
+
+  virtual void LogError() { }
+
+  virtual void LogMessage(Scanner::Location loc,
+                          const char* message,
+                          Vector<const char*> args);
+
+  virtual Vector<unsigned> ExtractData() {
+    int function_size = function_store_.size();
+    int total_size = ScriptDataImpl::kHeaderSize + function_size;
+    Vector<unsigned> data = Vector<unsigned>::New(total_size);
+    preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size;
+    preamble_[ScriptDataImpl::kSymbolCountOffset] = 0;
+    memcpy(data.start(), preamble_, sizeof(preamble_));
+    int symbol_start = ScriptDataImpl::kHeaderSize + function_size;
+    if (function_size > 0) {
+      function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize,
+                                             symbol_start));
+    }
+    return data;
+  }
+
+ protected:
+  bool has_error() {
+    return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]);
+  }
+
+  void WriteString(Vector<const char> str);
+
+  Collector<unsigned> function_store_;
+  unsigned preamble_[ScriptDataImpl::kHeaderSize];
+#ifdef DEBUG
+  int prev_start;
+#endif
+};
+
+
+// Record both functions and symbols.
+class CompleteParserRecorder: public PartialParserRecorder {
+ public:
+  CompleteParserRecorder();
+
   virtual void LogSymbol(int start, Vector<const char> literal) {
     int hash = vector_hash(literal);
     HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true);
     int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
     if (id == 0) {
       // Put (symbol_id_ + 1) into entry and increment it.
-      symbol_id_++;
-      entry->value = reinterpret_cast<void*>(symbol_id_);
+      id = ++symbol_id_;
+      entry->value = reinterpret_cast<void*>(id);
       Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal);
       entry->key = &symbol[0];
-    } else {
-      // Log a reuse of an earlier seen symbol.
-      symbol_store_.Add(start);
-      symbol_store_.Add(id - 1);
     }
+    symbol_store_.Add(id - 1);
   }
-  virtual void LogError() { }
-  virtual void LogMessage(Scanner::Location loc,
-                          const char* message,
-                          Vector<const char*> args);
-  Vector<unsigned> ExtractData() {
+
+  virtual Vector<unsigned> ExtractData() {
     int function_size = function_store_.size();
+    // Add terminator to symbols, then pad to unsigned size.
     int symbol_size = symbol_store_.size();
-    int total_size = ScriptDataImpl::kHeaderSize + function_size + symbol_size;
+    int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned));
+    symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator);
+    symbol_size += padding;
+    int total_size = ScriptDataImpl::kHeaderSize + function_size
+        + (symbol_size / sizeof(unsigned));
     Vector<unsigned> data = Vector<unsigned>::New(total_size);
     preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size;
     preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_;
@@ -970,23 +1022,17 @@ class ParserRecorder: public ParserLog {
       function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize,
                                              symbol_start));
     }
-    if (symbol_size > 0) {
-      symbol_store_.WriteTo(data.SubVector(symbol_start, total_size));
+    if (!has_error()) {
+      symbol_store_.WriteTo(
+          Vector<byte>::cast(data.SubVector(symbol_start, total_size)));
     }
     return data;
   }
 
-  virtual int function_position() { return function_store_.size(); }
   virtual int symbol_position() { return symbol_store_.size(); }
   virtual int symbol_ids() { return symbol_id_; }
  private:
-  Collector<unsigned> function_store_;
-  Collector<unsigned> symbol_store_;
-  Collector<Vector<const char> > symbol_entries_;
-  HashMap symbol_table_;
-  int symbol_id_;
-
-  static int vector_hash(Vector<const char> string) {
+    static int vector_hash(Vector<const char> string) {
     int hash = 0;
     for (int i = 0; i < string.length(); i++) {
       int c = string[i];
@@ -1005,15 +1051,13 @@ class ParserRecorder: public ParserLog {
     return memcmp(string1->start(), string2->start(), length) == 0;
   }
 
-  unsigned preamble_[ScriptDataImpl::kHeaderSize];
-#ifdef DEBUG
-  int prev_start;
-#endif
+  // Write a non-negative number to the symbol store.
+  void WriteNumber(int number);
 
-  bool has_error() {
-    return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]);
-  }
-  void WriteString(Vector<const char> str);
+  Collector<byte> symbol_store_;
+  Collector<Vector<const char> > symbol_entries_;
+  HashMap symbol_table_;
+  int symbol_id_;
 };
 
 
@@ -1038,18 +1082,11 @@ FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) {
 }
 
 
-int ScriptDataImpl::GetSymbolIdentifier(int start) {
-  int next = symbol_index_ + 2;
-  if (next <= store_.length()
-      && static_cast<int>(store_[symbol_index_]) == start) {
-    symbol_index_ = next;
-    return store_[next - 1];
-  }
-  return symbol_id_++;
+int ScriptDataImpl::GetSymbolIdentifier() {
+  return ReadNumber(&symbol_data_);
 }
 
 
-
 bool ScriptDataImpl::SanityCheck() {
   // Check that the header data is valid and doesn't specify
   // point to positions outside the store.
@@ -1080,7 +1117,7 @@ bool ScriptDataImpl::SanityCheck() {
   int symbol_count =
       static_cast<int>(store_[ScriptDataImpl::kSymbolCountOffset]);
   if (symbol_count < 0) return false;
-  // Check that the total size has room both function entries.
+  // Check that the total size has room for header and function entries.
   int minimum_size =
       ScriptDataImpl::kHeaderSize + functions_size;
   if (store_.length() < minimum_size) return false;
@@ -1088,15 +1125,8 @@ bool ScriptDataImpl::SanityCheck() {
 }
 
 
-ParserRecorder::ParserRecorder()
-  : function_store_(0),
-    symbol_store_(0),
-    symbol_entries_(0),
-    symbol_table_(vector_compare),
-    symbol_id_(0) {
-#ifdef DEBUG
-  prev_start = -1;
-#endif
+
+PartialParserRecorder::PartialParserRecorder() : function_store_(0) {
   preamble_[ScriptDataImpl::kMagicOffset] = ScriptDataImpl::kMagicNumber;
   preamble_[ScriptDataImpl::kVersionOffset] = ScriptDataImpl::kCurrentVersion;
   preamble_[ScriptDataImpl::kHasErrorOffset] = false;
@@ -1104,10 +1134,22 @@ ParserRecorder::ParserRecorder()
   preamble_[ScriptDataImpl::kSymbolCountOffset] = 0;
   preamble_[ScriptDataImpl::kSizeOffset] = 0;
   ASSERT_EQ(6, ScriptDataImpl::kHeaderSize);
+#ifdef DEBUG
+  prev_start = -1;
+#endif
 }
 
 
-void ParserRecorder::WriteString(Vector<const char> str) {
+CompleteParserRecorder::CompleteParserRecorder()
+    : PartialParserRecorder(),
+      symbol_store_(0),
+      symbol_entries_(0),
+      symbol_table_(vector_compare),
+      symbol_id_(0) {
+}
+
+
+void PartialParserRecorder::WriteString(Vector<const char> str) {
   function_store_.Add(str.length());
   for (int i = 0; i < str.length(); i++) {
     function_store_.Add(str[i]);
@@ -1115,6 +1157,22 @@ void ParserRecorder::WriteString(Vector<const char> str) {
 }
 
 
+void CompleteParserRecorder::WriteNumber(int number) {
+  ASSERT(number >= 0);
+
+  int mask = (1 << 28) - 1;
+  for (int i = 28; i > 0; i -= 7) {
+    if (number > mask) {
+      symbol_store_.Add(static_cast<byte>(number >> i) | 0x80u);
+      number &= mask;
+    }
+    mask >>= 7;
+  }
+  symbol_store_.Add(static_cast<byte>(number));
+}
+
+
+
 const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) {
   int length = start[0];
   char* result = NewArray<char>(length + 1);
@@ -1127,8 +1185,9 @@ const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) {
 }
 
 
-void ParserRecorder::LogMessage(Scanner::Location loc, const char* message,
-                                Vector<const char*> args) {
+void PartialParserRecorder::LogMessage(Scanner::Location loc,
+                                      const char* message,
+                                      Vector<const char*> args) {
   if (has_error()) return;
   preamble_[ScriptDataImpl::kHasErrorOffset] = true;
   function_store_.Reset();
@@ -1162,7 +1221,8 @@ const char* ScriptDataImpl::BuildMessage() {
 Vector<const char*> ScriptDataImpl::BuildArgs() {
   int arg_count = Read(kMessageArgCountPos);
   const char** array = NewArray<const char*>(arg_count);
-  // Position after the string starting at position 3.
+  // Position after text found by skipping past length field and
+  // length field content words.
   int pos = kMessageTextPos + 1 + Read(kMessageTextPos);
   for (int i = 0; i < arg_count; i++) {
     int count = 0;
@@ -1183,7 +1243,7 @@ unsigned* ScriptDataImpl::ReadAddress(int position) {
 }
 
 
-FunctionEntry ParserRecorder::LogFunction(int start) {
+FunctionEntry PartialParserRecorder::LogFunction(int start) {
 #ifdef DEBUG
   ASSERT(start > prev_start);
   prev_start = start;
@@ -1206,7 +1266,7 @@ class AstBuildingParser : public Parser {
                factory(),
                log(),
                pre_data),
-        factory_(pre_data ? pre_data->symbol_count() : 16) { }
+        factory_(pre_data ? pre_data->symbol_count() : 0) { }
   virtual void ReportMessageAt(Scanner::Location loc, const char* message,
                                Vector<const char*> args);
   virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode,
@@ -1223,23 +1283,46 @@ class AstBuildingParser : public Parser {
 class PreParser : public Parser {
  public:
   PreParser(Handle<Script> script, bool allow_natives_syntax,
-            v8::Extension* extension)
+            v8::Extension* extension, ParserLog* recorder)
       : Parser(script, allow_natives_syntax, extension, PREPARSE,
-               factory(), recorder(), NULL),
+               factory(), recorder, NULL),
         factory_(true) { }
   virtual void ReportMessageAt(Scanner::Location loc, const char* message,
                                Vector<const char*> args);
   virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode,
                                  FunctionLiteral* fun, bool resolve, bool* ok);
   ParserFactory* factory() { return &factory_; }
-  ParserRecorder* recorder() { return &recorder_; }
+  virtual PartialParserRecorder* recorder() = 0;
 
  private:
-  ParserRecorder recorder_;
   ParserFactory factory_;
 };
 
 
+class CompletePreParser : public PreParser {
+ public:
+  CompletePreParser(Handle<Script> script, bool allow_natives_syntax,
+                    v8::Extension* extension)
+      : PreParser(script, allow_natives_syntax, extension, &recorder_),
+        recorder_()  { }
+  virtual PartialParserRecorder* recorder() { return &recorder_; }
+ private:
+  CompleteParserRecorder recorder_;
+};
+
+
+class PartialPreParser : public PreParser {
+ public:
+  PartialPreParser(Handle<Script> script, bool allow_natives_syntax,
+                   v8::Extension* extension)
+      : PreParser(script, allow_natives_syntax, extension, &recorder_),
+        recorder_() { }
+  virtual PartialParserRecorder* recorder() { return &recorder_; }
+ private:
+  PartialParserRecorder recorder_;
+};
+
+
 Scope* AstBuildingParserFactory::NewScope(Scope* parent, Scope::Type type,
                                           bool inside_with) {
   Scope* result = new Scope(parent, type);
@@ -1574,17 +1657,12 @@ void Parser::ReportMessage(const char* type, Vector<const char*> args) {
 
 
 Handle<String> Parser::GetSymbol(bool* ok) {
+  log()->LogSymbol(scanner_.location().beg_pos, scanner_.literal());
+  int symbol_id = -1;
   if (pre_data() != NULL) {
-    int symbol_id =
-        pre_data()->GetSymbolIdentifier(scanner_.location().beg_pos);
-    if (symbol_id < 0) {
-      ReportInvalidPreparseData(Factory::empty_symbol(), ok);
-      return Handle<String>::null();
-    }
-    return factory()->LookupSymbol(symbol_id, scanner_.literal());
+    symbol_id = pre_data()->GetSymbolIdentifier();
   }
-  log()->LogSymbol(scanner_.location().beg_pos, scanner_.literal());
-  return factory()->LookupSymbol(-1, scanner_.literal());
+  return factory()->LookupSymbol(symbol_id, scanner_.literal());
 }
 
 
@@ -4111,8 +4189,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
       Counters::total_preparse_skipped.Increment(end_pos - function_block_pos);
       scanner_.SeekForward(end_pos);
       pre_data()->Skip(entry.predata_function_skip(),
-                       entry.predata_symbol_skip(),
-                       entry.symbol_id_skip());
+                       entry.predata_symbol_skip());
       materialized_literal_count = entry.literal_count();
       expected_property_count = entry.property_count();
       only_simple_this_property_assignments = false;
@@ -4126,7 +4203,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
       FunctionEntry entry = log()->LogFunction(function_block_pos);
       int predata_function_position_before = log()->function_position();
       int predata_symbol_position_before = log()->symbol_position();
-      int symbol_ids_before = log()->symbol_ids();
       ParseSourceElements(&body, Token::RBRACE, CHECK_OK);
       materialized_literal_count = temp_scope.materialized_literal_count();
       expected_property_count = temp_scope.expected_property_count();
@@ -4144,8 +4220,6 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
             log()->function_position() - predata_function_position_before);
         entry.set_predata_symbol_skip(
             log()->symbol_position() - predata_symbol_position_before);
-        entry.set_symbol_id_skip(
-            log()->symbol_ids() - symbol_ids_before);
       }
     }
 
@@ -4178,58 +4252,43 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
 
   Expect(Token::MOD, CHECK_OK);
   Handle<String> name = ParseIdentifier(CHECK_OK);
-  Runtime::Function* function =
-      Runtime::FunctionForName(scanner_.literal());
   ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
-  if (function == NULL && extension_ != NULL) {
+  if (is_pre_parsing_) return NULL;
+
+  if (extension_ != NULL) {
     // The extension structures are only accessible while parsing the
     // very first time not when reparsing because of lazy compilation.
     top_scope_->ForceEagerCompilation();
   }
 
-  // Check for built-in macros.
-  if (!is_pre_parsing_) {
-    if (function == Runtime::FunctionForId(Runtime::kIS_VAR)) {
-      // %IS_VAR(x)
-      //   evaluates to x if x is a variable,
-      //   leads to a parse error otherwise
-      if (args->length() == 1 && args->at(0)->AsVariableProxy() != NULL) {
-        return args->at(0);
-      }
-      *ok = false;
-    // Check here for other macros.
-    // } else if (function == Runtime::FunctionForId(Runtime::kIS_VAR)) {
-    //   ...
-    }
+  Runtime::Function* function = Runtime::FunctionForSymbol(name);
 
-    if (!*ok) {
-      // We found a macro but it failed.
+  // Check for built-in IS_VAR macro.
+  if (function != NULL &&
+      function->intrinsic_type == Runtime::RUNTIME &&
+      function->function_id == Runtime::kIS_VAR) {
+    // %IS_VAR(x) evaluates to x if x is a variable,
+    // leads to a parse error otherwise.  Could be implemented as an
+    // inline function %_IS_VAR(x) to eliminate this special case.
+    if (args->length() == 1 && args->at(0)->AsVariableProxy() != NULL) {
+      return args->at(0);
+    } else {
       ReportMessage("unable_to_parse", Vector<const char*>::empty());
+      *ok = false;
       return NULL;
     }
   }
 
-  // Check that the expected number arguments are passed to runtime functions.
-  if (!is_pre_parsing_) {
-    if (function != NULL
-        && function->nargs != -1
-        && function->nargs != args->length()) {
-      ReportMessage("illegal_access", Vector<const char*>::empty());
-      *ok = false;
-      return NULL;
-    } else if (function == NULL && !name.is_null()) {
-      // If this is not a runtime function implemented in C++ it might be an
-      // inlined runtime function.
-      int argc = CodeGenerator::InlineRuntimeCallArgumentsCount(name);
-      if (argc != -1 && argc != args->length()) {
-        ReportMessage("illegal_access", Vector<const char*>::empty());
-        *ok = false;
-        return NULL;
-      }
-    }
+  // Check that the expected number of arguments are being passed.
+  if (function != NULL &&
+      function->nargs != -1 &&
+      function->nargs != args->length()) {
+    ReportMessage("illegal_access", Vector<const char*>::empty());
+    *ok = false;
+    return NULL;
   }
 
-  // Otherwise we have a valid runtime call.
+  // We have a valid intrinsics call or a call to a builtin.
   return NEW(CallRuntime(name, function, args));
 }
 
@@ -5413,6 +5472,66 @@ bool ScriptDataImpl::HasError() {
 }
 
 
+// Preparse, but only collect data that is immediately useful,
+// even if the preparser data is only used once.
+ScriptDataImpl* PartialPreParse(Handle<String> source,
+                                unibrow::CharacterStream* stream,
+                                v8::Extension* extension) {
+  Handle<Script> no_script;
+  bool allow_natives_syntax =
+      always_allow_natives_syntax ||
+      FLAG_allow_natives_syntax ||
+      Bootstrapper::IsActive();
+  PartialPreParser parser(no_script, allow_natives_syntax, extension);
+  if (!parser.PreParseProgram(source, stream)) return NULL;
+  // Extract the accumulated data from the recorder as a single
+  // contiguous vector that we are responsible for disposing.
+  Vector<unsigned> store = parser.recorder()->ExtractData();
+  return new ScriptDataImpl(store);
+}
+
+
+void ScriptDataImpl::Initialize() {
+  if (store_.length() >= kHeaderSize) {
+    int symbol_data_offset = kHeaderSize + store_[kFunctionsSizeOffset];
+    if (store_.length() > symbol_data_offset) {
+      symbol_data_ = reinterpret_cast<byte*>(&store_[symbol_data_offset]);
+    } else {
+      // Partial preparse causes no symbol information.
+      symbol_data_ = reinterpret_cast<byte*>(&store_[0] + store_.length());
+    }
+    symbol_data_end_ = reinterpret_cast<byte*>(&store_[0] + store_.length());
+  }
+}
+
+
+int ScriptDataImpl::ReadNumber(byte** source) {
+  // Reads a number from symbol_data_ in base 128. The most significant
+  // bit marks that there are more digits.
+  // If the first byte is 0x80 (kNumberTerminator), it would normally
+  // represent a leading zero. Since that is useless, and therefore won't
+  // appear as the first digit of any actual value, it is used to
+  // mark the end of the input stream.
+  byte* data = *source;
+  if (data >= symbol_data_end_) return -1;
+  byte input = *data;
+  if (input == kNumberTerminator) {
+    // End of stream marker.
+    return -1;
+  }
+  int result = input & 0x7f;
+  data++;
+  while ((input & 0x80u) != 0) {
+    if (data >= symbol_data_end_) return -1;
+    input = *data;
+    result = (result << 7) | (input & 0x7f);
+    data++;
+  }
+  *source = data;
+  return result;
+}
+
+
 ScriptDataImpl* PreParse(Handle<String> source,
                          unibrow::CharacterStream* stream,
                          v8::Extension* extension) {
@@ -5421,7 +5540,7 @@ ScriptDataImpl* PreParse(Handle<String> source,
       always_allow_natives_syntax ||
       FLAG_allow_natives_syntax ||
       Bootstrapper::IsActive();
-  PreParser parser(no_script, allow_natives_syntax, extension);
+  CompletePreParser parser(no_script, allow_natives_syntax, extension);
   if (!parser.PreParseProgram(source, stream)) return NULL;
   // Extract the accumulated data from the recorder as a single
   // contiguous vector that we are responsible for disposing.
index 56412a0..8bab92d 100644 (file)
@@ -82,15 +82,9 @@ class FunctionEntry BASE_EMBEDDED {
     backing_[kPredataSymbolSkipOffset] = value;
   }
 
-  int symbol_id_skip() { return backing_[kSymbolIdSkipOffset]; }
-  void set_symbol_id_skip(int value) {
-    backing_[kSymbolIdSkipOffset] = value;
-  }
-
-
   bool is_valid() { return backing_.length() > 0; }
 
-  static const int kSize = 7;
+  static const int kSize = 6;
 
  private:
   Vector<unsigned> backing_;
@@ -100,7 +94,6 @@ class FunctionEntry BASE_EMBEDDED {
   static const int kPropertyCountOffset = 3;
   static const int kPredataFunctionSkipOffset = 4;
   static const int kPredataSymbolSkipOffset = 5;
-  static const int kSymbolIdSkipOffset = 6;
 };
 
 
@@ -109,18 +102,10 @@ class ScriptDataImpl : public ScriptData {
   explicit ScriptDataImpl(Vector<unsigned> store)
       : store_(store),
         function_index_(kHeaderSize),
-        symbol_id_(0),
         owns_store_(true) {
     Initialize();
   }
 
-  void Initialize() {
-    if (store_.length() >= kHeaderSize) {
-      // Otherwise we won't satisfy the SanityCheck.
-      symbol_index_ = kHeaderSize + store_[kFunctionsSizeOffset];
-    }
-  }
-
   // Create an empty ScriptDataImpl that is guaranteed to not satisfy
   // a SanityCheck.
   ScriptDataImpl() : store_(Vector<unsigned>()), owns_store_(false) { }
@@ -130,8 +115,11 @@ class ScriptDataImpl : public ScriptData {
   virtual const char* Data();
   virtual bool HasError();
 
+  void Initialize();
+  void ReadNextSymbolPosition();
+
   FunctionEntry GetFunctionEntry(int start);
-  int GetSymbolIdentifier(int start);
+  int GetSymbolIdentifier();
   void SkipFunctionEntry(int start);
   bool SanityCheck();
 
@@ -149,19 +137,27 @@ class ScriptDataImpl : public ScriptData {
   unsigned version() { return store_[kVersionOffset]; }
 
   // Skip forward in the preparser data by the given number
-  // of unsigned ints.
-  virtual void Skip(int function_entries, int symbol_entries, int symbol_ids) {
+  // of unsigned ints of function entries and the given number of bytes of
+  // symbol id encoding.
+  void Skip(int function_entries, int symbol_entries) {
     ASSERT(function_entries >= 0);
     ASSERT(function_entries
            <= (static_cast<int>(store_[kFunctionsSizeOffset])
                - (function_index_ - kHeaderSize)));
-    function_index_ += function_entries;
-    symbol_index_ += symbol_entries;
-    symbol_id_ += symbol_ids;
+    ASSERT(symbol_entries >= 0);
+    ASSERT(symbol_entries <= symbol_data_end_ - symbol_data_);
+
+    unsigned max_function_skip = store_[kFunctionsSizeOffset] -
+        static_cast<unsigned>(function_index_ - kHeaderSize);
+    function_index_ +=
+        Min(static_cast<unsigned>(function_entries), max_function_skip);
+    symbol_data_ +=
+        Min(static_cast<unsigned>(symbol_entries),
+            static_cast<unsigned>(symbol_data_end_ - symbol_data_));
   }
 
   static const unsigned kMagicNumber = 0xBadDead;
-  static const unsigned kCurrentVersion = 2;
+  static const unsigned kCurrentVersion = 3;
 
   static const int kMagicOffset = 0;
   static const int kVersionOffset = 1;
@@ -171,26 +167,30 @@ class ScriptDataImpl : public ScriptData {
   static const int kSizeOffset = 5;
   static const int kHeaderSize = 6;
 
+  // If encoding a message, the following positions are fixed.
   static const int kMessageStartPos = 0;
   static const int kMessageEndPos = 1;
   static const int kMessageArgCountPos = 2;
   static const int kMessageTextPos = 3;
 
+  static const byte kNumberTerminator = 0x80u;
+
  private:
   Vector<unsigned> store_;
+  unsigned char* symbol_data_;
+  unsigned char* symbol_data_end_;
   int function_index_;
-  int symbol_index_;
-  int symbol_id_;
   bool owns_store_;
 
   unsigned Read(int position);
   unsigned* ReadAddress(int position);
+  // Reads a number from the current symbols
+  int ReadNumber(byte** source);
 
   ScriptDataImpl(const char* backing_store, int length)
       : store_(reinterpret_cast<unsigned*>(const_cast<char*>(backing_store)),
                length / sizeof(unsigned)),
         function_index_(kHeaderSize),
-        symbol_id_(0),
         owns_store_(false) {
     ASSERT_EQ(0, reinterpret_cast<intptr_t>(backing_store) % sizeof(unsigned));
     Initialize();
@@ -212,11 +212,17 @@ FunctionLiteral* MakeAST(bool compile_in_global_context,
                          ScriptDataImpl* pre_data,
                          bool is_json = false);
 
-
+// Generic preparser generating full preparse data.
 ScriptDataImpl* PreParse(Handle<String> source,
                          unibrow::CharacterStream* stream,
                          v8::Extension* extension);
 
+// Preparser that only does preprocessing that makes sense if only used
+// immediately after.
+ScriptDataImpl* PartialPreParse(Handle<String> source,
+                                unibrow::CharacterStream* stream,
+                                v8::Extension* extension);
+
 
 bool ParseRegExp(FlatStringReader* input,
                  bool multiline,
index 2de7a2f..f8fa23d 100644 (file)
@@ -31,6 +31,7 @@
 #include "global-handles.h"
 #include "scopeinfo.h"
 #include "top.h"
+#include "unicode.h"
 #include "zone-inl.h"
 
 #include "profile-generator-inl.h"
@@ -2132,6 +2133,357 @@ HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
   return diff;
 }
 
+
+class OutputStreamWriter {
+ public:
+  explicit OutputStreamWriter(v8::OutputStream* stream)
+      : stream_(stream),
+        chunk_size_(stream->GetChunkSize()),
+        chunk_(chunk_size_),
+        chunk_pos_(0),
+        aborted_(false) {
+    ASSERT(chunk_size_ > 0);
+  }
+  bool aborted() { return aborted_; }
+  void AddCharacter(char c) {
+    ASSERT(c != '\0');
+    ASSERT(chunk_pos_ < chunk_size_);
+    chunk_[chunk_pos_++] = c;
+    MaybeWriteChunk();
+  }
+  void AddString(const char* s) {
+    AddSubstring(s, StrLength(s));
+  }
+  void AddSubstring(const char* s, int n) {
+    if (n <= 0) return;
+    ASSERT(static_cast<size_t>(n) <= strlen(s));
+    const char* s_end = s + n;
+    while (s < s_end) {
+      int s_chunk_size = Min(
+          chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
+      ASSERT(s_chunk_size > 0);
+      memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size);
+      s += s_chunk_size;
+      chunk_pos_ += s_chunk_size;
+      MaybeWriteChunk();
+    }
+  }
+  void AddNumber(int n) { AddNumberImpl<int>(n, "%d"); }
+  void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
+  void AddNumber(uint64_t n) { AddNumberImpl<uint64_t>(n, "%llu"); }
+  void Finalize() {
+    if (aborted_) return;
+    ASSERT(chunk_pos_ < chunk_size_);
+    if (chunk_pos_ != 0) {
+      WriteChunk();
+    }
+    stream_->EndOfStream();
+  }
+
+ private:
+  template<typename T>
+  void AddNumberImpl(T n, const char* format) {
+    ScopedVector<char> buffer(32);
+    int result = OS::SNPrintF(buffer, format, n);
+    USE(result);
+    ASSERT(result != -1);
+    AddString(buffer.start());
+  }
+  void MaybeWriteChunk() {
+    ASSERT(chunk_pos_ <= chunk_size_);
+    if (chunk_pos_ == chunk_size_) {
+      WriteChunk();
+      chunk_pos_ = 0;
+    }
+  }
+  void WriteChunk() {
+    if (aborted_) return;
+    if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
+        v8::OutputStream::kAbort) aborted_ = true;
+  }
+
+  v8::OutputStream* stream_;
+  int chunk_size_;
+  ScopedVector<char> chunk_;
+  int chunk_pos_;
+  bool aborted_;
+};
+
+void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
+  ASSERT(writer_ == NULL);
+  writer_ = new OutputStreamWriter(stream);
+
+  // Since nodes graph is cyclic, we need the first pass to enumerate
+  // them. Strings can be serialized in one pass.
+  EnumerateNodes();
+  SerializeImpl();
+
+  delete writer_;
+  writer_ = NULL;
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeImpl() {
+  writer_->AddCharacter('{');
+  writer_->AddString("\"snapshot\":{");
+  SerializeSnapshot();
+  if (writer_->aborted()) return;
+  writer_->AddString("},\n");
+  writer_->AddString("\"nodes\":[");
+  SerializeNodes();
+  if (writer_->aborted()) return;
+  writer_->AddString("],\n");
+  writer_->AddString("\"strings\":[");
+  SerializeStrings();
+  if (writer_->aborted()) return;
+  writer_->AddCharacter(']');
+  writer_->AddCharacter('}');
+  writer_->Finalize();
+}
+
+
+class HeapSnapshotJSONSerializerEnumerator {
+ public:
+  explicit HeapSnapshotJSONSerializerEnumerator(HeapSnapshotJSONSerializer* s)
+      : s_(s) {
+  }
+  void Apply(HeapEntry** entry) {
+    s_->GetNodeId(*entry);
+  }
+ private:
+  HeapSnapshotJSONSerializer* s_;
+};
+
+void HeapSnapshotJSONSerializer::EnumerateNodes() {
+  GetNodeId(snapshot_->root());  // Make sure root gets the first id.
+  HeapSnapshotJSONSerializerEnumerator iter(this);
+  snapshot_->IterateEntries(&iter);
+}
+
+
+int HeapSnapshotJSONSerializer::GetNodeId(HeapEntry* entry) {
+  HashMap::Entry* cache_entry = nodes_.Lookup(entry, ObjectHash(entry), true);
+  if (cache_entry->value == NULL) {
+    cache_entry->value = reinterpret_cast<void*>(next_node_id_++);
+  }
+  return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
+}
+
+
+int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
+  HashMap::Entry* cache_entry = strings_.Lookup(
+      const_cast<char*>(s), ObjectHash(s), true);
+  if (cache_entry->value == NULL) {
+    cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
+  }
+  return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) {
+  writer_->AddCharacter(',');
+  writer_->AddNumber(edge->type());
+  writer_->AddCharacter(',');
+  if (edge->type() == HeapGraphEdge::kElement) {
+    writer_->AddNumber(edge->index());
+  } else {
+    writer_->AddNumber(GetStringId(edge->name()));
+  }
+  writer_->AddCharacter(',');
+  writer_->AddNumber(GetNodeId(edge->to()));
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
+  writer_->AddCharacter('\n');
+  writer_->AddCharacter(',');
+  writer_->AddNumber(entry->type());
+  writer_->AddCharacter(',');
+  writer_->AddNumber(GetStringId(entry->name()));
+  writer_->AddCharacter(',');
+  writer_->AddNumber(entry->id());
+  writer_->AddCharacter(',');
+  writer_->AddNumber(entry->self_size());
+  Vector<HeapGraphEdge> children = entry->children();
+  writer_->AddCharacter(',');
+  writer_->AddNumber(children.length());
+  for (int i = 0; i < children.length(); ++i) {
+    SerializeEdge(&children[i]);
+    if (writer_->aborted()) return;
+  }
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeNodes() {
+  // The first (zero) item of nodes array is a JSON-ified object
+  // describing node serialization layout.
+  // We use a set of macros to improve readability.
+#define JSON_A(s) "["s"]"
+#define JSON_O(s) "{"s"}"
+#define JSON_S(s) "\\\""s"\\\""
+  writer_->AddString("\"" JSON_O(
+    JSON_S("fields") ":" JSON_A(
+        JSON_S("type")
+        "," JSON_S("name")
+        "," JSON_S("id")
+        "," JSON_S("self_size")
+        "," JSON_S("children_count")
+        "," JSON_S("children"))
+    "," JSON_S("types") ":" JSON_A(
+        JSON_A(
+            JSON_S("internal")
+            "," JSON_S("array")
+            "," JSON_S("string")
+            "," JSON_S("object")
+            "," JSON_S("code")
+            "," JSON_S("closure"))
+        "," JSON_S("string")
+        "," JSON_S("number")
+        "," JSON_S("number")
+        "," JSON_S("number")
+        "," JSON_O(
+            JSON_S("fields") ":" JSON_A(
+                JSON_S("type")
+                "," JSON_S("name_or_index")
+                "," JSON_S("to_node"))
+            "," JSON_S("types") ":" JSON_A(
+                JSON_A(
+                    JSON_S("context")
+                    "," JSON_S("element")
+                    "," JSON_S("property")
+                    "," JSON_S("internal"))
+                "," JSON_S("string_or_number")
+                "," JSON_S("node"))))) "\"");
+#undef JSON_S
+#undef JSON_O
+#undef JSON_A
+
+  const int node_fields_count = 5;  // type,name,id,self_size,children_count.
+  const int edge_fields_count = 3;  // type,name|index,to_node.
+  List<HashMap::Entry*> sorted_nodes;
+  SortHashMap(&nodes_, &sorted_nodes);
+  // Rewrite node ids, so they refer to actual array positions.
+  if (sorted_nodes.length() > 1) {
+    // Nodes start from array index 1.
+    int prev_value = 1;
+    sorted_nodes[0]->value = reinterpret_cast<void*>(prev_value);
+    for (int i = 1; i < sorted_nodes.length(); ++i) {
+      HeapEntry* prev_heap_entry =
+          reinterpret_cast<HeapEntry*>(sorted_nodes[i-1]->key);
+      prev_value += node_fields_count +
+          prev_heap_entry->children().length() * edge_fields_count;
+      sorted_nodes[i]->value = reinterpret_cast<void*>(prev_value);
+    }
+  }
+  for (int i = 0; i < sorted_nodes.length(); ++i) {
+    SerializeNode(reinterpret_cast<HeapEntry*>(sorted_nodes[i]->key));
+    if (writer_->aborted()) return;
+  }
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeSnapshot() {
+  writer_->AddString("\"title\":\"");
+  writer_->AddString(snapshot_->title());
+  writer_->AddString("\"");
+  writer_->AddString(",\"uid\":");
+  writer_->AddNumber(snapshot_->uid());
+}
+
+
+static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
+  static const char hex_chars[] = "0123456789ABCDEF";
+  w->AddString("\\u");
+  w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
+  w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
+  w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
+  w->AddCharacter(hex_chars[u & 0xf]);
+}
+
+void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
+  writer_->AddCharacter('\n');
+  writer_->AddCharacter('\"');
+  for ( ; *s != '\0'; ++s) {
+    switch (*s) {
+      case '\b':
+        writer_->AddString("\\b");
+        continue;
+      case '\f':
+        writer_->AddString("\\f");
+        continue;
+      case '\n':
+        writer_->AddString("\\n");
+        continue;
+      case '\r':
+        writer_->AddString("\\r");
+        continue;
+      case '\t':
+        writer_->AddString("\\t");
+        continue;
+      case '\"':
+      case '\\':
+        writer_->AddCharacter('\\');
+        writer_->AddCharacter(*s);
+        continue;
+      default:
+        if (*s > 31 && *s < 128) {
+          writer_->AddCharacter(*s);
+        } else if (*s <= 31) {
+          // Special character with no dedicated literal.
+          WriteUChar(writer_, *s);
+        } else {
+          // Convert UTF-8 into \u UTF-16 literal.
+          unsigned length = 1, cursor = 0;
+          for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
+          unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
+          if (c != unibrow::Utf8::kBadChar) {
+            WriteUChar(writer_, c);
+            ASSERT(cursor != 0);
+            s += cursor - 1;
+          } else {
+            writer_->AddCharacter('?');
+          }
+        }
+    }
+  }
+  writer_->AddCharacter('\"');
+}
+
+
+void HeapSnapshotJSONSerializer::SerializeStrings() {
+  List<HashMap::Entry*> sorted_strings;
+  SortHashMap(&strings_, &sorted_strings);
+  writer_->AddString("\"<dummy>\"");
+  for (int i = 0; i < sorted_strings.length(); ++i) {
+    writer_->AddCharacter(',');
+    SerializeString(
+        reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
+    if (writer_->aborted()) return;
+  }
+}
+
+
+template<typename T>
+inline static int SortUsingEntryValue(const T* x, const T* y) {
+  uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value);
+  uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value);
+  if (x_uint > y_uint) {
+    return 1;
+  } else if (x_uint == y_uint) {
+    return 0;
+  } else {
+    return -1;
+  }
+}
+
+
+void HeapSnapshotJSONSerializer::SortHashMap(
+    HashMap* map, List<HashMap::Entry*>* sorted_entries) {
+  for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
+    sorted_entries->Add(p);
+  sorted_entries->Sort(SortUsingEntryValue);
+}
+
 } }  // namespace v8::internal
 
 #endif  // ENABLE_LOGGING_AND_PROFILING
index c6d6f4c..c2bc4ce 100644 (file)
@@ -976,6 +976,54 @@ class HeapSnapshotGenerator {
   DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator);
 };
 
+class OutputStreamWriter;
+
+class HeapSnapshotJSONSerializer {
+ public:
+  explicit HeapSnapshotJSONSerializer(HeapSnapshot* snapshot)
+      : snapshot_(snapshot),
+        nodes_(ObjectsMatch),
+        strings_(ObjectsMatch),
+        next_node_id_(1),
+        next_string_id_(1),
+        writer_(NULL) {
+  }
+  void Serialize(v8::OutputStream* stream);
+
+ private:
+  INLINE(static bool ObjectsMatch(void* key1, void* key2)) {
+    return key1 == key2;
+  }
+
+  INLINE(static uint32_t ObjectHash(const void* key)) {
+    return static_cast<int32_t>(reinterpret_cast<intptr_t>(key));
+  }
+
+  void EnumerateNodes();
+  int GetNodeId(HeapEntry* entry);
+  int GetStringId(const char* s);
+  void SerializeEdge(HeapGraphEdge* edge);
+  void SerializeImpl();
+  void SerializeNode(HeapEntry* entry);
+  void SerializeNodes();
+  void SerializeSnapshot();
+  void SerializeString(const unsigned char* s);
+  void SerializeStrings();
+  void SortHashMap(HashMap* map, List<HashMap::Entry*>* sorted_entries);
+
+  HeapSnapshot* snapshot_;
+  HashMap nodes_;
+  HashMap strings_;
+  int next_node_id_;
+  int next_string_id_;
+  OutputStreamWriter* writer_;
+
+  friend class HeapSnapshotJSONSerializerEnumerator;
+  friend class HeapSnapshotJSONSerializerIterator;
+
+  DISALLOW_COPY_AND_ASSIGN(HeapSnapshotJSONSerializer);
+};
+
 } }  // namespace v8::internal
 
 #endif  // ENABLE_LOGGING_AND_PROFILING
index 43a6734..627ea12 100644 (file)
@@ -47,6 +47,7 @@
 #include "smart-pointer.h"
 #include "stub-cache.h"
 #include "v8threads.h"
+#include "string-search.h"
 
 namespace v8 {
 namespace internal {
@@ -2571,418 +2572,6 @@ static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
 }
 
 
-// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
-// limit, we can fix the size of tables.
-static const int kBMMaxShift = 0xff;
-// Reduce alphabet to this size.
-static const int kBMAlphabetSize = 0x100;
-// For patterns below this length, the skip length of Boyer-Moore is too short
-// to compensate for the algorithmic overhead compared to simple brute force.
-static const int kBMMinPatternLength = 5;
-
-// Holds the two buffers used by Boyer-Moore string search's Good Suffix
-// shift. Only allows the last kBMMaxShift characters of the needle
-// to be indexed.
-class BMGoodSuffixBuffers {
- public:
-  BMGoodSuffixBuffers() {}
-  inline void init(int needle_length) {
-    ASSERT(needle_length > 1);
-    int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
-    int len = needle_length - start;
-    biased_suffixes_ = suffixes_ - start;
-    biased_good_suffix_shift_ = good_suffix_shift_ - start;
-    for (int i = 0; i <= len; i++) {
-      good_suffix_shift_[i] = len;
-    }
-  }
-  inline int& suffix(int index) {
-    ASSERT(biased_suffixes_ + index >= suffixes_);
-    return biased_suffixes_[index];
-  }
-  inline int& shift(int index) {
-    ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
-    return biased_good_suffix_shift_[index];
-  }
- private:
-  int suffixes_[kBMMaxShift + 1];
-  int good_suffix_shift_[kBMMaxShift + 1];
-  int* biased_suffixes_;
-  int* biased_good_suffix_shift_;
-  DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
-};
-
-// buffers reused by BoyerMoore
-static int bad_char_occurrence[kBMAlphabetSize];
-static BMGoodSuffixBuffers bmgs_buffers;
-
-// State of the string match tables.
-// SIMPLE: No usable content in the buffers.
-// BOYER_MOORE_HORSPOOL: The bad_char_occurences table has been populated.
-// BOYER_MOORE: The bmgs_buffers tables have also been populated.
-// Whenever starting with a new needle, one should call InitializeStringSearch
-// to determine which search strategy to use, and in the case of a long-needle
-// strategy, the call also initializes the algorithm to SIMPLE.
-enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
-static StringSearchAlgorithm algorithm;
-
-
-// Compute the bad-char table for Boyer-Moore in the static buffer.
-template <typename pchar>
-static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern) {
-  // Only preprocess at most kBMMaxShift last characters of pattern.
-  int start = pattern.length() < kBMMaxShift ? 0
-                                             : pattern.length() - kBMMaxShift;
-  // Run forwards to populate bad_char_table, so that *last* instance
-  // of character equivalence class is the one registered.
-  // Notice: Doesn't include the last character.
-  int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
-                                        : kBMAlphabetSize;
-  if (start == 0) {  // All patterns less than kBMMaxShift in length.
-    memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
-  } else {
-    for (int i = 0; i < table_size; i++) {
-      bad_char_occurrence[i] = start - 1;
-    }
-  }
-  for (int i = start; i < pattern.length() - 1; i++) {
-    pchar c = pattern[i];
-    int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
-    bad_char_occurrence[bucket] = i;
-  }
-}
-
-
-template <typename pchar>
-static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern) {
-  int m = pattern.length();
-  int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
-  int len = m - start;
-  // Compute Good Suffix tables.
-  bmgs_buffers.init(m);
-
-  bmgs_buffers.shift(m-1) = 1;
-  bmgs_buffers.suffix(m) = m + 1;
-  pchar last_char = pattern[m - 1];
-  int suffix = m + 1;
-  for (int i = m; i > start;) {
-    for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
-      if (bmgs_buffers.shift(suffix) == len) {
-        bmgs_buffers.shift(suffix) = suffix - i;
-      }
-      suffix = bmgs_buffers.suffix(suffix);
-    }
-    i--;
-    suffix--;
-    bmgs_buffers.suffix(i) = suffix;
-    if (suffix == m) {
-      // No suffix to extend, so we check against last_char only.
-      while (i > start && pattern[i - 1] != last_char) {
-        if (bmgs_buffers.shift(m) == len) {
-          bmgs_buffers.shift(m) = m - i;
-        }
-        i--;
-        bmgs_buffers.suffix(i) = m;
-      }
-      if (i > start) {
-        i--;
-        suffix--;
-        bmgs_buffers.suffix(i) = suffix;
-      }
-    }
-  }
-  if (suffix < m) {
-    for (int i = start; i <= m; i++) {
-      if (bmgs_buffers.shift(i) == len) {
-        bmgs_buffers.shift(i) = suffix - start;
-      }
-      if (i == suffix) {
-        suffix = bmgs_buffers.suffix(suffix);
-      }
-    }
-  }
-}
-
-
-template <typename schar, typename pchar>
-static inline int CharOccurrence(int char_code) {
-  if (sizeof(schar) == 1) {
-    return bad_char_occurrence[char_code];
-  }
-  if (sizeof(pchar) == 1) {
-    if (char_code > String::kMaxAsciiCharCode) {
-      return -1;
-    }
-    return bad_char_occurrence[char_code];
-  }
-  return bad_char_occurrence[char_code % kBMAlphabetSize];
-}
-
-
-// Restricted simplified Boyer-Moore string matching.
-// Uses only the bad-shift table of Boyer-Moore and only uses it
-// for the character compared to the last character of the needle.
-template <typename schar, typename pchar>
-static int BoyerMooreHorspool(Vector<const schar> subject,
-                              Vector<const pchar> pattern,
-                              int start_index,
-                              bool* complete) {
-  ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
-  int n = subject.length();
-  int m = pattern.length();
-
-  int badness = -m;
-
-  // How bad we are doing without a good-suffix table.
-  int idx;  // No matches found prior to this index.
-  pchar last_char = pattern[m - 1];
-  int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
-  // Perform search
-  for (idx = start_index; idx <= n - m;) {
-    int j = m - 1;
-    int c;
-    while (last_char != (c = subject[idx + j])) {
-      int bc_occ = CharOccurrence<schar, pchar>(c);
-      int shift = j - bc_occ;
-      idx += shift;
-      badness += 1 - shift;  // at most zero, so badness cannot increase.
-      if (idx > n - m) {
-        *complete = true;
-        return -1;
-      }
-    }
-    j--;
-    while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
-    if (j < 0) {
-      *complete = true;
-      return idx;
-    } else {
-      idx += last_char_shift;
-      // Badness increases by the number of characters we have
-      // checked, and decreases by the number of characters we
-      // can skip by shifting. It's a measure of how we are doing
-      // compared to reading each character exactly once.
-      badness += (m - j) - last_char_shift;
-      if (badness > 0) {
-        *complete = false;
-        return idx;
-      }
-    }
-  }
-  *complete = true;
-  return -1;
-}
-
-
-template <typename schar, typename pchar>
-static int BoyerMooreIndexOf(Vector<const schar> subject,
-                             Vector<const pchar> pattern,
-                             int idx) {
-  ASSERT(algorithm <= BOYER_MOORE);
-  int n = subject.length();
-  int m = pattern.length();
-  // Only preprocess at most kBMMaxShift last characters of pattern.
-  int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
-
-  pchar last_char = pattern[m - 1];
-  // Continue search from i.
-  while (idx <= n - m) {
-    int j = m - 1;
-    schar c;
-    while (last_char != (c = subject[idx + j])) {
-      int shift = j - CharOccurrence<schar, pchar>(c);
-      idx += shift;
-      if (idx > n - m) {
-        return -1;
-      }
-    }
-    while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
-    if (j < 0) {
-      return idx;
-    } else if (j < start) {
-      // we have matched more than our tables allow us to be smart about.
-      // Fall back on BMH shift.
-      idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
-    } else {
-      int gs_shift = bmgs_buffers.shift(j + 1);       // Good suffix shift.
-      int bc_occ = CharOccurrence<schar, pchar>(c);
-      int shift = j - bc_occ;                         // Bad-char shift.
-      if (gs_shift > shift) {
-        shift = gs_shift;
-      }
-      idx += shift;
-    }
-  }
-
-  return -1;
-}
-
-
-// Trivial string search for shorter strings.
-// On return, if "complete" is set to true, the return value is the
-// final result of searching for the patter in the subject.
-// If "complete" is set to false, the return value is the index where
-// further checking should start, i.e., it's guaranteed that the pattern
-// does not occur at a position prior to the returned index.
-template <typename pchar, typename schar>
-static int SimpleIndexOf(Vector<const schar> subject,
-                         Vector<const pchar> pattern,
-                         int idx,
-                         bool* complete) {
-  ASSERT(pattern.length() > 1);
-  // Badness is a count of how much work we have done.  When we have
-  // done enough work we decide it's probably worth switching to a better
-  // algorithm.
-  int badness = -10 - (pattern.length() << 2);
-
-  // We know our pattern is at least 2 characters, we cache the first so
-  // the common case of the first character not matching is faster.
-  pchar pattern_first_char = pattern[0];
-  for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
-    badness++;
-    if (badness > 0) {
-      *complete = false;
-      return i;
-    }
-    if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
-      const schar* pos = reinterpret_cast<const schar*>(
-          memchr(subject.start() + i,
-                 pattern_first_char,
-                 n - i + 1));
-      if (pos == NULL) {
-        *complete = true;
-        return -1;
-      }
-      i = static_cast<int>(pos - subject.start());
-    } else {
-      if (subject[i] != pattern_first_char) continue;
-    }
-    int j = 1;
-    do {
-      if (pattern[j] != subject[i+j]) {
-        break;
-      }
-      j++;
-    } while (j < pattern.length());
-    if (j == pattern.length()) {
-      *complete = true;
-      return i;
-    }
-    badness += j;
-  }
-  *complete = true;
-  return -1;
-}
-
-// Simple indexOf that never bails out. For short patterns only.
-template <typename pchar, typename schar>
-static int SimpleIndexOf(Vector<const schar> subject,
-                         Vector<const pchar> pattern,
-                         int idx) {
-  pchar pattern_first_char = pattern[0];
-  for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
-    if (sizeof(schar) == 1 && sizeof(pchar) == 1) {
-      const schar* pos = reinterpret_cast<const schar*>(
-          memchr(subject.start() + i,
-                 pattern_first_char,
-                 n - i + 1));
-      if (pos == NULL) return -1;
-      i = static_cast<int>(pos - subject.start());
-    } else {
-      if (subject[i] != pattern_first_char) continue;
-    }
-    int j = 1;
-    while (j < pattern.length()) {
-      if (pattern[j] != subject[i+j]) {
-        break;
-      }
-      j++;
-    }
-    if (j == pattern.length()) {
-      return i;
-    }
-  }
-  return -1;
-}
-
-
-// Strategy for searching for a string in another string.
-enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
-
-
-template <typename pchar>
-static inline StringSearchStrategy InitializeStringSearch(
-    Vector<const pchar> pat, bool ascii_subject) {
-  // We have an ASCII haystack and a non-ASCII needle. Check if there
-  // really is a non-ASCII character in the needle and bail out if there
-  // is.
-  if (ascii_subject && sizeof(pchar) > 1) {
-    for (int i = 0; i < pat.length(); i++) {
-      uc16 c = pat[i];
-      if (c > String::kMaxAsciiCharCode) {
-        return SEARCH_FAIL;
-      }
-    }
-  }
-  if (pat.length() < kBMMinPatternLength) {
-    return SEARCH_SHORT;
-  }
-  algorithm = SIMPLE_SEARCH;
-  return SEARCH_LONG;
-}
-
-
-// Dispatch long needle searches to different algorithms.
-template <typename schar, typename pchar>
-static int ComplexIndexOf(Vector<const schar> sub,
-                          Vector<const pchar> pat,
-                          int start_index) {
-  ASSERT(pat.length() >= kBMMinPatternLength);
-  // Try algorithms in order of increasing setup cost and expected performance.
-  bool complete;
-  int idx = start_index;
-  switch (algorithm) {
-    case SIMPLE_SEARCH:
-      idx = SimpleIndexOf(sub, pat, idx, &complete);
-      if (complete) return idx;
-      BoyerMoorePopulateBadCharTable(pat);
-      algorithm = BOYER_MOORE_HORSPOOL;
-      // FALLTHROUGH.
-    case BOYER_MOORE_HORSPOOL:
-      idx = BoyerMooreHorspool(sub, pat, idx, &complete);
-      if (complete) return idx;
-      // Build the Good Suffix table and continue searching.
-      BoyerMoorePopulateGoodSuffixTable(pat);
-      algorithm = BOYER_MOORE;
-      // FALLTHROUGH.
-    case BOYER_MOORE:
-      return BoyerMooreIndexOf(sub, pat, idx);
-  }
-  UNREACHABLE();
-  return -1;
-}
-
-
-// Dispatch to different search strategies for a single search.
-// If searching multiple times on the same needle, the search
-// strategy should only be computed once and then dispatch to different
-// loops.
-template <typename schar, typename pchar>
-static int StringSearch(Vector<const schar> sub,
-                        Vector<const pchar> pat,
-                        int start_index) {
-  bool ascii_subject = (sizeof(schar) == 1);
-  StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
-  switch (strategy) {
-    case SEARCH_FAIL: return -1;
-    case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
-    case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
-  }
-  UNREACHABLE();
-  return -1;
-}
-
-
 // Perform string match of pattern on subject, starting at start index.
 // Caller must ensure that 0 <= start_index <= sub->length(),
 // and should check that pat->length() + start_index <= sub->length()
@@ -3042,32 +2631,33 @@ static Object* Runtime_StringIndexOf(Arguments args) {
 
 
 template <typename schar, typename pchar>
-static int StringMatchBackwards(Vector<const schar> sub,
-                                Vector<const pchar> pat,
+static int StringMatchBackwards(Vector<const schar> subject,
+                                Vector<const pchar> pattern,
                                 int idx) {
-  ASSERT(pat.length() >= 1);
-  ASSERT(idx + pat.length() <= sub.length());
+  int pattern_length = pattern.length();
+  ASSERT(pattern_length >= 1);
+  ASSERT(idx + pattern_length <= subject.length());
 
   if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
-    for (int i = 0; i < pat.length(); i++) {
-      uc16 c = pat[i];
+    for (int i = 0; i < pattern_length; i++) {
+      uc16 c = pattern[i];
       if (c > String::kMaxAsciiCharCode) {
         return -1;
       }
     }
   }
 
-  pchar pattern_first_char = pat[0];
+  pchar pattern_first_char = pattern[0];
   for (int i = idx; i >= 0; i--) {
-    if (sub[i] != pattern_first_char) continue;
+    if (subject[i] != pattern_first_char) continue;
     int j = 1;
-    while (j < pat.length()) {
-      if (pat[j] != sub[i+j]) {
+    while (j < pattern_length) {
+      if (pattern[j] != subject[i+j]) {
         break;
       }
       j++;
     }
-    if (j == pat.length()) {
+    if (j == pattern_length) {
       return i;
     }
   }
@@ -4929,7 +4519,6 @@ static Object* Runtime_StringParseInt(Arguments args) {
   RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36));
   double value = StringToInt(s, radix);
   return Heap::NumberFromDouble(value);
-  return Heap::nan_value();
 }
 
 
@@ -5167,6 +4756,12 @@ static Object* Runtime_StringTrim(Arguments args) {
 }
 
 
+// Define storage for buffers declared in header file.
+// TODO(lrn): Remove these when rewriting search code.
+int BMBuffers::bad_char_occurrence[kBMAlphabetSize];
+BMGoodSuffixBuffers BMBuffers::bmgs_buffers;
+
+
 template <typename schar, typename pchar>
 void FindStringIndices(Vector<const schar> subject,
                        Vector<const pchar> pattern,
@@ -7979,15 +7574,17 @@ static Object* Runtime_MoveArrayContents(Arguments args) {
 }
 
 
-// How many elements does this array have?
+// How many elements does this object/array have?
 static Object* Runtime_EstimateNumberOfElements(Arguments args) {
   ASSERT(args.length() == 1);
-  CONVERT_CHECKED(JSArray, array, args[0]);
-  HeapObject* elements = array->elements();
+  CONVERT_CHECKED(JSObject, object, args[0]);
+  HeapObject* elements = object->elements();
   if (elements->IsDictionary()) {
     return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
+  } else if (object->IsJSArray()) {
+    return JSArray::cast(object)->length();
   } else {
-    return array->length();
+    return Smi::FromInt(FixedArray::cast(elements)->length());
   }
 }
 
@@ -8019,8 +7616,10 @@ static Object* Runtime_SwapElements(Arguments args) {
 
 
 // Returns an array that tells you where in the [0, length) interval an array
-// might have elements.  Can either return keys or intervals.  Keys can have
-// gaps in (undefined).  Intervals can also span over some undefined keys.
+// might have elements.  Can either return keys (positive integers) or
+// intervals (pair of a negative integer (-start-1) followed by a
+// positive (length)) or undefined values.
+// Intervals can span over some keys that are not in the object.
 static Object* Runtime_GetArrayKeys(Arguments args) {
   ASSERT(args.length() == 2);
   HandleScope scope;
@@ -10464,6 +10063,7 @@ static Object* Runtime_ListNatives(Arguments args) {
   inline_runtime_functions = false;
   RUNTIME_FUNCTION_LIST(ADD_ENTRY)
   inline_runtime_functions = true;
+  INLINE_FUNCTION_LIST(ADD_ENTRY)
   INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
 #undef ADD_ENTRY
   return *result;
@@ -10490,35 +10090,55 @@ static Object* Runtime_IS_VAR(Arguments args) {
 // ----------------------------------------------------------------------------
 // Implementation of Runtime
 
-#define F(name, nargs, ressize)                                           \
-  { #name, FUNCTION_ADDR(Runtime_##name), nargs, \
-    static_cast<int>(Runtime::k##name), ressize },
+#define F(name, number_of_args, result_size)                             \
+  { Runtime::k##name, Runtime::RUNTIME, #name,   \
+    FUNCTION_ADDR(Runtime_##name), number_of_args, result_size },
 
-static Runtime::Function Runtime_functions[] = {
+
+#define I(name, number_of_args, result_size)                             \
+  { Runtime::kInline##name, Runtime::INLINE,     \
+    "_" #name, NULL, number_of_args, result_size },
+
+Runtime::Function kIntrinsicFunctions[] = {
   RUNTIME_FUNCTION_LIST(F)
-  { NULL, NULL, 0, -1, 0 }
+  INLINE_FUNCTION_LIST(I)
+  INLINE_RUNTIME_FUNCTION_LIST(I)
 };
 
-#undef F
 
-
-Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
-  ASSERT(0 <= fid && fid < kNofFunctions);
-  return &Runtime_functions[fid];
+Object* Runtime::InitializeIntrinsicFunctionNames(Object* dictionary) {
+  ASSERT(dictionary != NULL);
+  ASSERT(StringDictionary::cast(dictionary)->NumberOfElements() == 0);
+  for (int i = 0; i < kNumFunctions; ++i) {
+    Object* name_symbol = Heap::LookupAsciiSymbol(kIntrinsicFunctions[i].name);
+    if (name_symbol->IsFailure()) return name_symbol;
+    StringDictionary* string_dictionary = StringDictionary::cast(dictionary);
+    dictionary = string_dictionary->Add(String::cast(name_symbol),
+                                        Smi::FromInt(i),
+                                        PropertyDetails(NONE, NORMAL));
+    // Non-recoverable failure.  Calling code must restart heap initialization.
+    if (dictionary->IsFailure()) return dictionary;
+  }
+  return dictionary;
 }
 
 
-Runtime::Function* Runtime::FunctionForName(Vector<const char> name) {
-  for (Function* f = Runtime_functions; f->name != NULL; f++) {
-    if (strncmp(f->name, name.start(), name.length()) == 0
-        && f->name[name.length()] == 0) {
-      return f;
-    }
+Runtime::Function* Runtime::FunctionForSymbol(Handle<String> name) {
+  int entry = Heap::intrinsic_function_names()->FindEntry(*name);
+  if (entry != kNotFound) {
+    Object* smi_index = Heap::intrinsic_function_names()->ValueAt(entry);
+    int function_index = Smi::cast(smi_index)->value();
+    return &(kIntrinsicFunctions[function_index]);
   }
   return NULL;
 }
 
 
+Runtime::Function* Runtime::FunctionForId(Runtime::FunctionId id) {
+  return &(kIntrinsicFunctions[static_cast<int>(id)]);
+}
+
+
 void Runtime::PerformGC(Object* result) {
   Failure* failure = Failure::cast(result);
   if (failure->IsRetryAfterGC()) {
index 312907a..8a3671a 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -389,6 +389,59 @@ namespace internal {
   RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F)
 
 // ----------------------------------------------------------------------------
+// INLINE_FUNCTION_LIST defines all inlined functions accessed
+// with a native call of the form %_name from within JS code.
+// Entries have the form F(name, number of arguments, number of return values).
+#define INLINE_FUNCTION_LIST(F) \
+  F(IsSmi, 1, 1)                                                             \
+  F(IsNonNegativeSmi, 1, 1)                                                  \
+  F(IsArray, 1, 1)                                                           \
+  F(IsRegExp, 1, 1)                                                          \
+  F(CallFunction, -1 /* receiver + n args + function */, 1)                  \
+  F(ArgumentsLength, 0, 1)                                                   \
+  F(Arguments, 1, 1)                                                         \
+  F(ValueOf, 1, 1)                                                           \
+  F(SetValueOf, 2, 1)                                                        \
+  F(StringCharFromCode, 1, 1)                                                \
+  F(StringCharAt, 2, 1)                                                      \
+  F(ObjectEquals, 2, 1)                                                      \
+  F(RandomHeapNumber, 0, 1)                                                  \
+  F(IsObject, 1, 1)                                                          \
+  F(IsFunction, 1, 1)                                                        \
+  F(IsUndetectableObject, 1, 1)                                              \
+  F(IsSpecObject, 1, 1)                                                      \
+  F(IsStringWrapperSafeForDefaultValueOf, 1, 1)                              \
+  F(MathPow, 2, 1)                                                           \
+  F(MathSin, 1, 1)                                                           \
+  F(MathCos, 1, 1)                                                           \
+  F(MathSqrt, 1, 1)                                                          \
+  F(IsRegExpEquivalent, 2, 1)                                                \
+  F(HasCachedArrayIndex, 1, 1)                                               \
+  F(GetCachedArrayIndex, 1, 1)
+
+
+// ----------------------------------------------------------------------------
+// INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
+// with a native call of the form %_name from within JS code that also have
+  // a corresponding runtime function, that is called for slow cases.
+// Entries have the form F(name, number of arguments, number of return values).
+#define INLINE_RUNTIME_FUNCTION_LIST(F) \
+  F(IsConstructCall, 0, 1)                                                   \
+  F(ClassOf, 1, 1)                                                           \
+  F(StringCharCodeAt, 2, 1)                                                  \
+  F(Log, 3, 1)                                                               \
+  F(StringAdd, 2, 1)                                                         \
+  F(SubString, 3, 1)                                                         \
+  F(StringCompare, 2, 1)                                                     \
+  F(RegExpExec, 4, 1)                                                        \
+  F(RegExpConstructResult, 3, 1)                                             \
+  F(RegExpCloneResult, 1, 1)                                                 \
+  F(GetFromCache, 2, 1)                                                      \
+  F(NumberToString, 1, 1)                                                    \
+  F(SwapElements, 3, 1)
+
+
+//---------------------------------------------------------------------------
 // Runtime provides access to all C++ runtime functions.
 
 class Runtime : public AllStatic {
@@ -396,33 +449,52 @@ class Runtime : public AllStatic {
   enum FunctionId {
 #define F(name, nargs, ressize) k##name,
     RUNTIME_FUNCTION_LIST(F)
-    kNofFunctions
 #undef F
+#define F(name, nargs, ressize) kInline##name,
+    INLINE_FUNCTION_LIST(F)
+    INLINE_RUNTIME_FUNCTION_LIST(F)
+#undef F
+    kNumFunctions,
+    kFirstInlineFunction = kInlineIsSmi
   };
 
-  // Runtime function descriptor.
+  enum IntrinsicType {
+    RUNTIME,
+    INLINE
+  };
+
+  // Intrinsic function descriptor.
   struct Function {
+    FunctionId function_id;
+    IntrinsicType intrinsic_type;
     // The JS name of the function.
     const char* name;
 
-    // The C++ (native) entry point.
+    // The C++ (native) entry point.  NULL if the function is inlined.
     byte* entry;
 
-    // The number of arguments expected; nargs < 0 if variable no. of
-    // arguments.
+    // The number of arguments expected. nargs is -1 if the function takes
+    // a variable number of arguments.
     int nargs;
-    int stub_id;
-    // Size of result, if complex (larger than a single pointer),
-    // otherwise zero.
+    // Size of result.  Most functions return a single pointer, size 1.
     int result_size;
   };
 
-  // Get the runtime function with the given function id.
-  static Function* FunctionForId(FunctionId fid);
+  static const int kNotFound = -1;
+
+  // Add symbols for all the intrinsic function names to a StringDictionary.
+  // Returns failure if an allocation fails.  In this case, it must be
+  // retried with a new, empty StringDictionary, not with the same one.
+  // Alternatively, heap initialization can be completely restarted.
+  static Object* InitializeIntrinsicFunctionNames(Object* dictionary);
+
+  // Get the intrinsic function with the given name, which must be a symbol.
+  static Function* FunctionForSymbol(Handle<String> name);
 
-  // Get the runtime function with the given name.
-  static Function* FunctionForName(Vector<const char> name);
+  // Get the intrinsic function with the given FunctionId.
+  static Function* FunctionForId(FunctionId id);
 
+  // General-purpose helper functions for runtime system.
   static int StringMatch(Handle<String> sub, Handle<String> pat, int index);
 
   static bool IsUpperCaseChar(uint16_t ch);
diff --git a/deps/v8/src/string-search.h b/deps/v8/src/string-search.h
new file mode 100644 (file)
index 0000000..d7959c0
--- /dev/null
@@ -0,0 +1,463 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_STRING_SEARCH_H_
+#define V8_STRING_SEARCH_H_
+
+namespace v8 {
+namespace internal {
+
+
+// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
+// limit, we can fix the size of tables. For a needle longer than this limit,
+// search will not be optimal, since we only build tables for a smaller suffix
+// of the string, which is a safe approximation.
+static const int kBMMaxShift = 250;
+// Reduce alphabet to this size.
+// One of the tables used by Boyer-Moore and Boyer-Moore-Horspool has size
+// proportional to the input alphabet. We reduce the alphabet size by
+// equating input characters modulo a smaller alphabet size. This gives
+// a potentially less efficient searching, but is a safe approximation.
+// For needles using only characters in the same Unicode 256-code point page,
+// there is no search speed degradation.
+static const int kBMAlphabetSize = 256;
+// For patterns below this length, the skip length of Boyer-Moore is too short
+// to compensate for the algorithmic overhead compared to simple brute force.
+static const int kBMMinPatternLength = 7;
+
+// Holds the two buffers used by Boyer-Moore string search's Good Suffix
+// shift. Only allows the last kBMMaxShift characters of the needle
+// to be indexed.
+class BMGoodSuffixBuffers {
+ public:
+  BMGoodSuffixBuffers() {}
+  inline void Initialize(int needle_length) {
+    ASSERT(needle_length > 1);
+    int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
+    int len = needle_length - start;
+    biased_suffixes_ = suffixes_ - start;
+    biased_good_suffix_shift_ = good_suffix_shift_ - start;
+    for (int i = 0; i <= len; i++) {
+      good_suffix_shift_[i] = len;
+    }
+  }
+  inline int& suffix(int index) {
+    ASSERT(biased_suffixes_ + index >= suffixes_);
+    return biased_suffixes_[index];
+  }
+  inline int& shift(int index) {
+    ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
+    return biased_good_suffix_shift_[index];
+  }
+ private:
+  int suffixes_[kBMMaxShift + 1];
+  int good_suffix_shift_[kBMMaxShift + 1];
+  int* biased_suffixes_;
+  int* biased_good_suffix_shift_;
+  DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
+};
+
+// buffers reused by BoyerMoore
+struct BMBuffers {
+ public:
+  static int bad_char_occurrence[kBMAlphabetSize];
+  static BMGoodSuffixBuffers bmgs_buffers;
+};
+
+// State of the string match tables.
+// SIMPLE: No usable content in the buffers.
+// BOYER_MOORE_HORSPOOL: The bad_char_occurence table has been populated.
+// BOYER_MOORE: The bmgs_buffers tables have also been populated.
+// Whenever starting with a new needle, one should call InitializeStringSearch
+// to determine which search strategy to use, and in the case of a long-needle
+// strategy, the call also initializes the algorithm to SIMPLE.
+enum StringSearchAlgorithm { SIMPLE_SEARCH, BOYER_MOORE_HORSPOOL, BOYER_MOORE };
+static StringSearchAlgorithm algorithm;
+
+
+// Compute the bad-char table for Boyer-Moore in the static buffer.
+template <typename PatternChar>
+static void BoyerMoorePopulateBadCharTable(Vector<const PatternChar> pattern) {
+  // Only preprocess at most kBMMaxShift last characters of pattern.
+  int start = Max(pattern.length() - kBMMaxShift, 0);
+  // Run forwards to populate bad_char_table, so that *last* instance
+  // of character equivalence class is the one registered.
+  // Notice: Doesn't include the last character.
+  int table_size = (sizeof(PatternChar) == 1) ? String::kMaxAsciiCharCode + 1
+                                        : kBMAlphabetSize;
+  if (start == 0) {  // All patterns less than kBMMaxShift in length.
+    memset(BMBuffers::bad_char_occurrence,
+           -1,
+           table_size * sizeof(*BMBuffers::bad_char_occurrence));
+  } else {
+    for (int i = 0; i < table_size; i++) {
+      BMBuffers::bad_char_occurrence[i] = start - 1;
+    }
+  }
+  for (int i = start; i < pattern.length() - 1; i++) {
+    PatternChar c = pattern[i];
+    int bucket = (sizeof(PatternChar) ==1) ? c : c % kBMAlphabetSize;
+    BMBuffers::bad_char_occurrence[bucket] = i;
+  }
+}
+
+
+template <typename PatternChar>
+static void BoyerMoorePopulateGoodSuffixTable(
+    Vector<const PatternChar> pattern) {
+  int m = pattern.length();
+  int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
+  int len = m - start;
+  // Compute Good Suffix tables.
+  BMBuffers::bmgs_buffers.Initialize(m);
+
+  BMBuffers::bmgs_buffers.shift(m-1) = 1;
+  BMBuffers::bmgs_buffers.suffix(m) = m + 1;
+  PatternChar last_char = pattern[m - 1];
+  int suffix = m + 1;
+  {
+    int i = m;
+    while (i > start) {
+      PatternChar c = pattern[i - 1];
+      while (suffix <= m && c != pattern[suffix - 1]) {
+        if (BMBuffers::bmgs_buffers.shift(suffix) == len) {
+          BMBuffers::bmgs_buffers.shift(suffix) = suffix - i;
+        }
+        suffix = BMBuffers::bmgs_buffers.suffix(suffix);
+      }
+      BMBuffers::bmgs_buffers.suffix(--i) = --suffix;
+      if (suffix == m) {
+        // No suffix to extend, so we check against last_char only.
+        while ((i > start) && (pattern[i - 1] != last_char)) {
+          if (BMBuffers::bmgs_buffers.shift(m) == len) {
+            BMBuffers::bmgs_buffers.shift(m) = m - i;
+          }
+          BMBuffers::bmgs_buffers.suffix(--i) = m;
+        }
+        if (i > start) {
+          BMBuffers::bmgs_buffers.suffix(--i) = --suffix;
+        }
+      }
+    }
+  }
+  if (suffix < m) {
+    for (int i = start; i <= m; i++) {
+      if (BMBuffers::bmgs_buffers.shift(i) == len) {
+        BMBuffers::bmgs_buffers.shift(i) = suffix - start;
+      }
+      if (i == suffix) {
+        suffix = BMBuffers::bmgs_buffers.suffix(suffix);
+      }
+    }
+  }
+}
+
+
+template <typename SubjectChar, typename PatternChar>
+static inline int CharOccurrence(int char_code) {
+  if (sizeof(SubjectChar) == 1) {
+    return BMBuffers::bad_char_occurrence[char_code];
+  }
+  if (sizeof(PatternChar) == 1) {
+    if (char_code > String::kMaxAsciiCharCode) {
+      return -1;
+    }
+    return BMBuffers::bad_char_occurrence[char_code];
+  }
+  return BMBuffers::bad_char_occurrence[char_code % kBMAlphabetSize];
+}
+
+
+// Restricted simplified Boyer-Moore string matching.
+// Uses only the bad-shift table of Boyer-Moore and only uses it
+// for the character compared to the last character of the needle.
+template <typename SubjectChar, typename PatternChar>
+static int BoyerMooreHorspool(Vector<const SubjectChar> subject,
+                              Vector<const PatternChar> pattern,
+                              int start_index,
+                              bool* complete) {
+  ASSERT(algorithm <= BOYER_MOORE_HORSPOOL);
+  int n = subject.length();
+  int m = pattern.length();
+
+  int badness = -m;
+
+  // How bad we are doing without a good-suffix table.
+  int idx;  // No matches found prior to this index.
+  PatternChar last_char = pattern[m - 1];
+  int last_char_shift =
+      m - 1 - CharOccurrence<SubjectChar, PatternChar>(last_char);
+  // Perform search
+  for (idx = start_index; idx <= n - m;) {
+    int j = m - 1;
+    int c;
+    while (last_char != (c = subject[idx + j])) {
+      int bc_occ = CharOccurrence<SubjectChar, PatternChar>(c);
+      int shift = j - bc_occ;
+      idx += shift;
+      badness += 1 - shift;  // at most zero, so badness cannot increase.
+      if (idx > n - m) {
+        *complete = true;
+        return -1;
+      }
+    }
+    j--;
+    while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
+    if (j < 0) {
+      *complete = true;
+      return idx;
+    } else {
+      idx += last_char_shift;
+      // Badness increases by the number of characters we have
+      // checked, and decreases by the number of characters we
+      // can skip by shifting. It's a measure of how we are doing
+      // compared to reading each character exactly once.
+      badness += (m - j) - last_char_shift;
+      if (badness > 0) {
+        *complete = false;
+        return idx;
+      }
+    }
+  }
+  *complete = true;
+  return -1;
+}
+
+
+template <typename SubjectChar, typename PatternChar>
+static int BoyerMooreIndexOf(Vector<const SubjectChar> subject,
+                             Vector<const PatternChar> pattern,
+                             int idx) {
+  ASSERT(algorithm <= BOYER_MOORE);
+  int n = subject.length();
+  int m = pattern.length();
+  // Only preprocess at most kBMMaxShift last characters of pattern.
+  int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
+
+  PatternChar last_char = pattern[m - 1];
+  // Continue search from i.
+  while (idx <= n - m) {
+    int j = m - 1;
+    SubjectChar c;
+    while (last_char != (c = subject[idx + j])) {
+      int shift = j - CharOccurrence<SubjectChar, PatternChar>(c);
+      idx += shift;
+      if (idx > n - m) {
+        return -1;
+      }
+    }
+    while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
+    if (j < 0) {
+      return idx;
+    } else if (j < start) {
+      // we have matched more than our tables allow us to be smart about.
+      // Fall back on BMH shift.
+      idx += m - 1 - CharOccurrence<SubjectChar, PatternChar>(last_char);
+    } else {
+      int gs_shift = BMBuffers::bmgs_buffers.shift(j + 1);
+      int bc_occ = CharOccurrence<SubjectChar, PatternChar>(c);
+      int shift = j - bc_occ;
+      if (gs_shift > shift) {
+        shift = gs_shift;
+      }
+      idx += shift;
+    }
+  }
+
+  return -1;
+}
+
+
+// Trivial string search for shorter strings.
+// On return, if "complete" is set to true, the return value is the
+// final result of searching for the patter in the subject.
+// If "complete" is set to false, the return value is the index where
+// further checking should start, i.e., it's guaranteed that the pattern
+// does not occur at a position prior to the returned index.
+template <typename PatternChar, typename SubjectChar>
+static int SimpleIndexOf(Vector<const SubjectChar> subject,
+                         Vector<const PatternChar> pattern,
+                         int idx,
+                         bool* complete) {
+  ASSERT(pattern.length() > 1);
+  int pattern_length = pattern.length();
+  // Badness is a count of how much work we have done.  When we have
+  // done enough work we decide it's probably worth switching to a better
+  // algorithm.
+  int badness = -10 - (pattern_length << 2);
+
+  // We know our pattern is at least 2 characters, we cache the first so
+  // the common case of the first character not matching is faster.
+  PatternChar pattern_first_char = pattern[0];
+  for (int i = idx, n = subject.length() - pattern_length; i <= n; i++) {
+    badness++;
+    if (badness > 0) {
+      *complete = false;
+      return i;
+    }
+    if (sizeof(SubjectChar) == 1 && sizeof(PatternChar) == 1) {
+      const SubjectChar* pos = reinterpret_cast<const SubjectChar*>(
+          memchr(subject.start() + i,
+                 pattern_first_char,
+                 n - i + 1));
+      if (pos == NULL) {
+        *complete = true;
+        return -1;
+      }
+      i = static_cast<int>(pos - subject.start());
+    } else {
+      if (subject[i] != pattern_first_char) continue;
+    }
+    int j = 1;
+    do {
+      if (pattern[j] != subject[i+j]) {
+        break;
+      }
+      j++;
+    } while (j < pattern_length);
+    if (j == pattern_length) {
+      *complete = true;
+      return i;
+    }
+    badness += j;
+  }
+  *complete = true;
+  return -1;
+}
+
+// Simple indexOf that never bails out. For short patterns only.
+template <typename PatternChar, typename SubjectChar>
+static int SimpleIndexOf(Vector<const SubjectChar> subject,
+                         Vector<const PatternChar> pattern,
+                         int idx) {
+  int pattern_length = pattern.length();
+  PatternChar pattern_first_char = pattern[0];
+  for (int i = idx, n = subject.length() - pattern_length; i <= n; i++) {
+    if (sizeof(SubjectChar) == 1 && sizeof(PatternChar) == 1) {
+      const SubjectChar* pos = reinterpret_cast<const SubjectChar*>(
+          memchr(subject.start() + i,
+                 pattern_first_char,
+                 n - i + 1));
+      if (pos == NULL) return -1;
+      i = static_cast<int>(pos - subject.start());
+    } else {
+      if (subject[i] != pattern_first_char) continue;
+    }
+    int j = 1;
+    while (j < pattern_length) {
+      if (pattern[j] != subject[i+j]) {
+        break;
+      }
+      j++;
+    }
+    if (j == pattern_length) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+
+// Strategy for searching for a string in another string.
+enum StringSearchStrategy { SEARCH_FAIL, SEARCH_SHORT, SEARCH_LONG };
+
+
+template <typename PatternChar>
+static inline StringSearchStrategy InitializeStringSearch(
+    Vector<const PatternChar> pat, bool ascii_subject) {
+  // We have an ASCII haystack and a non-ASCII needle. Check if there
+  // really is a non-ASCII character in the needle and bail out if there
+  // is.
+  if (ascii_subject && sizeof(PatternChar) > 1) {
+    for (int i = 0; i < pat.length(); i++) {
+      uc16 c = pat[i];
+      if (c > String::kMaxAsciiCharCode) {
+        return SEARCH_FAIL;
+      }
+    }
+  }
+  if (pat.length() < kBMMinPatternLength) {
+    return SEARCH_SHORT;
+  }
+  algorithm = SIMPLE_SEARCH;
+  return SEARCH_LONG;
+}
+
+
+// Dispatch long needle searches to different algorithms.
+template <typename SubjectChar, typename PatternChar>
+static int ComplexIndexOf(Vector<const SubjectChar> sub,
+                          Vector<const PatternChar> pat,
+                          int start_index) {
+  ASSERT(pat.length() >= kBMMinPatternLength);
+  // Try algorithms in order of increasing setup cost and expected performance.
+  bool complete;
+  int idx = start_index;
+  switch (algorithm) {
+    case SIMPLE_SEARCH:
+      idx = SimpleIndexOf(sub, pat, idx, &complete);
+      if (complete) return idx;
+      BoyerMoorePopulateBadCharTable(pat);
+      algorithm = BOYER_MOORE_HORSPOOL;
+      // FALLTHROUGH.
+    case BOYER_MOORE_HORSPOOL:
+      idx = BoyerMooreHorspool(sub, pat, idx, &complete);
+      if (complete) return idx;
+      // Build the Good Suffix table and continue searching.
+      BoyerMoorePopulateGoodSuffixTable(pat);
+      algorithm = BOYER_MOORE;
+      // FALLTHROUGH.
+    case BOYER_MOORE:
+      return BoyerMooreIndexOf(sub, pat, idx);
+  }
+  UNREACHABLE();
+  return -1;
+}
+
+
+// Dispatch to different search strategies for a single search.
+// If searching multiple times on the same needle, the search
+// strategy should only be computed once and then dispatch to different
+// loops.
+template <typename SubjectChar, typename PatternChar>
+static int StringSearch(Vector<const SubjectChar> sub,
+                        Vector<const PatternChar> pat,
+                        int start_index) {
+  bool ascii_subject = (sizeof(SubjectChar) == 1);
+  StringSearchStrategy strategy = InitializeStringSearch(pat, ascii_subject);
+  switch (strategy) {
+    case SEARCH_FAIL: return -1;
+    case SEARCH_SHORT: return SimpleIndexOf(sub, pat, start_index);
+    case SEARCH_LONG: return ComplexIndexOf(sub, pat, start_index);
+  }
+  UNREACHABLE();
+  return -1;
+}
+
+}}  // namespace v8::internal
+
+#endif  // V8_STRING_SEARCH_H_
index 7a490d3..34989d3 100644 (file)
@@ -1222,23 +1222,23 @@ CallStubCompiler::CallStubCompiler(int argc,
 Object* CallStubCompiler::CompileCustomCall(int generator_id,
                                             Object* object,
                                             JSObject* holder,
+                                            JSGlobalPropertyCell* cell,
                                             JSFunction* function,
-                                            String* fname,
-                                            CheckType check) {
-    ASSERT(generator_id >= 0 && generator_id < kNumCallGenerators);
-    switch (generator_id) {
-#define CALL_GENERATOR_CASE(ignored1, ignored2, name)          \
-      case k##name##CallGenerator:                             \
-        return CallStubCompiler::Compile##name##Call(object,   \
-                                                     holder,   \
-                                                     function, \
-                                                     fname,    \
-                                                     check);
-      CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE)
+                                            String* fname) {
+  ASSERT(generator_id >= 0 && generator_id < kNumCallGenerators);
+  switch (generator_id) {
+#define CALL_GENERATOR_CASE(ignored1, ignored2, ignored3, name) \
+    case k##name##CallGenerator:                                \
+      return CallStubCompiler::Compile##name##Call(object,      \
+                                                   holder,      \
+                                                   cell,        \
+                                                   function,    \
+                                                   fname);
+    CUSTOM_CALL_IC_GENERATORS(CALL_GENERATOR_CASE)
 #undef CALL_GENERATOR_CASE
-    }
-    UNREACHABLE();
-    return Heap::undefined_value();
+  }
+  UNREACHABLE();
+  return Heap::undefined_value();
 }
 
 
index bf14a4f..388bb52 100644 (file)
@@ -612,21 +612,29 @@ class KeyedStoreStubCompiler: public StubCompiler {
 // Installation of custom call generators for the selected builtins is
 // handled by the bootstrapper.
 //
-// Each entry has a name of a global function (lowercased), a name of
-// a builtin function on its instance prototype (the one the generator
-// is set for), and a name of a generator itself (used to build ids
-// and generator function names).
-#define CUSTOM_CALL_IC_GENERATORS(V)      \
-  V(array, push, ArrayPush)               \
-  V(array, pop, ArrayPop)                 \
-  V(string, charCodeAt, StringCharCodeAt) \
-  V(string, charAt, StringCharAt)
+// Each entry has a name of a global function (lowercased), a flag
+// controlling whether the generator is set on the function itself or
+// on its instance prototype, a name of a builtin function on the
+// function or its instance prototype (the one the generator is set
+// for), and a name of a generator itself (used to build ids and
+// generator function names).
+#define CUSTOM_CALL_IC_GENERATORS(V)                          \
+  V(array, INSTANCE_PROTOTYPE, push, ArrayPush)               \
+  V(array, INSTANCE_PROTOTYPE, pop, ArrayPop)                 \
+  V(string, INSTANCE_PROTOTYPE, charCodeAt, StringCharCodeAt) \
+  V(string, INSTANCE_PROTOTYPE, charAt, StringCharAt)         \
+  V(string, FUNCTION, fromCharCode, StringFromCharCode)
 
 
 class CallStubCompiler: public StubCompiler {
  public:
+  enum CustomGeneratorOwner {
+    FUNCTION,
+    INSTANCE_PROTOTYPE
+  };
+
   enum {
-#define DECLARE_CALL_GENERATOR_ID(ignored1, ignored2, name) \
+#define DECLARE_CALL_GENERATOR_ID(ignored1, ignore2, ignored3, name) \
     k##name##CallGenerator,
     CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR_ID)
 #undef DECLARE_CALL_GENERATOR_ID
@@ -656,20 +664,21 @@ class CallStubCompiler: public StubCompiler {
                             JSFunction* function,
                             String* name);
 
-  // Compiles a custom call constant IC using the generator with given id.
+  // Compiles a custom call constant/global IC using the generator
+  // with given id. For constant calls cell is NULL.
   Object* CompileCustomCall(int generator_id,
                             Object* object,
                             JSObject* holder,
+                            JSGlobalPropertyCell* cell,
                             JSFunction* function,
-                            String* name,
-                            CheckType check);
+                            String* name);
 
-#define DECLARE_CALL_GENERATOR(ignored1, ignored2, name) \
-  Object* Compile##name##Call(Object* object,            \
-                              JSObject* holder,          \
-                              JSFunction* function,      \
-                              String* fname,             \
-                              CheckType check);
+#define DECLARE_CALL_GENERATOR(ignored1, ignored2, ignored3, name) \
+  Object* Compile##name##Call(Object* object,                      \
+                              JSObject* holder,                    \
+                              JSGlobalPropertyCell* cell,          \
+                              JSFunction* function,                \
+                              String* fname);
   CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR)
 #undef DECLARE_CALL_GENERATOR
 
@@ -689,6 +698,17 @@ class CallStubCompiler: public StubCompiler {
 
   void GenerateNameCheck(String* name, Label* miss);
 
+  void GenerateGlobalReceiverCheck(JSObject* object,
+                                   JSObject* holder,
+                                   String* name,
+                                   Label* miss);
+
+  // Generates code to load the function from the cell checking that
+  // it still contains the same function.
+  void GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
+                                    JSFunction* function,
+                                    Label* miss);
+
   // Generates a jump to CallIC miss stub. Returns Failure if the jump cannot
   // be generated.
   Object* GenerateMissBranch();
index a3b799e..9d1d683 100644 (file)
@@ -120,6 +120,9 @@ class Utf8 {
   static inline unsigned Encode(char* out, uchar c);
   static const byte* ReadBlock(Buffer<const char*> str, byte* buffer,
       unsigned capacity, unsigned* chars_read, unsigned* offset);
+  static uchar CalculateValue(const byte* str,
+                              unsigned length,
+                              unsigned* cursor);
   static const uchar kBadChar = 0xFFFD;
   static const unsigned kMaxEncodedSize   = 4;
   static const unsigned kMaxOneByteChar   = 0x7f;
@@ -133,9 +136,6 @@ class Utf8 {
   static inline uchar ValueOf(const byte* str,
                               unsigned length,
                               unsigned* cursor);
-  static uchar CalculateValue(const byte* str,
-                              unsigned length,
-                              unsigned* cursor);
 };
 
 // --- C h a r a c t e r   S t r e a m ---
index 30db7ab..daf267f 100644 (file)
@@ -391,6 +391,12 @@ class Vector {
   // Factory method for creating empty vectors.
   static Vector<T> empty() { return Vector<T>(NULL, 0); }
 
+  template<typename S>
+  static Vector<T> cast(Vector<S> input) {
+    return Vector<T>(reinterpret_cast<T*>(input.start()),
+                     input.length() * sizeof(S) / sizeof(T));
+  }
+
  protected:
   void set_start(T* start) { start_ = start; }
 
diff --git a/deps/v8/src/utils.h.orig b/deps/v8/src/utils.h.orig
new file mode 100644 (file)
index 0000000..fefbfe9
--- /dev/null
@@ -0,0 +1,962 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_UTILS_H_
+#define V8_UTILS_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// General helper functions
+
+#define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0)
+
+// Returns true iff x is a power of 2 (or zero). Cannot be used with the
+// maximally negative value of the type T (the -1 overflows).
+template <typename T>
+static inline bool IsPowerOf2(T x) {
+  return IS_POWER_OF_TWO(x);
+}
+
+
+// X must be a power of 2.  Returns the number of trailing zeros.
+template <typename T>
+static inline int WhichPowerOf2(T x) {
+  ASSERT(IsPowerOf2(x));
+  ASSERT(x != 0);
+  if (x < 0) return 31;
+  int bits = 0;
+#ifdef DEBUG
+  int original_x = x;
+#endif
+  if (x >= 0x10000) {
+    bits += 16;
+    x >>= 16;
+  }
+  if (x >= 0x100) {
+    bits += 8;
+    x >>= 8;
+  }
+  if (x >= 0x10) {
+    bits += 4;
+    x >>= 4;
+  }
+  switch (x) {
+    default: UNREACHABLE();
+    case 8: bits++;  // Fall through.
+    case 4: bits++;  // Fall through.
+    case 2: bits++;  // Fall through.
+    case 1: break;
+  }
+  ASSERT_EQ(1 << bits, original_x);
+  return bits;
+  return 0;
+}
+
+
+// The C++ standard leaves the semantics of '>>' undefined for
+// negative signed operands. Most implementations do the right thing,
+// though.
+static inline int ArithmeticShiftRight(int x, int s) {
+  return x >> s;
+}
+
+
+// Compute the 0-relative offset of some absolute value x of type T.
+// This allows conversion of Addresses and integral types into
+// 0-relative int offsets.
+template <typename T>
+static inline intptr_t OffsetFrom(T x) {
+  return x - static_cast<T>(0);
+}
+
+
+// Compute the absolute value of type T for some 0-relative offset x.
+// This allows conversion of 0-relative int offsets into Addresses and
+// integral types.
+template <typename T>
+static inline T AddressFrom(intptr_t x) {
+  return static_cast<T>(static_cast<T>(0) + x);
+}
+
+
+// Return the largest multiple of m which is <= x.
+template <typename T>
+static inline T RoundDown(T x, int m) {
+  ASSERT(IsPowerOf2(m));
+  return AddressFrom<T>(OffsetFrom(x) & -m);
+}
+
+
+// Return the smallest multiple of m which is >= x.
+template <typename T>
+static inline T RoundUp(T x, int m) {
+  return RoundDown(x + m - 1, m);
+}
+
+
+template <typename T>
+static int Compare(const T& a, const T& b) {
+  if (a == b)
+    return 0;
+  else if (a < b)
+    return -1;
+  else
+    return 1;
+}
+
+
+template <typename T>
+static int PointerValueCompare(const T* a, const T* b) {
+  return Compare<T>(*a, *b);
+}
+
+
+// Returns the smallest power of two which is >= x. If you pass in a
+// number that is already a power of two, it is returned as is.
+uint32_t RoundUpToPowerOf2(uint32_t x);
+
+
+template <typename T>
+static inline bool IsAligned(T value, T alignment) {
+  ASSERT(IsPowerOf2(alignment));
+  return (value & (alignment - 1)) == 0;
+}
+
+
+// Returns true if (addr + offset) is aligned.
+static inline bool IsAddressAligned(Address addr,
+                                    intptr_t alignment,
+                                    int offset) {
+  intptr_t offs = OffsetFrom(addr + offset);
+  return IsAligned(offs, alignment);
+}
+
+
+// Returns the maximum of the two parameters.
+template <typename T>
+static T Max(T a, T b) {
+  return a < b ? b : a;
+}
+
+
+// Returns the minimum of the two parameters.
+template <typename T>
+static T Min(T a, T b) {
+  return a < b ? a : b;
+}
+
+
+inline int StrLength(const char* string) {
+  size_t length = strlen(string);
+  ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
+  return static_cast<int>(length);
+}
+
+
+// ----------------------------------------------------------------------------
+// BitField is a help template for encoding and decode bitfield with
+// unsigned content.
+template<class T, int shift, int size>
+class BitField {
+ public:
+  // Tells whether the provided value fits into the bit field.
+  static bool is_valid(T value) {
+    return (static_cast<uint32_t>(value) & ~((1U << (size)) - 1)) == 0;
+  }
+
+  // Returns a uint32_t mask of bit field.
+  static uint32_t mask() {
+    // To use all bits of a uint32 in a bitfield without compiler warnings we
+    // have to compute 2^32 without using a shift count of 32.
+    return ((1U << shift) << size) - (1U << shift);
+  }
+
+  // Returns a uint32_t with the bit field value encoded.
+  static uint32_t encode(T value) {
+    ASSERT(is_valid(value));
+    return static_cast<uint32_t>(value) << shift;
+  }
+
+  // Extracts the bit field from the value.
+  static T decode(uint32_t value) {
+    return static_cast<T>((value & mask()) >> shift);
+  }
+};
+
+
+// ----------------------------------------------------------------------------
+// Hash function.
+
+uint32_t ComputeIntegerHash(uint32_t key);
+
+
+// ----------------------------------------------------------------------------
+// I/O support.
+
+// Our version of printf(). Avoids compilation errors that we get
+// with standard printf when attempting to print pointers, etc.
+// (the errors are due to the extra compilation flags, which we
+// want elsewhere).
+void PrintF(const char* format, ...);
+
+// Our version of fflush.
+void Flush();
+
+
+// Read a line of characters after printing the prompt to stdout. The resulting
+// char* needs to be disposed off with DeleteArray by the caller.
+char* ReadLine(const char* prompt);
+
+
+// Read and return the raw bytes in a file. the size of the buffer is returned
+// in size.
+// The returned buffer must be freed by the caller.
+byte* ReadBytes(const char* filename, int* size, bool verbose = true);
+
+
+// Write size chars from str to the file given by filename.
+// The file is overwritten. Returns the number of chars written.
+int WriteChars(const char* filename,
+               const char* str,
+               int size,
+               bool verbose = true);
+
+
+// Write size bytes to the file given by filename.
+// The file is overwritten. Returns the number of bytes written.
+int WriteBytes(const char* filename,
+               const byte* bytes,
+               int size,
+               bool verbose = true);
+
+
+// Write the C code
+// const char* <varname> = "<str>";
+// const int <varname>_len = <len>;
+// to the file given by filename. Only the first len chars are written.
+int WriteAsCFile(const char* filename, const char* varname,
+                 const char* str, int size, bool verbose = true);
+
+
+// ----------------------------------------------------------------------------
+// Miscellaneous
+
+// A static resource holds a static instance that can be reserved in
+// a local scope using an instance of Access.  Attempts to re-reserve
+// the instance will cause an error.
+template <typename T>
+class StaticResource {
+ public:
+  StaticResource() : is_reserved_(false)  {}
+
+ private:
+  template <typename S> friend class Access;
+  T instance_;
+  bool is_reserved_;
+};
+
+
+// Locally scoped access to a static resource.
+template <typename T>
+class Access {
+ public:
+  explicit Access(StaticResource<T>* resource)
+    : resource_(resource)
+    , instance_(&resource->instance_) {
+    ASSERT(!resource->is_reserved_);
+    resource->is_reserved_ = true;
+  }
+
+  ~Access() {
+    resource_->is_reserved_ = false;
+    resource_ = NULL;
+    instance_ = NULL;
+  }
+
+  T* value()  { return instance_; }
+  T* operator -> ()  { return instance_; }
+
+ private:
+  StaticResource<T>* resource_;
+  T* instance_;
+};
+
+
+template <typename T>
+class Vector {
+ public:
+  Vector() : start_(NULL), length_(0) {}
+  Vector(T* data, int length) : start_(data), length_(length) {
+    ASSERT(length == 0 || (length > 0 && data != NULL));
+  }
+
+  static Vector<T> New(int length) {
+    return Vector<T>(NewArray<T>(length), length);
+  }
+
+  // Returns a vector using the same backing storage as this one,
+  // spanning from and including 'from', to but not including 'to'.
+  Vector<T> SubVector(int from, int to) {
+    ASSERT(to <= length_);
+    ASSERT(from < to);
+    ASSERT(0 <= from);
+    return Vector<T>(start() + from, to - from);
+  }
+
+  // Returns the length of the vector.
+  int length() const { return length_; }
+
+  // Returns whether or not the vector is empty.
+  bool is_empty() const { return length_ == 0; }
+
+  // Returns the pointer to the start of the data in the vector.
+  T* start() const { return start_; }
+
+  // Access individual vector elements - checks bounds in debug mode.
+  T& operator[](int index) const {
+    ASSERT(0 <= index && index < length_);
+    return start_[index];
+  }
+
+  T& first() { return start_[0]; }
+
+  T& last() { return start_[length_ - 1]; }
+
+  // Returns a clone of this vector with a new backing store.
+  Vector<T> Clone() const {
+    T* result = NewArray<T>(length_);
+    for (int i = 0; i < length_; i++) result[i] = start_[i];
+    return Vector<T>(result, length_);
+  }
+
+  void Sort(int (*cmp)(const T*, const T*)) {
+    typedef int (*RawComparer)(const void*, const void*);
+    qsort(start(),
+          length(),
+          sizeof(T),
+          reinterpret_cast<RawComparer>(cmp));
+  }
+
+  void Sort() {
+    Sort(PointerValueCompare<T>);
+  }
+
+  void Truncate(int length) {
+    ASSERT(length <= length_);
+    length_ = length;
+  }
+
+  // Releases the array underlying this vector. Once disposed the
+  // vector is empty.
+  void Dispose() {
+    DeleteArray(start_);
+    start_ = NULL;
+    length_ = 0;
+  }
+
+  inline Vector<T> operator+(int offset) {
+    ASSERT(offset < length_);
+    return Vector<T>(start_ + offset, length_ - offset);
+  }
+
+  // Factory method for creating empty vectors.
+  static Vector<T> empty() { return Vector<T>(NULL, 0); }
+
+  template<typename S>
+  static Vector<T> cast(Vector<S> input) {
+    return Vector<T>(reinterpret_cast<T*>(input.start()),
+                     input.length() * sizeof(S) / sizeof(T));
+  }
+
+ protected:
+  void set_start(T* start) { start_ = start; }
+
+ private:
+  T* start_;
+  int length_;
+};
+
+
+// A temporary assignment sets a (non-local) variable to a value on
+// construction and resets it the value on destruction.
+template <typename T>
+class TempAssign {
+ public:
+  TempAssign(T* var, T value): var_(var), old_value_(*var) {
+    *var = value;
+  }
+
+  ~TempAssign() { *var_ = old_value_; }
+
+ private:
+  T* var_;
+  T old_value_;
+};
+
+
+template <typename T, int kSize>
+class EmbeddedVector : public Vector<T> {
+ public:
+  EmbeddedVector() : Vector<T>(buffer_, kSize) { }
+
+  // When copying, make underlying Vector to reference our buffer.
+  EmbeddedVector(const EmbeddedVector& rhs)
+      : Vector<T>(rhs) {
+    memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize);
+    set_start(buffer_);
+  }
+
+  EmbeddedVector& operator=(const EmbeddedVector& rhs) {
+    if (this == &rhs) return *this;
+    Vector<T>::operator=(rhs);
+    memcpy(buffer_, rhs.buffer_, sizeof(T) * kSize);
+    this->set_start(buffer_);
+    return *this;
+  }
+
+ private:
+  T buffer_[kSize];
+};
+
+
+template <typename T>
+class ScopedVector : public Vector<T> {
+ public:
+  explicit ScopedVector(int length) : Vector<T>(NewArray<T>(length), length) { }
+  ~ScopedVector() {
+    DeleteArray(this->start());
+  }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedVector);
+};
+
+
+inline Vector<const char> CStrVector(const char* data) {
+  return Vector<const char>(data, StrLength(data));
+}
+
+inline Vector<char> MutableCStrVector(char* data) {
+  return Vector<char>(data, StrLength(data));
+}
+
+inline Vector<char> MutableCStrVector(char* data, int max) {
+  int length = StrLength(data);
+  return Vector<char>(data, (length < max) ? length : max);
+}
+
+template <typename T>
+inline Vector< Handle<Object> > HandleVector(v8::internal::Handle<T>* elms,
+                                             int length) {
+  return Vector< Handle<Object> >(
+      reinterpret_cast<v8::internal::Handle<Object>*>(elms), length);
+}
+
+
+/*
+ * A class that collects values into a backing store.
+ * Specialized versions of the class can allow access to the backing store
+ * in different ways.
+ * There is no guarantee that the backing store is contiguous (and, as a
+ * consequence, no guarantees that consecutively added elements are adjacent
+ * in memory). The collector may move elements unless it has guaranteed not
+ * to.
+ */
+template <typename T, int growth_factor = 2, int max_growth = 1 * MB>
+class Collector {
+ public:
+  explicit Collector(int initial_capacity = kMinCapacity)
+      : index_(0), size_(0) {
+    if (initial_capacity < kMinCapacity) {
+      initial_capacity = kMinCapacity;
+    }
+    current_chunk_ = Vector<T>::New(initial_capacity);
+  }
+
+  virtual ~Collector() {
+    // Free backing store (in reverse allocation order).
+    current_chunk_.Dispose();
+    for (int i = chunks_.length() - 1; i >= 0; i--) {
+      chunks_.at(i).Dispose();
+    }
+  }
+
+  // Add a single element.
+  inline void Add(T value) {
+    if (index_ >= current_chunk_.length()) {
+      Grow(1);
+    }
+    current_chunk_[index_] = value;
+    index_++;
+    size_++;
+  }
+
+  // Add a block of contiguous elements and return a Vector backed by the
+  // memory area.
+  // A basic Collector will keep this vector valid as long as the Collector
+  // is alive.
+  inline Vector<T> AddBlock(int size, T initial_value) {
+    ASSERT(size > 0);
+    if (size > current_chunk_.length() - index_) {
+      Grow(size);
+    }
+    T* position = current_chunk_.start() + index_;
+    index_ += size;
+    size_ += size;
+    for (int i = 0; i < size; i++) {
+      position[i] = initial_value;
+    }
+    return Vector<T>(position, size);
+  }
+
+
+  // Write the contents of the collector into the provided vector.
+  void WriteTo(Vector<T> destination) {
+    ASSERT(size_ <= destination.length());
+    int position = 0;
+    for (int i = 0; i < chunks_.length(); i++) {
+      Vector<T> chunk = chunks_.at(i);
+      for (int j = 0; j < chunk.length(); j++) {
+        destination[position] = chunk[j];
+        position++;
+      }
+    }
+    for (int i = 0; i < index_; i++) {
+      destination[position] = current_chunk_[i];
+      position++;
+    }
+  }
+
+  // Allocate a single contiguous vector, copy all the collected
+  // elements to the vector, and return it.
+  // The caller is responsible for freeing the memory of the returned
+  // vector (e.g., using Vector::Dispose).
+  Vector<T> ToVector() {
+    Vector<T> new_store = Vector<T>::New(size_);
+    WriteTo(new_store);
+    return new_store;
+  }
+
+  // Resets the collector to be empty.
+  virtual void Reset() {
+    for (int i = chunks_.length() - 1; i >= 0; i--) {
+      chunks_.at(i).Dispose();
+    }
+    chunks_.Rewind(0);
+    index_ = 0;
+    size_ = 0;
+  }
+
+  // Total number of elements added to collector so far.
+  inline int size() { return size_; }
+
+ protected:
+  static const int kMinCapacity = 16;
+  List<Vector<T> > chunks_;
+  Vector<T> current_chunk_;  // Block of memory currently being written into.
+  int index_;  // Current index in current chunk.
+  int size_;  // Total number of elements in collector.
+
+  // Creates a new current chunk, and stores the old chunk in the chunks_ list.
+  void Grow(int min_capacity) {
+    ASSERT(growth_factor > 1);
+    int growth = current_chunk_.length() * (growth_factor - 1);
+    if (growth > max_growth) {
+      growth = max_growth;
+    }
+    int new_capacity = current_chunk_.length() + growth;
+    if (new_capacity < min_capacity) {
+      new_capacity = min_capacity + growth;
+    }
+    Vector<T> new_chunk = Vector<T>::New(new_capacity);
+    int new_index = PrepareGrow(new_chunk);
+    if (index_ > 0) {
+      chunks_.Add(current_chunk_.SubVector(0, index_));
+    } else {
+      // Can happen if the call to PrepareGrow moves everything into
+      // the new chunk.
+      current_chunk_.Dispose();
+    }
+    current_chunk_ = new_chunk;
+    index_ = new_index;
+    ASSERT(index_ + min_capacity <= current_chunk_.length());
+  }
+
+  // Before replacing the current chunk, give a subclass the option to move
+  // some of the current data into the new chunk. The function may update
+  // the current index_ value to represent data no longer in the current chunk.
+  // Returns the initial index of the new chunk (after copied data).
+  virtual int PrepareGrow(Vector<T> new_chunk)  {
+    return 0;
+  }
+};
+
+
+/*
+ * A collector that allows sequences of values to be guaranteed to
+ * stay consecutive.
+ * If the backing store grows while a sequence is active, the current
+ * sequence might be moved, but after the sequence is ended, it will
+ * not move again.
+ * NOTICE: Blocks allocated using Collector::AddBlock(int) can move
+ * as well, if inside an active sequence where another element is added.
+ */
+template <typename T, int growth_factor = 2, int max_growth = 1 * MB>
+class SequenceCollector : public Collector<T, growth_factor, max_growth> {
+ public:
+  explicit SequenceCollector(int initial_capacity)
+      : Collector<T, growth_factor, max_growth>(initial_capacity),
+        sequence_start_(kNoSequence) { }
+
+  virtual ~SequenceCollector() {}
+
+  void StartSequence() {
+    ASSERT(sequence_start_ == kNoSequence);
+    sequence_start_ = this->index_;
+  }
+
+  Vector<T> EndSequence() {
+    ASSERT(sequence_start_ != kNoSequence);
+    int sequence_start = sequence_start_;
+    sequence_start_ = kNoSequence;
+    if (sequence_start == this->index_) return Vector<T>();
+    return this->current_chunk_.SubVector(sequence_start, this->index_);
+  }
+
+  // Drops the currently added sequence, and all collected elements in it.
+  void DropSequence() {
+    ASSERT(sequence_start_ != kNoSequence);
+    int sequence_length = this->index_ - sequence_start_;
+    this->index_ = sequence_start_;
+    this->size_ -= sequence_length;
+    sequence_start_ = kNoSequence;
+  }
+
+  virtual void Reset() {
+    sequence_start_ = kNoSequence;
+    this->Collector<T, growth_factor, max_growth>::Reset();
+  }
+
+ private:
+  static const int kNoSequence = -1;
+  int sequence_start_;
+
+  // Move the currently active sequence to the new chunk.
+  virtual int PrepareGrow(Vector<T> new_chunk) {
+    if (sequence_start_ != kNoSequence) {
+      int sequence_length = this->index_ - sequence_start_;
+      // The new chunk is always larger than the current chunk, so there
+      // is room for the copy.
+      ASSERT(sequence_length < new_chunk.length());
+      for (int i = 0; i < sequence_length; i++) {
+        new_chunk[i] = this->current_chunk_[sequence_start_ + i];
+      }
+      this->index_ = sequence_start_;
+      sequence_start_ = 0;
+      return sequence_length;
+    }
+    return 0;
+  }
+};
+
+
+// Simple support to read a file into a 0-terminated C-string.
+// The returned buffer must be freed by the caller.
+// On return, *exits tells whether the file existed.
+Vector<const char> ReadFile(const char* filename,
+                            bool* exists,
+                            bool verbose = true);
+
+
+// Simple wrapper that allows an ExternalString to refer to a
+// Vector<const char>. Doesn't assume ownership of the data.
+class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource {
+ public:
+  explicit AsciiStringAdapter(Vector<const char> data) : data_(data) {}
+
+  virtual const char* data() const { return data_.start(); }
+
+  virtual size_t length() const { return data_.length(); }
+
+ private:
+  Vector<const char> data_;
+};
+
+
+// Helper class for building result strings in a character buffer. The
+// purpose of the class is to use safe operations that checks the
+// buffer bounds on all operations in debug mode.
+class StringBuilder {
+ public:
+  // Create a string builder with a buffer of the given size. The
+  // buffer is allocated through NewArray<char> and must be
+  // deallocated by the caller of Finalize().
+  explicit StringBuilder(int size);
+
+  StringBuilder(char* buffer, int size)
+      : buffer_(buffer, size), position_(0) { }
+
+  ~StringBuilder() { if (!is_finalized()) Finalize(); }
+
+  int size() const { return buffer_.length(); }
+
+  // Get the current position in the builder.
+  int position() const {
+    ASSERT(!is_finalized());
+    return position_;
+  }
+
+  // Reset the position.
+  void Reset() { position_ = 0; }
+
+  // Add a single character to the builder. It is not allowed to add
+  // 0-characters; use the Finalize() method to terminate the string
+  // instead.
+  void AddCharacter(char c) {
+    ASSERT(c != '\0');
+    ASSERT(!is_finalized() && position_ < buffer_.length());
+    buffer_[position_++] = c;
+  }
+
+  // Add an entire string to the builder. Uses strlen() internally to
+  // compute the length of the input string.
+  void AddString(const char* s);
+
+  // Add the first 'n' characters of the given string 's' to the
+  // builder. The input string must have enough characters.
+  void AddSubstring(const char* s, int n);
+
+  // Add formatted contents to the builder just like printf().
+  void AddFormatted(const char* format, ...);
+
+  // Add character padding to the builder. If count is non-positive,
+  // nothing is added to the builder.
+  void AddPadding(char c, int count);
+
+  // Finalize the string by 0-terminating it and returning the buffer.
+  char* Finalize();
+
+ private:
+  Vector<char> buffer_;
+  int position_;
+
+  bool is_finalized() const { return position_ < 0; }
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
+};
+
+
+// Custom memcpy implementation for platforms where the standard version
+// may not be good enough.
+// TODO(lrn): Check whether some IA32 platforms should be excluded.
+#if defined(V8_TARGET_ARCH_IA32)
+
+// TODO(lrn): Extend to other platforms as needed.
+
+typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size);
+
+// Implemented in codegen-<arch>.cc.
+MemCopyFunction CreateMemCopyFunction();
+
+// Copy memory area to disjoint memory area.
+static inline void MemCopy(void* dest, const void* src, size_t size) {
+  static MemCopyFunction memcopy = CreateMemCopyFunction();
+  (*memcopy)(dest, src, size);
+#ifdef DEBUG
+  CHECK_EQ(0, memcmp(dest, src, size));
+#endif
+}
+
+
+// Limit below which the extra overhead of the MemCopy function is likely
+// to outweigh the benefits of faster copying.
+// TODO(lrn): Try to find a more precise value.
+static const int kMinComplexMemCopy = 64;
+
+#else  // V8_TARGET_ARCH_IA32
+
+static inline void MemCopy(void* dest, const void* src, size_t size) {
+  memcpy(dest, src, size);
+}
+
+static const int kMinComplexMemCopy = 256;
+
+#endif  // V8_TARGET_ARCH_IA32
+
+
+// Copy from ASCII/16bit chars to ASCII/16bit chars.
+template <typename sourcechar, typename sinkchar>
+static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
+  sinkchar* limit = dest + chars;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+  if (sizeof(*dest) == sizeof(*src)) {
+    if (chars >= static_cast<int>(kMinComplexMemCopy / sizeof(*dest))) {
+      MemCopy(dest, src, chars * sizeof(*dest));
+      return;
+    }
+    // Number of characters in a uintptr_t.
+    static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest);  // NOLINT
+    while (dest <= limit - kStepSize) {
+      *reinterpret_cast<uintptr_t*>(dest) =
+          *reinterpret_cast<const uintptr_t*>(src);
+      dest += kStepSize;
+      src += kStepSize;
+    }
+  }
+#endif
+  while (dest < limit) {
+    *dest++ = static_cast<sinkchar>(*src++);
+  }
+}
+
+
+// Compare ASCII/16bit chars to ASCII/16bit chars.
+template <typename lchar, typename rchar>
+static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) {
+  const lchar* limit = lhs + chars;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+  if (sizeof(*lhs) == sizeof(*rhs)) {
+    // Number of characters in a uintptr_t.
+    static const int kStepSize = sizeof(uintptr_t) / sizeof(*lhs);  // NOLINT
+    while (lhs <= limit - kStepSize) {
+      if (*reinterpret_cast<const uintptr_t*>(lhs) !=
+          *reinterpret_cast<const uintptr_t*>(rhs)) {
+        break;
+      }
+      lhs += kStepSize;
+      rhs += kStepSize;
+    }
+  }
+#endif
+  while (lhs < limit) {
+    int r = static_cast<int>(*lhs) - static_cast<int>(*rhs);
+    if (r != 0) return r;
+    ++lhs;
+    ++rhs;
+  }
+  return 0;
+}
+
+
+template <typename T>
+static inline void MemsetPointer(T** dest, T* value, int counter) {
+#if defined(V8_HOST_ARCH_IA32)
+#define STOS "stosl"
+#elif defined(V8_HOST_ARCH_X64)
+#define STOS "stosq"
+#endif
+
+#if defined(__GNUC__) && defined(STOS)
+  asm volatile(
+      "cld;"
+      "rep ; " STOS
+      : "+&c" (counter), "+&D" (dest)
+      : "a" (value)
+      : "memory", "cc");
+#else
+  for (int i = 0; i < counter; i++) {
+    dest[i] = value;
+  }
+#endif
+
+#undef STOS
+}
+
+
+// Copies data from |src| to |dst|.  The data spans MUST not overlap.
+inline void CopyWords(Object** dst, Object** src, int num_words) {
+  ASSERT(Min(dst, src) + num_words <= Max(dst, src));
+  ASSERT(num_words > 0);
+
+  // Use block copying memcpy if the segment we're copying is
+  // enough to justify the extra call/setup overhead.
+  static const int kBlockCopyLimit = 16;
+
+  if (num_words >= kBlockCopyLimit) {
+    memcpy(dst, src, num_words * kPointerSize);
+  } else {
+    int remaining = num_words;
+    do {
+      remaining--;
+      *dst++ = *src++;
+    } while (remaining > 0);
+  }
+}
+
+
+// Calculate 10^exponent.
+int TenToThe(int exponent);
+
+
+// The type-based aliasing rule allows the compiler to assume that pointers of
+// different types (for some definition of different) never alias each other.
+// Thus the following code does not work:
+//
+// float f = foo();
+// int fbits = *(int*)(&f);
+//
+// The compiler 'knows' that the int pointer can't refer to f since the types
+// don't match, so the compiler may cache f in a register, leaving random data
+// in fbits.  Using C++ style casts makes no difference, however a pointer to
+// char data is assumed to alias any other pointer.  This is the 'memcpy
+// exception'.
+//
+// Bit_cast uses the memcpy exception to move the bits from a variable of one
+// type of a variable of another type.  Of course the end result is likely to
+// be implementation dependent.  Most compilers (gcc-4.2 and MSVC 2005)
+// will completely optimize BitCast away.
+//
+// There is an additional use for BitCast.
+// Recent gccs will warn when they see casts that may result in breakage due to
+// the type-based aliasing rule.  If you have checked that there is no breakage
+// you can use BitCast to cast one pointer type to another.  This confuses gcc
+// enough that it can no longer see that you have cast one pointer type to
+// another thus avoiding the warning.
+template <class Dest, class Source>
+inline Dest BitCast(const Source& source) {
+  // Compile time assertion: sizeof(Dest) == sizeof(Source)
+  // A compile error here means your Dest and Source have different sizes.
+  typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
+
+  Dest dest;
+  memcpy(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+template <class Dest, class Source>
+inline Dest BitCast(Source* source) {
+  return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
+}
+
+} }  // namespace v8::internal
+
+#endif  // V8_UTILS_H_
index 0af1795..c144ade 100644 (file)
@@ -34,7 +34,7 @@
 // cannot be changed without changing the SCons build script.
 #define MAJOR_VERSION     2
 #define MINOR_VERSION     4
-#define BUILD_NUMBER      2
+#define BUILD_NUMBER      4
 #define PATCH_LEVEL       0
 #define CANDIDATE_VERSION false
 
index 9ad94ce..9318bb8 100644 (file)
@@ -829,6 +829,7 @@ void Assembler::call(Label* L) {
 
 
 void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) {
+  WriteRecordedPositions();
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
   // 1110 1000 #32-bit disp.
index c75b945..b480412 100644 (file)
@@ -1404,33 +1404,35 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
   Label slow, done;
 
   if (op_ == Token::SUB) {
-    // Check whether the value is a smi.
-    Label try_float;
-    __ JumpIfNotSmi(rax, &try_float);
-
-    if (negative_zero_ == kIgnoreNegativeZero) {
-      __ SmiCompare(rax, Smi::FromInt(0));
-      __ j(equal, &done);
-    }
-
-    // Enter runtime system if the value of the smi is zero
-    // to make sure that we switch between 0 and -0.
-    // Also enter it if the value of the smi is Smi::kMinValue.
-    __ SmiNeg(rax, rax, &done);
-
-    // Either zero or Smi::kMinValue, neither of which become a smi when
-    // negated.
-    if (negative_zero_ == kStrictNegativeZero) {
-      __ SmiCompare(rax, Smi::FromInt(0));
-      __ j(not_equal, &slow);
-      __ Move(rax, Factory::minus_zero_value());
-      __ jmp(&done);
-    } else  {
-      __ jmp(&slow);
+    if (include_smi_code_) {
+      // Check whether the value is a smi.
+      Label try_float;
+      __ JumpIfNotSmi(rax, &try_float);
+      if (negative_zero_ == kIgnoreNegativeZero) {
+        __ SmiCompare(rax, Smi::FromInt(0));
+        __ j(equal, &done);
+      }
+      __ SmiNeg(rax, rax, &done);
+
+      // Either zero or Smi::kMinValue, neither of which become a smi when
+      // negated. We handle negative zero here if required. We always enter
+      // the runtime system if we have Smi::kMinValue.
+      if (negative_zero_ == kStrictNegativeZero) {
+        __ SmiCompare(rax, Smi::FromInt(0));
+        __ j(not_equal, &slow);
+        __ Move(rax, Factory::minus_zero_value());
+        __ jmp(&done);
+      } else  {
+        __ SmiCompare(rax, Smi::FromInt(Smi::kMinValue));
+        __ j(equal, &slow);
+        __ jmp(&done);
+      }
+      // Try floating point case.
+      __ bind(&try_float);
+    } else if (FLAG_debug_code) {
+      __ AbortIfSmi(rax);
     }
 
-    // Try floating point case.
-    __ bind(&try_float);
     __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
     __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
     __ j(not_equal, &slow);
@@ -1449,6 +1451,17 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
       __ movq(rax, rcx);
     }
   } else if (op_ == Token::BIT_NOT) {
+    if (include_smi_code_) {
+      Label try_float;
+      __ JumpIfNotSmi(rax, &try_float);
+      __ SmiNot(rax, rax);
+      __ jmp(&done);
+      // Try floating point case.
+      __ bind(&try_float);
+    } else if (FLAG_debug_code) {
+      __ AbortIfSmi(rax);
+    }
+
     // Check if the operand is a heap number.
     __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
     __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
@@ -2115,6 +2128,26 @@ void CompareStub::Generate(MacroAssembler* masm) {
   ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
 
   Label check_unequal_objects, done;
+
+  // Compare two smis if required.
+  if (include_smi_compare_) {
+    Label non_smi, smi_done;
+    __ JumpIfNotBothSmi(rax, rdx, &non_smi);
+    __ subq(rdx, rax);
+    __ j(no_overflow, &smi_done);
+    __ neg(rdx);  // Correct sign in case of overflow.
+    __ bind(&smi_done);
+    __ movq(rax, rdx);
+    __ ret(0);
+    __ bind(&non_smi);
+  } else if (FLAG_debug_code) {
+    Label ok;
+    __ JumpIfNotSmi(rdx, &ok);
+    __ JumpIfNotSmi(rax, &ok);
+    __ Abort("CompareStub: smi operands");
+    __ bind(&ok);
+  }
+
   // The compare stub returns a positive, negative, or zero 64-bit integer
   // value in rax, corresponding to result of comparing the two inputs.
   // NOTICE! This code is only reached after a smi-fast-case check, so
@@ -3001,7 +3034,8 @@ int CompareStub::MinorKey() {
          | RegisterField::encode(false)    // lhs_ and rhs_ are not used
          | StrictField::encode(strict_)
          | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
-         | IncludeNumberCompareField::encode(include_number_compare_);
+         | IncludeNumberCompareField::encode(include_number_compare_)
+         | IncludeSmiCompareField::encode(include_smi_compare_);
 }
 
 
@@ -3041,12 +3075,18 @@ const char* CompareStub::GetName() {
     include_number_compare_name = "_NO_NUMBER";
   }
 
+  const char* include_smi_compare_name = "";
+  if (!include_smi_compare_) {
+    include_smi_compare_name = "_NO_SMI";
+  }
+
   OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
                "CompareStub_%s%s%s%s",
                cc_name,
                strict_name,
                never_nan_nan_name,
-               include_number_compare_name);
+               include_number_compare_name,
+               include_smi_compare_name);
   return name_;
 }
 
index b1dd45e..91d9ff0 100644 (file)
@@ -1940,6 +1940,19 @@ static Condition DoubleCondition(Condition cc) {
 }
 
 
+static CompareFlags ComputeCompareFlags(NaNInformation nan_info,
+                                        bool inline_number_compare) {
+  CompareFlags flags = NO_SMI_COMPARE_IN_STUB;
+  if (nan_info == kCantBothBeNaN) {
+    flags = static_cast<CompareFlags>(flags | CANT_BOTH_BE_NAN);
+  }
+  if (inline_number_compare) {
+    flags = static_cast<CompareFlags>(flags | NO_NUMBER_COMPARE_IN_STUB);
+  }
+  return flags;
+}
+
+
 void CodeGenerator::Comparison(AstNode* node,
                                Condition cc,
                                bool strict,
@@ -2070,7 +2083,9 @@ void CodeGenerator::Comparison(AstNode* node,
 
       // Setup and call the compare stub.
       is_not_string.Bind(&left_side);
-      CompareStub stub(cc, strict, kCantBothBeNaN);
+      CompareFlags flags =
+          static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_CODE_IN_STUB);
+      CompareStub stub(cc, strict, flags);
       Result result = frame_->CallStub(&stub, &left_side, &right_side);
       result.ToRegister();
       __ testq(result.reg(), result.reg());
@@ -2174,7 +2189,8 @@ void CodeGenerator::Comparison(AstNode* node,
 
       // End of in-line compare, call out to the compare stub. Don't include
       // number comparison in the stub if it was inlined.
-      CompareStub stub(cc, strict, nan_info, !inline_number_compare);
+      CompareFlags flags = ComputeCompareFlags(nan_info, inline_number_compare);
+      CompareStub stub(cc, strict, flags);
       Result answer = frame_->CallStub(&stub, &left_side, &right_side);
       __ testq(answer.reg(), answer.reg());  // Sets both zero and sign flag.
       answer.Unuse();
@@ -2207,7 +2223,9 @@ void CodeGenerator::Comparison(AstNode* node,
 
         // End of in-line compare, call out to the compare stub. Don't include
         // number comparison in the stub if it was inlined.
-        CompareStub stub(cc, strict, nan_info, !inline_number_compare);
+        CompareFlags flags =
+            ComputeCompareFlags(nan_info, inline_number_compare);
+        CompareStub stub(cc, strict, flags);
         Result answer = frame_->CallStub(&stub, &left_side, &right_side);
         __ testq(answer.reg(), answer.reg());  // Sets both zero and sign flags.
         answer.Unuse();
@@ -2332,7 +2350,9 @@ void CodeGenerator::ConstantSmiComparison(Condition cc,
       }
 
       // Setup and call the compare stub.
-      CompareStub stub(cc, strict, kCantBothBeNaN);
+      CompareFlags flags =
+          static_cast<CompareFlags>(CANT_BOTH_BE_NAN | NO_SMI_CODE_IN_STUB);
+      CompareStub stub(cc, strict, flags);
       Result result = frame_->CallStub(&stub, left_side, right_side);
       result.ToRegister();
       __ testq(result.reg(), result.reg());
@@ -7395,6 +7415,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
         GenericUnaryOpStub stub(
             Token::SUB,
             overwrite,
+            NO_UNARY_FLAGS,
             no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
         Result operand = frame_->Pop();
         Result answer = frame_->CallStub(&stub, &operand);
@@ -7413,7 +7434,9 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
         Condition is_smi = masm_->CheckSmi(operand.reg());
         smi_label.Branch(is_smi, &operand);
 
-        GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
+        GenericUnaryOpStub stub(Token::BIT_NOT,
+                                overwrite,
+                                NO_UNARY_SMI_CODE_IN_STUB);
         Result answer = frame_->CallStub(&stub, &operand);
         continue_label.Jump(&answer);
 
index 07bdadf..6e1dd72 100644 (file)
@@ -343,15 +343,17 @@ class CodeGenerator: public AstVisitor {
   bool in_spilled_code() const { return in_spilled_code_; }
   void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; }
 
-  // If the name is an inline runtime function call return the number of
-  // expected arguments. Otherwise return -1.
-  static int InlineRuntimeCallArgumentsCount(Handle<String> name);
-
   static Operand ContextOperand(Register context, int index) {
     return Operand(context, Context::SlotOffset(index));
   }
 
  private:
+  // Type of a member function that generates inline code for a native function.
+  typedef void (CodeGenerator::*InlineFunctionGenerator)
+      (ZoneList<Expression*>*);
+
+  static const InlineFunctionGenerator kInlineFunctionGenerators[];
+
   // Construction/Destruction
   explicit CodeGenerator(MacroAssembler* masm);
 
@@ -584,12 +586,9 @@ class CodeGenerator: public AstVisitor {
 
   void CheckStack();
 
-  struct InlineRuntimeLUT {
-    void (CodeGenerator::*method)(ZoneList<Expression*>*);
-    const char* name;
-    int nargs;
-  };
-  static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name);
+  static InlineFunctionGenerator FindInlineFunctionGenerator(
+      Runtime::FunctionId function_id);
+
   bool CheckForInlineRuntimeCall(CallRuntime* node);
 
   void ProcessDeclarations(ZoneList<Declaration*>* declarations);
@@ -742,8 +741,6 @@ class CodeGenerator: public AstVisitor {
   // in a spilled state.
   bool in_spilled_code_;
 
-  static InlineRuntimeLUT kInlineRuntimeLUT[];
-
   friend class VirtualFrame;
   friend class JumpTarget;
   friend class Reference;
index ccd0392..e4faafc 100644 (file)
@@ -507,7 +507,7 @@ MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) {
       int context_chain_length =
           scope()->ContextChainLength(slot->var()->scope());
       __ LoadContext(scratch, context_chain_length);
-      return CodeGenerator::ContextOperand(scratch, slot->index());
+      return ContextOperand(scratch, slot->index());
     }
     case Slot::LOOKUP:
       UNREACHABLE();
@@ -568,20 +568,17 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable,
         ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
         if (FLAG_debug_code) {
           // Check if we have the correct context pointer.
-          __ movq(rbx,
-                  CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX));
+          __ movq(rbx, ContextOperand(rsi, Context::FCONTEXT_INDEX));
           __ cmpq(rbx, rsi);
           __ Check(equal, "Unexpected declaration in current context.");
         }
         if (mode == Variable::CONST) {
           __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
-          __ movq(CodeGenerator::ContextOperand(rsi, slot->index()),
-                  kScratchRegister);
+          __ movq(ContextOperand(rsi, slot->index()), kScratchRegister);
           // No write barrier since the hole value is in old space.
         } else if (function != NULL) {
           VisitForValue(function, kAccumulator);
-          __ movq(CodeGenerator::ContextOperand(rsi, slot->index()),
-                  result_register());
+          __ movq(ContextOperand(rsi, slot->index()), result_register());
           int offset = Context::SlotOffset(slot->index());
           __ movq(rbx, rsi);
           __ RecordWrite(rbx, offset, result_register(), rcx);
@@ -680,9 +677,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
     VisitForValue(clause->label(), kAccumulator);
 
     // Perform the comparison as if via '==='.
-    if (ShouldInlineSmiCase(Token::EQ_STRICT)) {
+    __ movq(rdx, Operand(rsp, 0));  // Switch value.
+    bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
+    if (inline_smi_code) {
       Label slow_case;
-      __ movq(rdx, Operand(rsp, 0));  // Switch value.
       __ JumpIfNotBothSmi(rdx, rax, &slow_case);
       __ SmiCompare(rdx, rax);
       __ j(not_equal, &next_test);
@@ -691,7 +689,10 @@ void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
       __ bind(&slow_case);
     }
 
-    CompareStub stub(equal, true);
+    CompareFlags flags = inline_smi_code
+        ? NO_SMI_COMPARE_IN_STUB
+        : NO_COMPARE_FLAGS;
+    CompareStub stub(equal, true, flags);
     __ CallStub(&stub);
     __ testq(rax, rax);
     __ j(not_equal, &next_test);
@@ -749,11 +750,10 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
   __ bind(&done_convert);
   __ push(rax);
 
-  // TODO(kasperl): Check cache validity in generated code. This is a
-  // fast case for the JSObject::IsSimpleEnum cache validity
-  // checks. If we cannot guarantee cache validity, call the runtime
-  // system to check cache validity or get the property names in a
-  // fixed array.
+  // BUG(867): Check cache validity in generated code. This is a fast
+  // case for the JSObject::IsSimpleEnum cache validity checks. If we
+  // cannot guarantee cache validity, call the runtime system to check
+  // cache validity or get the property names in a fixed array.
 
   // Get the set of properties to enumerate.
   __ push(rax);  // Duplicate the enumerable object on the stack.
@@ -881,6 +881,153 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
 }
 
 
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
+    Slot* slot,
+    TypeofState typeof_state,
+    Label* slow) {
+  Register context = rsi;
+  Register temp = rdx;
+
+  Scope* s = scope();
+  while (s != NULL) {
+    if (s->num_heap_slots() > 0) {
+      if (s->calls_eval()) {
+        // Check that extension is NULL.
+        __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
+                Immediate(0));
+        __ j(not_equal, slow);
+      }
+      // Load next context in chain.
+      __ movq(temp, ContextOperand(context, Context::CLOSURE_INDEX));
+      __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset));
+      // Walk the rest of the chain without clobbering rsi.
+      context = temp;
+    }
+    // If no outer scope calls eval, we do not need to check more
+    // context extensions.  If we have reached an eval scope, we check
+    // all extensions from this point.
+    if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
+    s = s->outer_scope();
+  }
+
+  if (s != NULL && s->is_eval_scope()) {
+    // Loop up the context chain.  There is no frame effect so it is
+    // safe to use raw labels here.
+    Label next, fast;
+    if (!context.is(temp)) {
+      __ movq(temp, context);
+    }
+    // Load map for comparison into register, outside loop.
+    __ LoadRoot(kScratchRegister, Heap::kGlobalContextMapRootIndex);
+    __ bind(&next);
+    // Terminate at global context.
+    __ cmpq(kScratchRegister, FieldOperand(temp, HeapObject::kMapOffset));
+    __ j(equal, &fast);
+    // Check that extension is NULL.
+    __ cmpq(ContextOperand(temp, Context::EXTENSION_INDEX), Immediate(0));
+    __ j(not_equal, slow);
+    // Load next context in chain.
+    __ movq(temp, ContextOperand(temp, Context::CLOSURE_INDEX));
+    __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset));
+    __ jmp(&next);
+    __ bind(&fast);
+  }
+
+  // All extension objects were empty and it is safe to use a global
+  // load IC call.
+  __ movq(rax, CodeGenerator::GlobalObject());
+  __ Move(rcx, slot->var()->name());
+  Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+  RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+      ? RelocInfo::CODE_TARGET
+      : RelocInfo::CODE_TARGET_CONTEXT;
+  __ call(ic, mode);
+  __ nop();  // Signal no inlined code.
+}
+
+
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
+    Slot* slot,
+    Label* slow) {
+  ASSERT(slot->type() == Slot::CONTEXT);
+  Register context = rsi;
+  Register temp = rbx;
+
+  for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
+    if (s->num_heap_slots() > 0) {
+      if (s->calls_eval()) {
+        // Check that extension is NULL.
+        __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX),
+                Immediate(0));
+        __ j(not_equal, slow);
+      }
+      __ movq(temp, ContextOperand(context, Context::CLOSURE_INDEX));
+      __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset));
+      // Walk the rest of the chain without clobbering rsi.
+      context = temp;
+    }
+  }
+  // Check that last extension is NULL.
+  __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0));
+  __ j(not_equal, slow);
+  __ movq(temp, ContextOperand(context, Context::FCONTEXT_INDEX));
+  return ContextOperand(temp, slot->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
+    Slot* slot,
+    TypeofState typeof_state,
+    Label* slow,
+    Label* done) {
+  // Generate fast-case code for variables that might be shadowed by
+  // eval-introduced variables.  Eval is used a lot without
+  // introducing variables.  In those cases, we do not want to
+  // perform a runtime call for all variables in the scope
+  // containing the eval.
+  if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
+    EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow);
+    __ jmp(done);
+  } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
+    Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
+    Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
+    if (potential_slot != NULL) {
+      // Generate fast case for locals that rewrite to slots.
+      __ movq(rax,
+              ContextSlotOperandCheckExtensions(potential_slot, slow));
+      if (potential_slot->var()->mode() == Variable::CONST) {
+        __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+        __ j(not_equal, done);
+        __ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
+      }
+      __ jmp(done);
+    } else if (rewrite != NULL) {
+      // Generate fast case for calls of an argument function.
+      Property* property = rewrite->AsProperty();
+      if (property != NULL) {
+        VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+        Literal* key_literal = property->key()->AsLiteral();
+        if (obj_proxy != NULL &&
+            key_literal != NULL &&
+            obj_proxy->IsArguments() &&
+            key_literal->handle()->IsSmi()) {
+          // Load arguments object if there are no eval-introduced
+          // variables. Then load the argument from the arguments
+          // object using keyed load.
+          __ movq(rdx,
+                  ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                    slow));
+          __ Move(rax, key_literal->handle());
+          Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+          __ call(ic, RelocInfo::CODE_TARGET);
+          __ jmp(done);
+        }
+      }
+    }
+  }
+}
+
+
 void FullCodeGenerator::EmitVariableLoad(Variable* var,
                                          Expression::Context context) {
   // Four cases: non-this global variables, lookup slots, all other
@@ -904,10 +1051,19 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
     Apply(context, rax);
 
   } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
+    Label done, slow;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done);
+
+    __ bind(&slow);
     Comment cmnt(masm_, "Lookup slot");
     __ push(rsi);  // Context.
     __ Push(var->name());
     __ CallRuntime(Runtime::kLoadContextSlot, 2);
+    __ bind(&done);
+
     Apply(context, rax);
 
   } else if (slot != NULL) {
@@ -1713,15 +1869,42 @@ void FullCodeGenerator::VisitCall(Call* expr) {
     EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
   } else if (var != NULL && var->slot() != NULL &&
              var->slot()->type() == Slot::LOOKUP) {
-    // Call to a lookup slot (dynamically introduced variable).  Call
-    // the runtime to find the function to call (returned in rax) and
-    // the object holding it (returned in rdx).
+    // Call to a lookup slot (dynamically introduced variable).
+    Label slow, done;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    EmitDynamicLoadFromSlotFastCase(var->slot(),
+                                    NOT_INSIDE_TYPEOF,
+                                    &slow,
+                                    &done);
+
+    __ bind(&slow);
+    // Call the runtime to find the function to call (returned in rax)
+    // and the object holding it (returned in rdx).
     __ push(context_register());
     __ Push(var->name());
     __ CallRuntime(Runtime::kLoadContextSlot, 2);
     __ push(rax);  // Function.
     __ push(rdx);  // Receiver.
+
+    // If fast case code has been generated, emit code to push the
+    // function and receiver and have the slow path jump around this
+    // code.
+    if (done.is_linked()) {
+      Label call;
+      __ jmp(&call);
+      __ bind(&done);
+      // Push function.
+      __ push(rax);
+      // Push global receiver.
+      __ movq(rbx, CodeGenerator::GlobalObject());
+      __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+      __ bind(&call);
+    }
+
     EmitCallWithStub(expr);
+
   } else if (fun->AsProperty() != NULL) {
     // Call to an object property.
     Property* prop = fun->AsProperty();
@@ -2522,12 +2705,11 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
   Register key = rax;
   Register cache = rbx;
   Register tmp = rcx;
-  __ movq(cache, CodeGenerator::ContextOperand(rsi, Context::GLOBAL_INDEX));
+  __ movq(cache, ContextOperand(rsi, Context::GLOBAL_INDEX));
   __ movq(cache,
           FieldOperand(cache, GlobalObject::kGlobalContextOffset));
   __ movq(cache,
-          CodeGenerator::ContextOperand(
-              cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+          ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
   __ movq(cache,
           FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
 
@@ -2777,7 +2959,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
       UnaryOverwriteMode overwrite =
           can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-      GenericUnaryOpStub stub(Token::SUB, overwrite);
+      GenericUnaryOpStub stub(Token::SUB, overwrite, NO_UNARY_FLAGS);
       // GenericUnaryOpStub expects the argument to be in the
       // accumulator register rax.
       VisitForValue(expr->expression(), kAccumulator);
@@ -2792,7 +2974,8 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       // in the accumulator register rax.
       VisitForValue(expr->expression(), kAccumulator);
       Label done;
-      if (ShouldInlineSmiCase(expr->op())) {
+      bool inline_smi_case = ShouldInlineSmiCase(expr->op());
+      if (inline_smi_case) {
         Label call_stub;
         __ JumpIfNotSmi(rax, &call_stub);
         __ SmiNot(rax, rax);
@@ -2802,7 +2985,10 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       bool overwrite = expr->expression()->ResultOverwriteAllowed();
       UnaryOverwriteMode mode =
           overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
-      GenericUnaryOpStub stub(Token::BIT_NOT, mode);
+      UnaryOpFlags flags = inline_smi_case
+          ? NO_UNARY_SMI_CODE_IN_STUB
+          : NO_UNARY_FLAGS;
+      GenericUnaryOpStub stub(Token::BIT_NOT, mode, flags);
       __ CallStub(&stub);
       __ bind(&done);
       Apply(context_, rax);
@@ -2997,13 +3183,24 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr, Location where) {
     // Use a regular load, not a contextual load, to avoid a reference
     // error.
     __ Call(ic, RelocInfo::CODE_TARGET);
+    __ nop();  // Signal no inlined code.
     if (where == kStack) __ push(rax);
   } else if (proxy != NULL &&
              proxy->var()->slot() != NULL &&
              proxy->var()->slot()->type() == Slot::LOOKUP) {
+    Label done, slow;
+
+    // Generate code for loading from variables potentially shadowed
+    // by eval-introduced variables.
+    Slot* slot = proxy->var()->slot();
+    EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done);
+
+    __ bind(&slow);
     __ push(rsi);
     __ Push(proxy->name());
     __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+    __ bind(&done);
+
     if (where == kStack) __ push(rax);
   } else {
     // This expression cannot throw a reference error at the top level.
@@ -3174,7 +3371,8 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
           UNREACHABLE();
       }
 
-      if (ShouldInlineSmiCase(op)) {
+      bool inline_smi_code = ShouldInlineSmiCase(op);
+      if (inline_smi_code) {
         Label slow_case;
         __ JumpIfNotBothSmi(rax, rdx, &slow_case);
         __ SmiCompare(rdx, rax);
@@ -3182,7 +3380,10 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
         __ bind(&slow_case);
       }
 
-      CompareStub stub(cc, strict);
+      CompareFlags flags = inline_smi_code
+          ? NO_SMI_COMPARE_IN_STUB
+          : NO_COMPARE_FLAGS;
+      CompareStub stub(cc, strict, flags);
       __ CallStub(&stub);
       __ testq(rax, rax);
       Split(cc, if_true, if_false, fall_through);
@@ -3243,7 +3444,7 @@ void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
 
 
 void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
-  __ movq(dst, CodeGenerator::ContextOperand(rsi, context_index));
+  __ movq(dst, ContextOperand(rsi, context_index));
 }
 
 
index f500ce6..765a90c 100644 (file)
@@ -821,6 +821,59 @@ void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) {
 }
 
 
+void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object,
+                                                   JSObject* holder,
+                                                   String* name,
+                                                   Label* miss) {
+  ASSERT(holder->IsGlobalObject());
+
+  // Get the number of arguments.
+  const int argc = arguments().immediate();
+
+  // Get the receiver from the stack.
+  __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
+
+  // If the object is the holder then we know that it's a global
+  // object which can only happen for contextual calls. In this case,
+  // the receiver cannot be a smi.
+  if (object != holder) {
+    __ JumpIfSmi(rdx, miss);
+  }
+
+  // Check that the maps haven't changed.
+  CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, miss);
+}
+
+
+void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell,
+                                                    JSFunction* function,
+                                                    Label* miss) {
+  // Get the value from the cell.
+  __ Move(rdi, Handle<JSGlobalPropertyCell>(cell));
+  __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset));
+
+  // Check that the cell contains the same function.
+  if (Heap::InNewSpace(function)) {
+    // We can't embed a pointer to a function in new space so we have
+    // to verify that the shared function info is unchanged. This has
+    // the nice side effect that multiple closures based on the same
+    // function can all use this call IC. Before we load through the
+    // function, we have to verify that it still is a function.
+    __ JumpIfSmi(rdi, miss);
+    __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
+    __ j(not_equal, miss);
+
+    // Check the shared function info. Make sure it hasn't changed.
+    __ Move(rax, Handle<SharedFunctionInfo>(function->shared()));
+    __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax);
+    __ j(not_equal, miss);
+  } else {
+    __ Cmp(rdi, Handle<JSFunction>(function));
+    __ j(not_equal, miss);
+  }
+}
+
+
 Object* CallStubCompiler::GenerateMissBranch() {
   Object* obj = StubCache::ComputeCallMiss(arguments().immediate(), kind_);
   if (obj->IsFailure()) return obj;
@@ -847,12 +900,10 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
   SharedFunctionInfo* function_info = function->shared();
   if (function_info->HasCustomCallGenerator()) {
     const int id = function_info->custom_call_generator_id();
-    Object* result =
-        CompileCustomCall(id, object, holder, function, name, check);
+    Object* result = CompileCustomCall(
+        id, object, holder,  NULL, function, name);
     // undefined means bail out to regular compiler.
-    if (!result->IsUndefined()) {
-      return result;
-    }
+    if (!result->IsUndefined()) return result;
   }
 
   Label miss_in_smi_check;
@@ -1043,9 +1094,9 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
 
 Object* CallStubCompiler::CompileArrayPushCall(Object* object,
                                                JSObject* holder,
+                                               JSGlobalPropertyCell* cell,
                                                JSFunction* function,
-                                               String* name,
-                                               CheckType check) {
+                                               String* name) {
   // ----------- S t a t e -------------
   //  -- rcx                 : name
   //  -- rsp[0]              : return address
@@ -1053,12 +1104,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
   //  -- ...
   //  -- rsp[(argc + 1) * 8] : receiver
   // -----------------------------------
-  ASSERT(check == RECEIVER_MAP_CHECK);
 
   // If object is not an array, bail out to regular call.
-  if (!object->IsJSArray()) {
-    return Heap::undefined_value();
-  }
+  if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
 
   Label miss;
 
@@ -1204,9 +1252,9 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
 
 Object* CallStubCompiler::CompileArrayPopCall(Object* object,
                                               JSObject* holder,
+                                              JSGlobalPropertyCell* cell,
                                               JSFunction* function,
-                                              String* name,
-                                              CheckType check) {
+                                              String* name) {
   // ----------- S t a t e -------------
   //  -- rcx                 : name
   //  -- rsp[0]              : return address
@@ -1214,12 +1262,9 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
   //  -- ...
   //  -- rsp[(argc + 1) * 8] : receiver
   // -----------------------------------
-  ASSERT(check == RECEIVER_MAP_CHECK);
 
   // If object is not an array, bail out to regular call.
-  if (!object->IsJSArray()) {
-    return Heap::undefined_value();
-  }
+  if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value();
 
   Label miss, return_undefined, call_builtin;
 
@@ -1289,9 +1334,9 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
 
 Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
                                                   JSObject* holder,
+                                                  JSGlobalPropertyCell* cell,
                                                   JSFunction* function,
-                                                  String* name,
-                                                  CheckType check) {
+                                                  String* name) {
   // ----------- S t a t e -------------
   //  -- rcx                 : function name
   //  -- rsp[0]              : return address
@@ -1301,7 +1346,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
   // -----------------------------------
 
   // If object is not a string, bail out to regular call.
-  if (!object->IsString()) return Heap::undefined_value();
+  if (!object->IsString() || cell != NULL) return Heap::undefined_value();
 
   const int argc = arguments().immediate();
 
@@ -1358,11 +1403,12 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
 }
 
 
-Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
-                                                      JSObject* holder,
-                                                      JSFunction* function,
-                                                      String* name,
-                                                      CheckType check) {
+Object* CallStubCompiler::CompileStringCharCodeAtCall(
+    Object* object,
+    JSObject* holder,
+    JSGlobalPropertyCell* cell,
+    JSFunction* function,
+    String* name) {
   // ----------- S t a t e -------------
   //  -- rcx                 : function name
   //  -- rsp[0]              : return address
@@ -1372,7 +1418,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
   // -----------------------------------
 
   // If object is not a string, bail out to regular call.
-  if (!object->IsString()) return Heap::undefined_value();
+  if (!object->IsString() || cell != NULL) return Heap::undefined_value();
 
   const int argc = arguments().immediate();
 
@@ -1426,6 +1472,75 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
 }
 
 
+Object* CallStubCompiler::CompileStringFromCharCodeCall(
+    Object* object,
+    JSObject* holder,
+    JSGlobalPropertyCell* cell,
+    JSFunction* function,
+    String* name) {
+  // ----------- S t a t e -------------
+  //  -- rcx                 : function name
+  //  -- rsp[0]              : return address
+  //  -- rsp[(argc - n) * 8] : arg[n] (zero-based)
+  //  -- ...
+  //  -- rsp[(argc + 1) * 8] : receiver
+  // -----------------------------------
+
+  const int argc = arguments().immediate();
+
+  // If the object is not a JSObject or we got an unexpected number of
+  // arguments, bail out to the regular call.
+  if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
+
+  Label miss;
+  GenerateNameCheck(name, &miss);
+
+  if (cell == NULL) {
+    __ movq(rdx, Operand(rsp, 2 * kPointerSize));
+
+    __ JumpIfSmi(rdx, &miss);
+
+    CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name,
+                    &miss);
+  } else {
+    ASSERT(cell->value() == function);
+    GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+    GenerateLoadFunctionFromCell(cell, function, &miss);
+  }
+
+  // Load the char code argument.
+  Register code = rbx;
+  __ movq(code, Operand(rsp, 1 * kPointerSize));
+
+  // Check the code is a smi.
+  Label slow;
+  __ JumpIfNotSmi(code, &slow);
+
+  // Convert the smi code to uint16.
+  __ SmiAndConstant(code, code, Smi::FromInt(0xffff));
+
+  StringCharFromCodeGenerator char_from_code_generator(code, rax);
+  char_from_code_generator.GenerateFast(masm());
+  __ ret(2 * kPointerSize);
+
+  ICRuntimeCallHelper call_helper;
+  char_from_code_generator.GenerateSlow(masm(), call_helper);
+
+  // Tail call the full function. We do not have to patch the receiver
+  // because the function makes no use of it.
+  __ bind(&slow);
+  __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+  __ bind(&miss);
+  // rcx: function name.
+  Object* obj = GenerateMissBranch();
+  if (obj->IsFailure()) return obj;
+
+  // Return the generated code.
+  return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
+}
+
+
 Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
                                                  JSObject* holder,
                                                  String* name) {
@@ -1498,7 +1613,6 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
                                             JSFunction* function,
                                             String* name) {
   // ----------- S t a t e -------------
-  // -----------------------------------
   // rcx                 : function name
   // rsp[0]              : return address
   // rsp[8]              : argument argc
@@ -1506,6 +1620,17 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // ...
   // rsp[argc * 8]       : argument 1
   // rsp[(argc + 1) * 8] : argument 0 = receiver
+  // -----------------------------------
+
+  SharedFunctionInfo* function_info = function->shared();
+  if (function_info->HasCustomCallGenerator()) {
+    const int id = function_info->custom_call_generator_id();
+    Object* result = CompileCustomCall(
+        id, object, holder, cell, function, name);
+    // undefined means bail out to regular compiler.
+    if (!result->IsUndefined()) return result;
+  }
+
   Label miss;
 
   GenerateNameCheck(name, &miss);
@@ -1513,42 +1638,9 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
   // Get the number of arguments.
   const int argc = arguments().immediate();
 
-  // Get the receiver from the stack.
-  __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
-
-  // If the object is the holder then we know that it's a global
-  // object which can only happen for contextual calls. In this case,
-  // the receiver cannot be a smi.
-  if (object != holder) {
-    __ JumpIfSmi(rdx, &miss);
-  }
-
-  // Check that the maps haven't changed.
-  CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, &miss);
-
-  // Get the value from the cell.
-  __ Move(rdi, Handle<JSGlobalPropertyCell>(cell));
-  __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset));
+  GenerateGlobalReceiverCheck(object, holder, name, &miss);
 
-  // Check that the cell contains the same function.
-  if (Heap::InNewSpace(function)) {
-    // We can't embed a pointer to a function in new space so we have
-    // to verify that the shared function info is unchanged. This has
-    // the nice side effect that multiple closures based on the same
-    // function can all use this call IC. Before we load through the
-    // function, we have to verify that it still is a function.
-    __ JumpIfSmi(rdi, &miss);
-    __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
-    __ j(not_equal, &miss);
-
-    // Check the shared function info. Make sure it hasn't changed.
-    __ Move(rax, Handle<SharedFunctionInfo>(function->shared()));
-    __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax);
-    __ j(not_equal, &miss);
-  } else {
-    __ Cmp(rdi, Handle<JSFunction>(function));
-    __ j(not_equal, &miss);
-  }
+  GenerateLoadFunctionFromCell(cell, function, &miss);
 
   // Patch the receiver on the stack with the global proxy.
   if (object->IsGlobalObject()) {
index 6dc49c0..5e570f3 100644 (file)
@@ -989,4 +989,156 @@ TEST(AggregatedHeapSnapshot) {
   CHECK(IsNodeRetainedAs(a_from_b, 1));  // B has 1 ref to A.
 }
 
+namespace {
+
+class TestJSONStream : public v8::OutputStream {
+ public:
+  TestJSONStream() : eos_signaled_(0), abort_countdown_(-1) {}
+  explicit TestJSONStream(int abort_countdown)
+      : eos_signaled_(0), abort_countdown_(abort_countdown) {}
+  virtual ~TestJSONStream() {}
+  virtual void EndOfStream() { ++eos_signaled_; }
+  virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
+    if (abort_countdown_ > 0) --abort_countdown_;
+    if (abort_countdown_ == 0) return kAbort;
+    CHECK_GT(chars_written, 0);
+    i::Vector<char> chunk = buffer_.AddBlock(chars_written, '\0');
+    memcpy(chunk.start(), buffer, chars_written);
+    return kContinue;
+  }
+  void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
+  int eos_signaled() { return eos_signaled_; }
+  int size() { return buffer_.size(); }
+ private:
+  i::Collector<char> buffer_;
+  int eos_signaled_;
+  int abort_countdown_;
+};
+
+class AsciiResource: public v8::String::ExternalAsciiStringResource {
+ public:
+  explicit AsciiResource(i::Vector<char> string): data_(string.start()) {
+    length_ = string.length();
+  }
+  virtual const char* data() const { return data_; }
+  virtual size_t length() const { return length_; }
+ private:
+  const char* data_;
+  size_t length_;
+};
+
+}  // namespace
+
+TEST(HeapSnapshotJSONSerialization) {
+  v8::HandleScope scope;
+  LocalContext env;
+
+#define STRING_LITERAL_FOR_TEST \
+  "\"String \\n\\r\\u0008\\u0081\\u0101\\u0801\\u8001\""
+  CompileAndRunScript(
+      "function A(s) { this.s = s; }\n"
+      "function B(x) { this.x = x; }\n"
+      "var a = new A(" STRING_LITERAL_FOR_TEST ");\n"
+      "var b = new B(a);");
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8::String::New("json"));
+  TestJSONStream stream;
+  snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
+  CHECK_GT(stream.size(), 0);
+  CHECK_EQ(1, stream.eos_signaled());
+  i::ScopedVector<char> json(stream.size());
+  stream.WriteTo(json);
+
+  // Verify that snapshot string is valid JSON.
+  AsciiResource json_res(json);
+  v8::Local<v8::String> json_string = v8::String::NewExternal(&json_res);
+  env->Global()->Set(v8::String::New("json_snapshot"), json_string);
+  v8::Local<v8::Value> snapshot_parse_result = CompileRun(
+      "var parsed = JSON.parse(json_snapshot); true;");
+  CHECK(!snapshot_parse_result.IsEmpty());
+
+  // Verify that snapshot object has required fields.
+  v8::Local<v8::Object> parsed_snapshot =
+      env->Global()->Get(v8::String::New("parsed"))->ToObject();
+  CHECK(parsed_snapshot->Has(v8::String::New("snapshot")));
+  CHECK(parsed_snapshot->Has(v8::String::New("nodes")));
+  CHECK(parsed_snapshot->Has(v8::String::New("strings")));
+
+  // Verify that nodes meta-info is valid JSON.
+  v8::Local<v8::Value> nodes_meta_parse_result = CompileRun(
+      "var parsed_meta = JSON.parse(parsed.nodes[0]); true;");
+  CHECK(!nodes_meta_parse_result.IsEmpty());
+
+  // Get node and edge "member" offsets.
+  v8::Local<v8::Value> meta_analysis_result = CompileRun(
+      "var children_count_offset ="
+      "    parsed_meta.fields.indexOf('children_count');\n"
+      "var children_offset ="
+      "    parsed_meta.fields.indexOf('children');\n"
+      "var children_meta ="
+      "    parsed_meta.types[children_offset];\n"
+      "var child_fields_count = children_meta.fields.length;\n"
+      "var child_type_offset ="
+      "    children_meta.fields.indexOf('type');\n"
+      "var child_name_offset ="
+      "    children_meta.fields.indexOf('name_or_index');\n"
+      "var child_to_node_offset ="
+      "    children_meta.fields.indexOf('to_node');\n"
+      "var property_type ="
+      "    children_meta.types[child_type_offset].indexOf('property');");
+  CHECK(!meta_analysis_result.IsEmpty());
+
+  // A helper function for processing encoded nodes.
+  CompileRun(
+      "function GetChildPosByProperty(pos, prop_name) {\n"
+      "  var nodes = parsed.nodes;\n"
+      "  var strings = parsed.strings;\n"
+      "  for (var i = 0,\n"
+      "      count = nodes[pos + children_count_offset] * child_fields_count;\n"
+      "      i < count; i += child_fields_count) {\n"
+      "    var child_pos = pos + children_offset + i;\n"
+      "    if (nodes[child_pos + child_type_offset] === property_type\n"
+      "       && strings[nodes[child_pos + child_name_offset]] === prop_name)\n"
+      "        return nodes[child_pos + child_to_node_offset];\n"
+      "  }\n"
+      "  return null;\n"
+      "}\n");
+  // Get the string index using the path: <root> -> <global>.b.x.s
+  v8::Local<v8::Value> string_obj_pos_val = CompileRun(
+      "GetChildPosByProperty(\n"
+      "  GetChildPosByProperty(\n"
+      "    GetChildPosByProperty("
+      "      parsed.nodes[1 + children_offset + child_to_node_offset],\"b\"),\n"
+      "    \"x\"),"
+      "  \"s\")");
+  CHECK(!string_obj_pos_val.IsEmpty());
+  int string_obj_pos =
+      static_cast<int>(string_obj_pos_val->ToNumber()->Value());
+  v8::Local<v8::Object> nodes_array =
+      parsed_snapshot->Get(v8::String::New("nodes"))->ToObject();
+  int string_index = static_cast<int>(
+      nodes_array->Get(string_obj_pos + 1)->ToNumber()->Value());
+  CHECK_GT(string_index, 0);
+  v8::Local<v8::Object> strings_array =
+      parsed_snapshot->Get(v8::String::New("strings"))->ToObject();
+  v8::Local<v8::String> string = strings_array->Get(string_index)->ToString();
+  v8::Local<v8::String> ref_string =
+      CompileRun(STRING_LITERAL_FOR_TEST)->ToString();
+#undef STRING_LITERAL_FOR_TEST
+  CHECK_EQ(*v8::String::Utf8Value(ref_string),
+           *v8::String::Utf8Value(string));
+}
+
+
+TEST(HeapSnapshotJSONSerializationAborting) {
+  v8::HandleScope scope;
+  LocalContext env;
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"));
+  TestJSONStream stream(5);
+  snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON);
+  CHECK_GT(stream.size(), 0);
+  CHECK_EQ(0, stream.eos_signaled());
+}
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
index 2322c54..7276742 100644 (file)
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 var array = [1,2,3,1,2,3,1,2,3,1,2,3];
+var undef_array = [0,,2,undefined,4,,6,undefined,8,,10];
+// Sparse arrays with length 42000.
+var sparse_array = [];
+sparse_array[100] = 3;
+sparse_array[200] = undefined;
+sparse_array[300] = 4;
+sparse_array[400] = 5;
+sparse_array[500] = 6;
+sparse_array[600] = 5;
+sparse_array[700] = 4;
+sparse_array[800] = undefined;
+sparse_array[900] = 3
+sparse_array[41999] = "filler";
+
+var dense_object = { 0: 42, 1: 37, length: 2 };
+var sparse_object = { 0: 42, 100000: 37, length: 200000 };
+var funky_object = { 10:42, 100000: 42, 100001: 37, length: 50000 };
+var infinite_object = { 10: 42, 100000: 37, length: Infinity };
 
 // ----------------------------------------------------------------------
 // Array.prototype.indexOf.
 // ----------------------------------------------------------------------
 
 // Negative cases.
-assertEquals([].indexOf(1), -1);
-assertEquals(array.indexOf(4), -1);
-assertEquals(array.indexOf(3, array.length), -1);
+assertEquals(-1, [].indexOf(1));
+assertEquals(-1, array.indexOf(4));
+assertEquals(-1, array.indexOf(3, array.length));
 
-assertEquals(array.indexOf(3), 2);
+assertEquals(2, array.indexOf(3));
 // Negative index out of range.
-assertEquals(array.indexOf(1, -17), 0);
+assertEquals(0, array.indexOf(1, -17));
 // Negative index in rage.
-assertEquals(array.indexOf(1, -11), 3);
+assertEquals(3, array.indexOf(1, -11));
 // Index in range.
-assertEquals(array.indexOf(1, 1), 3);
-assertEquals(array.indexOf(1, 3), 3);
-assertEquals(array.indexOf(1, 4), 6);
+assertEquals(3, array.indexOf(1, 1));
+assertEquals(3, array.indexOf(1, 3));
+assertEquals(6, array.indexOf(1, 4));
+
+// Find undefined, not holes.
+assertEquals(3, undef_array.indexOf(undefined));
+assertEquals(3, undef_array.indexOf(undefined, 3));
+assertEquals(7, undef_array.indexOf(undefined, 4));
+assertEquals(7, undef_array.indexOf(undefined, 7));
+assertEquals(-1, undef_array.indexOf(undefined, 8));
+assertEquals(3, undef_array.indexOf(undefined, -11));
+assertEquals(3, undef_array.indexOf(undefined, -8));
+assertEquals(7, undef_array.indexOf(undefined, -7));
+assertEquals(7, undef_array.indexOf(undefined, -4));
+assertEquals(-1, undef_array.indexOf(undefined, -3));
+
+// Find in sparse array.
+assertEquals(100, sparse_array.indexOf(3));
+assertEquals(900, sparse_array.indexOf(3, 101));
+assertEquals(-1, sparse_array.indexOf(3, 901));
+assertEquals(100, sparse_array.indexOf(3, -42000));
+assertEquals(900, sparse_array.indexOf(3, 101 - 42000));
+assertEquals(-1, sparse_array.indexOf(3, 901 - 42000));
+
+assertEquals(300, sparse_array.indexOf(4));
+assertEquals(700, sparse_array.indexOf(4, 301));
+assertEquals(-1, sparse_array.indexOf(4, 701));
+assertEquals(300, sparse_array.indexOf(4, -42000));
+assertEquals(700, sparse_array.indexOf(4, 301 - 42000));
+assertEquals(-1, sparse_array.indexOf(4, 701 - 42000));
+
+assertEquals(200, sparse_array.indexOf(undefined));
+assertEquals(800, sparse_array.indexOf(undefined, 201));
+assertEquals(-1, sparse_array.indexOf(undefined, 801));
+assertEquals(200, sparse_array.indexOf(undefined, -42000));
+assertEquals(800, sparse_array.indexOf(undefined, 201 - 42000));
+assertEquals(-1, sparse_array.indexOf(undefined, 801 - 42000));
+
+// Find in non-arrays.
+assertEquals(0, Array.prototype.indexOf.call(dense_object, 42));
+assertEquals(1, Array.prototype.indexOf.call(dense_object, 37));
+assertEquals(-1, Array.prototype.indexOf.call(dense_object, 87));
+
+assertEquals(0, Array.prototype.indexOf.call(sparse_object, 42));
+assertEquals(100000, Array.prototype.indexOf.call(sparse_object, 37));
+assertEquals(-1, Array.prototype.indexOf.call(sparse_object, 87));
+
+assertEquals(10, Array.prototype.indexOf.call(funky_object, 42));
+assertEquals(-1, Array.prototype.indexOf.call(funky_object, 42, 15));
+assertEquals(-1, Array.prototype.indexOf.call(funky_object, 37));
+
+assertEquals(-1, Array.prototype.indexOf.call(infinite_object, 42));
 
 // ----------------------------------------------------------------------
 // Array.prototype.lastIndexOf.
 // ----------------------------------------------------------------------
 
 // Negative cases.
-assertEquals([].lastIndexOf(1), -1);
-assertEquals(array.lastIndexOf(1, -17), -1);
+assertEquals(-1, [].lastIndexOf(1));
+assertEquals(-1, array.lastIndexOf(1, -17));
 
-assertEquals(array.lastIndexOf(1), 9);
+assertEquals(9, array.lastIndexOf(1));
 // Index out of range.
-assertEquals(array.lastIndexOf(1, array.length), 9);
+assertEquals(9, array.lastIndexOf(1, array.length));
 // Index in range.
-assertEquals(array.lastIndexOf(1, 2), 0);
-assertEquals(array.lastIndexOf(1, 4), 3);
-assertEquals(array.lastIndexOf(1, 3), 3);
+assertEquals(0, array.lastIndexOf(1, 2));
+assertEquals(3, array.lastIndexOf(1, 4));
+assertEquals(3, array.lastIndexOf(1, 3));
 // Negative index in range.
-assertEquals(array.lastIndexOf(1, -11), 0);
+assertEquals(0, array.lastIndexOf(1, -11));
+
+// Find undefined, not holes.
+assertEquals(7, undef_array.lastIndexOf(undefined));
+assertEquals(-1, undef_array.lastIndexOf(undefined, 2));
+assertEquals(3, undef_array.lastIndexOf(undefined, 3));
+assertEquals(3, undef_array.lastIndexOf(undefined, 6));
+assertEquals(7, undef_array.lastIndexOf(undefined, 7));
+assertEquals(7, undef_array.lastIndexOf(undefined, -1));
+assertEquals(-1, undef_array.lastIndexOf(undefined, -9));
+assertEquals(3, undef_array.lastIndexOf(undefined, -8));
+assertEquals(3, undef_array.lastIndexOf(undefined, -5));
+assertEquals(7, undef_array.lastIndexOf(undefined, -4));
+
+// Find in sparse array.
+assertEquals(900, sparse_array.lastIndexOf(3));
+assertEquals(100, sparse_array.lastIndexOf(3, 899));
+assertEquals(-1, sparse_array.lastIndexOf(3, 99));
+assertEquals(900, sparse_array.lastIndexOf(3, -1));
+assertEquals(100, sparse_array.lastIndexOf(3, 899 - 42000));
+assertEquals(-1, sparse_array.lastIndexOf(3, 99 - 42000));
+
+assertEquals(700, sparse_array.lastIndexOf(4));
+assertEquals(300, sparse_array.lastIndexOf(4, 699));
+assertEquals(-1, sparse_array.lastIndexOf(4, 299));
+assertEquals(700, sparse_array.lastIndexOf(4, -1));
+assertEquals(300, sparse_array.lastIndexOf(4, 699 - 42000));
+assertEquals(-1, sparse_array.lastIndexOf(4, 299 - 42000));
+
+assertEquals(800, sparse_array.lastIndexOf(undefined));
+assertEquals(200, sparse_array.lastIndexOf(undefined, 799));
+assertEquals(-1, sparse_array.lastIndexOf(undefined, 199));
+assertEquals(800, sparse_array.lastIndexOf(undefined, -1));
+assertEquals(200, sparse_array.lastIndexOf(undefined, 799 - 42000));
+assertEquals(-1, sparse_array.lastIndexOf(undefined, 199 - 42000));
+
+assertEquals(0, Array.prototype.lastIndexOf.call(dense_object, 42));
+assertEquals(1, Array.prototype.lastIndexOf.call(dense_object, 37));
+assertEquals(0, Array.prototype.lastIndexOf.call(sparse_object, 42));
+assertEquals(100000, Array.prototype.lastIndexOf.call(sparse_object, 37));
+
+//Find in non-arrays.
+assertEquals(0, Array.prototype.lastIndexOf.call(dense_object, 42));
+assertEquals(1, Array.prototype.lastIndexOf.call(dense_object, 37));
+assertEquals(-1, Array.prototype.lastIndexOf.call(dense_object, 87));
+
+assertEquals(0, Array.prototype.lastIndexOf.call(sparse_object, 42));
+assertEquals(100000, Array.prototype.lastIndexOf.call(sparse_object, 37));
+assertEquals(-1, Array.prototype.lastIndexOf.call(sparse_object, 87));
+
+assertEquals(10, Array.prototype.lastIndexOf.call(funky_object, 42, 15));
+assertEquals(10, Array.prototype.lastIndexOf.call(funky_object, 42));
+assertEquals(-1, Array.prototype.lastIndexOf.call(funky_object, 37));
 
+assertEquals(-1, Array.prototype.lastIndexOf.call(infinite_object, 42));
diff --git a/deps/v8/test/mjsunit/regress/regress-857.js b/deps/v8/test/mjsunit/regress/regress-857.js
new file mode 100644 (file)
index 0000000..183248d
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Make sure ES5 15.9.1.15 (ISO 8601 / RFC 3339) time zone offsets of
+// the form "+09:00" & "-09:00" get parsed as expected
+assertEquals(1283326536000, Date.parse("2010-08-31T22:35:36-09:00"));
+assertEquals(1283261736000, Date.parse("2010-08-31T22:35:36+09:00"));
+assertEquals(1283326536000, Date.parse("2010-08-31T22:35:36.0-09:00"));
+assertEquals(1283261736000, Date.parse("2010-08-31T22:35:36.0+09:00"));
+// colon-less time expressions in time zone offsets are not conformant
+// with ES5 15.9.1.15 but are nonetheless supported in V8
+assertEquals(1283326536000, Date.parse("2010-08-31T22:35:36-0900"));
+assertEquals(1283261736000, Date.parse("2010-08-31T22:35:36+0900"));
diff --git a/deps/v8/test/mjsunit/string-fromcharcode.js b/deps/v8/test/mjsunit/string-fromcharcode.js
new file mode 100644 (file)
index 0000000..7a2db5f
--- /dev/null
@@ -0,0 +1,89 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test String.fromCharCode.
+
+
+// Test various receivers and arguments passed to String.fromCharCode.
+
+Object.prototype.fromCharCode = function(x) { return this; };
+
+var fcc = String.fromCharCode;
+var fcc2 = fcc;
+
+function constFun(x) { return function(y) { return x; }; }
+
+function test(num) {
+  assertEquals(" ", String.fromCharCode(0x20));
+  assertEquals(" ", String.fromCharCode(0x20 + 0x10000));
+  assertEquals(" ", String.fromCharCode(0x20 - 0x10000));
+  assertEquals(" ", String.fromCharCode(0x20 + 0.5));
+
+  assertEquals("\u1234", String.fromCharCode(0x1234));
+  assertEquals("\u1234", String.fromCharCode(0x1234 + 0x10000));
+  assertEquals("\u1234", String.fromCharCode(0x1234 - 0x10000));
+  assertEquals("\u1234", String.fromCharCode(0x1234 + 0.5));
+
+  assertEquals("  ", String.fromCharCode(0x20, 0x20));
+  assertEquals("  ", String.fromCharCode(0x20 + 0.5, 0x20));
+
+  assertEquals(" ", fcc(0x20));
+  assertEquals(" ", fcc(0x20 + 0x10000));
+  assertEquals(" ", fcc(0x20 - 0x10000));
+  assertEquals(" ", fcc(0x20 + 0.5));
+
+  assertEquals("\u1234", fcc(0x1234));
+  assertEquals("\u1234", fcc(0x1234 + 0x10000));
+  assertEquals("\u1234", fcc(0x1234 - 0x10000));
+  assertEquals("\u1234", fcc(0x1234 + 0.5));
+
+  assertEquals("  ", fcc(0x20, 0x20));
+  assertEquals("  ", fcc(0x20 + 0.5, 0x20));
+
+  var receiver = (num < 5) ? String : (num < 9) ? "dummy" : 42;
+  fcc2 = (num < 5) ? fcc : (num < 9) ? constFun("dummy") : constFun(42);
+  var expected = (num < 5) ? " " : (num < 9) ? "dummy" : 42;
+  assertEquals(expected, receiver.fromCharCode(0x20));
+  assertEquals(expected, receiver.fromCharCode(0x20 - 0x10000));
+  assertEquals(expected, receiver.fromCharCode(0x20 + 0.5));
+  assertEquals(expected, fcc2(0x20));
+  assertEquals(expected, fcc2(0x20 - 0x10000));
+  assertEquals(expected, fcc2(0x20 + 0.5));
+}
+
+// Use loop to test the custom IC.
+for (var i = 0; i < 10; i++) {
+  test(i);
+}
+
+
+// Test the custom IC works correctly when the map changes.
+for (var i = 0; i < 10; i++) {
+  var expected = (i < 5) ? " " : 42;
+  if (i == 5) String.fromCharCode = function() { return 42; };
+  assertEquals(expected, String.fromCharCode(0x20));
+}