Index: runtime/vm/object.cc |
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc |
index 98d7b238584f2c48a85d7ce384475d7cf4e99e58..349dc25956fd78659aee6aae97efd35cecc3c34e 100644 |
--- a/runtime/vm/object.cc |
+++ b/runtime/vm/object.cc |
@@ -703,6 +703,10 @@ void Object::InitOnce(Isolate* isolate) { |
isolate->object_store()->set_bigint_class(cls); |
cls = Class::New<Double>(); |
isolate->object_store()->set_double_class(cls); |
+ cls = Class::New<Fraction>(); |
+ // TODO(regis): Remove once native getters are implemented for _Fraction. |
+ cls.SetRefinalizeAfterPatch(); // Fields are defined both in C++ and in Dart. |
+ isolate->object_store()->set_fraction_class(cls); |
// Ensure that class kExternalTypedDataUint8ArrayCid is registered as we |
// need it when reading in the token stream of bootstrap classes in the VM |
@@ -1378,6 +1382,13 @@ NOT_IN_PRODUCT( |
RegisterPrivateClass(cls, Symbols::_Double(), core_lib); |
pending_classes.Add(cls); |
+ cls = Class::New<Fraction>(); |
+ object_store->set_fraction_class(cls); |
+ // TODO(regis): Remove once native getters are implemented for _Fraction. |
+ cls.SetRefinalizeAfterPatch(); // Fields are defined both in C++ and in Dart. |
+ RegisterPrivateClass(cls, Symbols::_Fraction(), core_lib); |
+ pending_classes.Add(cls); |
+ |
// Class that represents the Dart class _Closure and C++ class Closure. |
cls = Class::New<Closure>(); |
cls.set_type_arguments_field_offset(Closure::type_arguments_offset()); |
@@ -1698,6 +1709,7 @@ NOT_IN_PRODUCT( |
#undef REGISTER_EXT_TYPED_DATA_CLASS |
cls = Class::New<Instance>(kByteBufferCid); |
+ object_store->set_byte_buffer_class(cls); |
cls = Class::New<Integer>(); |
object_store->set_integer_implementation_class(cls); |
@@ -1711,6 +1723,11 @@ NOT_IN_PRODUCT( |
cls = Class::New<Double>(); |
object_store->set_double_class(cls); |
+ cls = Class::New<Fraction>(); |
+ // TODO(regis): Remove once native getters are implemented for _Fraction. |
+ cls.SetRefinalizeAfterPatch(); // Fields are defined both in C++ and in Dart. |
+ object_store->set_fraction_class(cls); |
+ |
cls = Class::New<Closure>(); |
object_store->set_closure_class(cls); |
@@ -3572,7 +3589,7 @@ void Class::set_is_finalized() const { |
void Class::SetRefinalizeAfterPatch() const { |
- ASSERT(!IsTopLevel()); |
+ ASSERT((raw_ptr()->name_ == String::null()) || !IsTopLevel()); |
set_state_bits(ClassFinalizedBits::update(RawClass::kRefinalizeAfterPatch, |
raw_ptr()->state_bits_)); |
set_state_bits(TypeFinalizedBit::update(false, raw_ptr()->state_bits_)); |
@@ -4338,6 +4355,30 @@ RawBigint* Class::LookupCanonicalBigint(Zone* zone, |
} |
+RawFraction* Class::LookupCanonicalFraction(Zone* zone, |
+ const Fraction& value, |
+ intptr_t* index) const { |
+ ASSERT(this->raw() == Isolate::Current()->object_store()->fraction_class()); |
+ const Array& constants = Array::Handle(zone, this->constants()); |
+ const intptr_t constants_len = constants.Length(); |
+ // Linear search to see whether this value is already present in the |
+ // list of canonicalized constants. |
+ Fraction& canonical_value = Fraction::Handle(zone); |
+ while (*index < constants_len) { |
+ canonical_value ^= constants.At(*index); |
+ if (canonical_value.IsNull()) { |
+ break; |
+ } |
+ if (canonical_value.CanonicalizeEquals(value)) { |
+ ASSERT(canonical_value.IsCanonical()); |
+ return canonical_value.raw(); |
+ } |
+ *index = *index + 1; |
+ } |
+ return Fraction::null(); |
+} |
+ |
+ |
class CanonicalInstanceKey { |
public: |
explicit CanonicalInstanceKey(const Instance& key) : key_(key) { |
@@ -8017,6 +8058,9 @@ RawLiteralToken* LiteralToken::New(Token::Kind kind, const String& literal) { |
const Integer& value = Integer::Handle(Integer::NewCanonical(literal)); |
ASSERT(value.IsSmi() || value.IsOld()); |
result.set_value(value); |
+ } else if (kind == Token::kRATIONAL) { |
+ const Fraction& value = Fraction::Handle(Fraction::NewCanonical(literal)); |
+ result.set_value(value); |
} else if (kind == Token::kDOUBLE) { |
const Double& value = Double::Handle(Double::NewCanonical(literal)); |
result.set_value(value); |
@@ -18061,6 +18105,9 @@ RawInstance* Number::CheckAndCanonicalize(Thread* thread, |
return result.raw(); |
} |
} |
+ case kFractionCid: { |
+ return Fraction::NewCanonical(Fraction::Cast(*this)); |
+ } |
default: |
UNREACHABLE(); |
} |
@@ -18959,6 +19006,7 @@ RawBigint* Bigint::NewCanonical(const String& str) { |
return canonical_value.raw(); |
} |
} |
+ ASSERT(value.IsOld()); |
value.SetCanonical(); |
// The value needs to be added to the constants list. Grow the list if |
// it is full. |
@@ -19451,6 +19499,170 @@ const char* Bigint::ToCString() const { |
} |
+bool Fraction::CanonicalizeEquals(const Instance& other) const { |
+ if (this->raw() == other.raw()) { |
+ return true; // "===". |
+ } |
+ if (other.IsNull() || !other.IsFraction()) { |
+ return false; |
+ } |
+ return (numerator() == Fraction::Cast(other).numerator()) && |
+ (denominator() == Fraction::Cast(other).denominator()); |
+} |
+ |
+ |
+RawFraction* Fraction::New(Heap::Space space) { |
+ return Fraction::New(0, 0, space); |
+} |
+ |
+ |
+RawFraction* Fraction::New(uint64_t numerator, |
+ uint64_t denominator, |
+ Heap::Space space) { |
+ Thread* thread = Thread::Current(); |
+ Zone* zone = thread->zone(); |
+ Isolate* isolate = thread->isolate(); |
+ ASSERT(isolate->object_store()->fraction_class() != Class::null()); |
+ Fraction& result = Fraction::Handle(zone); |
+ { |
+ RawObject* raw = Object::Allocate(Fraction::kClassId, |
+ Fraction::InstanceSize(), |
+ space); |
+ NoSafepointScope no_safepoint; |
+ result ^= raw; |
+ } |
+ result.set_numerator(Integer::Handle( |
+ Integer::NewFromUint64(numerator, space))); |
+ result.set_denominator(Integer::Handle( |
+ Integer::NewFromUint64(denominator, space))); |
+ return result.raw(); |
+} |
+ |
+ |
+void Fraction::set_numerator(const Integer& value) const { |
+ StorePointer(&raw_ptr()->numerator_, value.raw()); |
+} |
+ |
+ |
+static bool CStringToFraction(const char* str, |
+ intptr_t length, |
+ uint64_t* numerator, |
+ uint64_t* denominator) { |
+ ASSERT(str != NULL); |
+ ASSERT(length > 0); |
+ const uint64_t max_accum = (1ULL << 63) / 10; |
+ uint64_t num = 0ULL; |
+ uint64_t denom = 1ULL; |
+ bool seen_decimal_point = false; |
+ for (intptr_t i = 0; i < length; i++) { |
+ char c = str[i]; |
+ if (!seen_decimal_point && (c == '.')) { |
+ seen_decimal_point = true; |
+ } else if (('0' <= c) && (c <= '9')) { |
+ // Bail if numerator or denominator get close to Bigint. |
+ if ((num > max_accum) || (denom > max_accum)) { |
+ return false; |
+ } |
+ num = num*10 + (c - '0'); |
+ if (seen_decimal_point) denom *= 10; |
+ } else { |
+ if ((str[i] != 'r') && (str[i] != 'R')) { |
+ return false; |
+ } |
+ break; |
+ } |
+ } |
+ // Reduce somewhat. Do not reduce if denominator is 100. |
+ while ((denom != 100ULL) && ((num & 1) == 0) && ((denom & 1) == 0)) { |
+ num >>= 1; |
+ denom >>= 1; |
+ } |
+ // TODO(regis): Reduce further using gcd. |
+ *numerator = num; |
+ *denominator = denom; |
+ return true; |
+} |
+ |
+ |
+RawFraction* Fraction::NewCanonical(const String& str) { |
+ uint64_t nominator; |
+ uint64_t denominator; |
+ if (!CStringToFraction(str.ToCString(), str.Length(), |
+ &nominator, &denominator)) { |
+ // Call the Dart version, which supports Bigint. |
+ // Not supported when called early from core lib itself. |
+ const Library& core_lib = Library::Handle(Library::CoreLibrary()); |
+ ASSERT(!core_lib.IsNull()); |
+ const String& fraction_class_name = Symbols::_Fraction(); |
+ const String& constructor_name = Symbols::DotFromRationalLiteral(); |
+ const Array& constructor_args = Array::Handle(Array::New(1)); |
+ constructor_args.SetAt(0, str); |
+ Object& value = Object::Handle( |
+ DartLibraryCalls::InstanceCreate(core_lib, |
+ fraction_class_name, |
+ constructor_name, |
+ true, // A factory. |
+ constructor_args)); |
+ ASSERT(!value.IsError()); |
+ return Fraction::NewCanonical(Fraction::Cast(value)); |
+ } |
+ return Fraction::NewCanonical(Fraction::Handle( |
+ Fraction::New(nominator, denominator, Heap::kOld))); |
+} |
+ |
+ |
+RawFraction* Fraction::NewCanonical(const Fraction& value) { |
+ // TODO(regis): Canonicalize fields. |
+ Thread* thread = Thread::Current(); |
+ Zone* zone = thread->zone(); |
+ Isolate* isolate = thread->isolate(); |
+ const Class& cls = |
+ Class::Handle(zone, isolate->object_store()->fraction_class()); |
+ intptr_t index = 0; |
+ Fraction& canonical_value = Fraction::Handle(zone); |
+ canonical_value ^= cls.LookupCanonicalFraction(zone, value, &index); |
+ if (!canonical_value.IsNull()) { |
+ return canonical_value.raw(); |
+ } |
+ { |
+ SafepointMutexLocker ml(isolate->constant_canonicalization_mutex()); |
+ // Retry lookup. |
+ { |
+ canonical_value ^= cls.LookupCanonicalFraction(zone, value, &index); |
+ if (!canonical_value.IsNull()) { |
+ return canonical_value.raw(); |
+ } |
+ } |
+ Fraction& result = Fraction::Handle(zone, value.raw()); |
+ if (result.IsNew()) { |
+ // Create a canonical object in old space. |
+ result ^= Object::Clone(result, Heap::kOld); |
+ } |
+ ASSERT(result.IsOld()); |
+ result.SetCanonical(); |
+ // The value needs to be added to the constants list. Grow the list if |
+ // it is full. |
+ cls.InsertCanonicalNumber(zone, index, result); |
+ return result.raw(); |
+ } |
+} |
+ |
+ |
+void Fraction::set_denominator(const Integer& value) const { |
+ StorePointer(&raw_ptr()->denominator_, value.raw()); |
+} |
+ |
+ |
+const char* Fraction::ToCString() const { |
+ if (IsNull()) { |
+ return "_Fraction NULL"; |
+ } |
+ return OS::SCreate(Thread::Current()->zone(), "_Fraction %s/%s", |
+ Integer::Handle(numerator()).ToCString(), |
+ Integer::Handle(denominator()).ToCString()); |
+} |
+ |
+ |
// Synchronize with implementation in compiler (intrinsifier). |
class StringHasher : ValueObject { |
public: |