Version 1.6.0-dev.9.3

svn merge -c 39268 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 39272 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -c 39260 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@39285 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analyzer/lib/src/generated/java_io.dart b/pkg/analyzer/lib/src/generated/java_io.dart
index 347463b..245bd04 100644
--- a/pkg/analyzer/lib/src/generated/java_io.dart
+++ b/pkg/analyzer/lib/src/generated/java_io.dart
@@ -112,7 +112,10 @@
   bool isDirectory() {
     return _newDirectory().existsSync();
   }
-  Uri toURI() => pathos.toUri(_path);
+  Uri toURI() {
+    String path = getAbsolutePath();
+    return pathos.toUri(path);
+  }
   String readAsStringSync() => _newFile().readAsStringSync();
   int lastModified() {
     if (!_newFile().existsSync()) return 0;
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 554ec4b..4e03306 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: analyzer
-version: 0.22.0+1
+version: 0.22.0+2
 author: Dart Team <misc@dartlang.org>
 description: Static analyzer for Dart.
 homepage: http://www.dartlang.org
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index f54e1e5..77f7694 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -8328,6 +8328,44 @@
       EXPECT_EQ(0x4e8c, ext_utf16_str[i]);
     }
 
+    // Test with a symbol (hash value should be preserved on externalization).
+    const char* symbol_ascii = "string";
+    expected_length = strlen(symbol_ascii);
+    Dart_Handle symbol_str =
+        Api::NewHandle(isolate, Symbols::New(symbol_ascii, expected_length));
+    EXPECT_VALID(symbol_str);
+    EXPECT(Dart_IsString(symbol_str));
+    EXPECT(Dart_IsStringLatin1(symbol_str));
+    EXPECT(!Dart_IsExternalString(symbol_str));
+    EXPECT_VALID(Dart_StringLength(symbol_str, &length));
+    EXPECT_EQ(expected_length, length);
+    EXPECT(Api::UnwrapStringHandle(isolate, symbol_str).HasHash());
+
+    uint8_t ext_symbol_ascii[kLength];
+    EXPECT_VALID(Dart_StringStorageSize(symbol_str, &size));
+    str = Dart_MakeExternalString(symbol_str,
+                                  ext_symbol_ascii,
+                                  size,
+                                  &peer8,
+                                  MakeExternalCback);
+    EXPECT(Api::UnwrapStringHandle(isolate, str).HasHash());
+    EXPECT(Api::UnwrapStringHandle(isolate, str).Hash() ==
+           Api::UnwrapStringHandle(isolate, symbol_str).Hash());
+    EXPECT(Dart_IsString(str));
+    EXPECT(Dart_IsString(symbol_str));
+    EXPECT(Dart_IsStringLatin1(str));
+    EXPECT(Dart_IsStringLatin1(symbol_str));
+    EXPECT(Dart_IsExternalString(str));
+    EXPECT(Dart_IsExternalString(symbol_str));
+    EXPECT_VALID(Dart_StringLength(str, &length));
+    EXPECT_EQ(expected_length, length);
+    EXPECT_VALID(Dart_StringLength(symbol_str, &length));
+    EXPECT_EQ(expected_length, length);
+    EXPECT(Dart_IdentityEquals(str, symbol_str));
+    for (intptr_t i = 0; i < length; i++) {
+      EXPECT_EQ(symbol_ascii[i], ext_symbol_ascii[i]);
+    }
+
     Dart_ExitScope();
   }
   EXPECT_EQ(40, peer8);
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 09058ab..eba4e8c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1890,18 +1890,62 @@
 }
 
 
+class FunctionName {
+ public:
+  FunctionName(const String& name, String* tmp_string)
+      : name_(name), tmp_string_(tmp_string) {}
+  bool Matches(const Function& function) const {
+    if (name_.IsSymbol()) {
+      return name_.raw() == function.name();
+    } else {
+      *tmp_string_ = function.name();
+      return name_.Equals(*tmp_string_);
+    }
+  }
+  intptr_t Hash() const { return name_.Hash(); }
+ private:
+  const String& name_;
+  String* tmp_string_;
+};
+
+
+// Traits for looking up Functions by name.
+class ClassFunctionsTraits {
+ public:
+  // Called when growing the table.
+  static bool IsMatch(const Object& a, const Object& b) {
+    ASSERT(a.IsFunction() && b.IsFunction());
+    // Function objects are always canonical.
+    return a.raw() == b.raw();
+  }
+  static bool IsMatch(const FunctionName& name, const Object& obj) {
+    return name.Matches(Function::Cast(obj));
+  }
+  static uword Hash(const Object& key) {
+    return String::HashRawSymbol(Function::Cast(key).name());
+  }
+  static uword Hash(const FunctionName& name) {
+    return name.Hash();
+  }
+};
+typedef UnorderedHashSet<ClassFunctionsTraits> ClassFunctionsSet;
+
+
 void Class::SetFunctions(const Array& value) const {
   ASSERT(!value.IsNull());
-#if defined(DEBUG)
-  // Verify that all the functions in the array have this class as owner.
-  Function& func = Function::Handle();
-  intptr_t len = value.Length();
-  for (intptr_t i = 0; i < len; i++) {
-    func ^= value.At(i);
-    ASSERT(func.Owner() == raw());
-  }
-#endif
   StorePointer(&raw_ptr()->functions_, value.raw());
+  const intptr_t len = value.Length();
+  ClassFunctionsSet set(HashTables::New<ClassFunctionsSet>(len));
+  if (len >= kFunctionLookupHashTreshold) {
+    Function& func = Function::Handle();
+    for (intptr_t i = 0; i < len; ++i) {
+      func ^= value.At(i);
+      // Verify that all the functions in the array have this class as owner.
+      ASSERT(func.Owner() == raw());
+      set.Insert(func);
+    }
+  }
+  StorePointer(&raw_ptr()->functions_hash_table_, set.Release().raw());
 }
 
 
@@ -1909,7 +1953,17 @@
   const Array& arr = Array::Handle(functions());
   const Array& new_arr = Array::Handle(Array::Grow(arr, arr.Length() + 1));
   new_arr.SetAt(arr.Length(), function);
-  SetFunctions(new_arr);
+  StorePointer(&raw_ptr()->functions_, new_arr.raw());
+  // Add to hash table, if any.
+  const intptr_t new_len = new_arr.Length();
+  if (new_len == kFunctionLookupHashTreshold) {
+    // Transition to using hash table.
+    SetFunctions(new_arr);
+  } else if (new_len > kFunctionLookupHashTreshold) {
+    ClassFunctionsSet set(raw_ptr()->functions_hash_table_);
+    set.Insert(function);
+    StorePointer(&raw_ptr()->functions_hash_table_, set.Release().raw());
+  }
 }
 
 
@@ -3831,6 +3885,15 @@
   ASSERT(!funcs.IsNull());
   const intptr_t len = funcs.Length();
   Function& function = isolate->FunctionHandle();
+  if (len >= kFunctionLookupHashTreshold) {
+    ClassFunctionsSet set(raw_ptr()->functions_hash_table_);
+    REUSABLE_STRING_HANDLESCOPE(isolate);
+    function ^= set.GetOrNull(FunctionName(name, &(isolate->StringHandle())));
+    // No mutations.
+    ASSERT(set.Release().raw() == raw_ptr()->functions_hash_table_);
+    return function.IsNull() ? Function::null()
+                             : CheckFunctionType(function, kind);
+  }
   if (name.IsSymbol()) {
     // Quick Symbol compare.
     NoGCScope no_gc;
@@ -17053,10 +17116,12 @@
       tags = RawObject::ClassIdTag::update(class_id, tags);
       raw_ptr()->tags_ = tags;
       result = this->raw();
+      const uint8_t* ext_array = reinterpret_cast<const uint8_t*>(array);
       ExternalStringData<uint8_t>* ext_data = new ExternalStringData<uint8_t>(
-          reinterpret_cast<const uint8_t*>(array), peer, cback);
-      result.SetLength(str_length);
-      result.SetHash(0);
+          ext_array, peer, cback);
+      ASSERT(result.Length() == str_length);
+      ASSERT(!result.HasHash() ||
+             (result.Hash() == String::Hash(ext_array, str_length)));
       ExternalOneByteString::SetExternalData(result, ext_data);
       external_data = ext_data;
       finalizer = ExternalOneByteString::Finalize;
@@ -17079,10 +17144,12 @@
       tags = RawObject::ClassIdTag::update(class_id, tags);
       raw_ptr()->tags_ = tags;
       result = this->raw();
+      const uint16_t* ext_array = reinterpret_cast<const uint16_t*>(array);
       ExternalStringData<uint16_t>* ext_data = new ExternalStringData<uint16_t>(
-          reinterpret_cast<const uint16_t*>(array), peer, cback);
-      result.SetLength(str_length);
-      result.SetHash(0);
+          ext_array, peer, cback);
+      ASSERT(result.Length() == str_length);
+      ASSERT(!result.HasHash() ||
+             (result.Hash() == String::Hash(ext_array, str_length)));
       ExternalTwoByteString::SetExternalData(result, ext_data);
       external_data = ext_data;
       finalizer = ExternalTwoByteString::Finalize;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 892060d..cede5f6 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -949,6 +949,7 @@
   // Returns true if non-static fields are defined.
   bool HasInstanceFields() const;
 
+  // TODO(koda): Unite w/ hash table.
   RawArray* functions() const { return raw_ptr()->functions_; }
   void SetFunctions(const Array& value) const;
   void AddFunction(const Function& function) const;
@@ -1211,6 +1212,9 @@
 
   void CalculateFieldOffsets() const;
 
+  // functions_hash_table is in use iff there are at least this many functions.
+  static const intptr_t kFunctionLookupHashTreshold = 16;
+
   // Initial value for the cached number of type arguments.
   static const intptr_t kUnknownNumTypeArguments = -1;
 
@@ -5389,6 +5393,10 @@
     this->SetHash(result);
     return result;
   }
+  bool HasHash() const {
+    ASSERT(Smi::New(0) == NULL);
+    return (raw_ptr()->hash_ != NULL);
+  }
 
   static intptr_t hash_offset() { return OFFSET_OF(RawString, hash_); }
   static intptr_t Hash(const String& str, intptr_t begin_index, intptr_t len);
@@ -5602,11 +5610,6 @@
   bool Equals(const uint8_t* characters, intptr_t len) const;
   static intptr_t Hash(const uint8_t* characters, intptr_t len);
 
-  bool HasHash() const {
-    ASSERT(Smi::New(0) == NULL);
-    return (raw_ptr()->hash_ != NULL);
-  }
-
   void SetLength(intptr_t value) const {
     // This is only safe because we create a new Smi, which does not cause
     // heap allocation.
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index bff2eff..86db68a 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -499,6 +499,7 @@
   RawString* name_;
   RawString* user_name_;
   RawArray* functions_;
+  RawArray* functions_hash_table_;
   RawArray* fields_;
   RawArray* offset_in_words_to_field_;
   RawGrowableObjectArray* closure_functions_;  // Local functions and literals.
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index 401a5c2..0ccf8a0 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -333,6 +333,7 @@
     isolate->object_store()->set_symbol_table(table.Release());
   }
   ASSERT(symbol.IsSymbol());
+  ASSERT(symbol.HasHash());
   return symbol.raw();
 }
 
diff --git a/tests/standalone/issue14236_test.dart b/tests/standalone/issue14236_test.dart
index f720a65..7e248f3 100644
--- a/tests/standalone/issue14236_test.dart
+++ b/tests/standalone/issue14236_test.dart
Binary files differ
diff --git a/tools/VERSION b/tools/VERSION
index 938a641..4ca5a22 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 6
 PATCH 0
 PRERELEASE 9
-PRERELEASE_PATCH 2
+PRERELEASE_PATCH 3