Chromium Code Reviews| Index: runtime/vm/precompiler.cc |
| diff --git a/runtime/vm/precompiler.cc b/runtime/vm/precompiler.cc |
| index 048d27071f5e5f67b4984afb471e5495d8a84c10..01bdfdb2494a9f50941c434321d0ce29f18e2d35 100644 |
| --- a/runtime/vm/precompiler.cc |
| +++ b/runtime/vm/precompiler.cc |
| @@ -4,158 +4,590 @@ |
| #include "vm/precompiler.h" |
| +#include "vm/compiler.h" |
| #include "vm/isolate.h" |
| +#include "vm/longjump.h" |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| -#include "vm/compiler.h" |
| -#include "vm/longjump.h" |
| +#include "vm/resolver.h" |
| +#include "vm/symbols.h" |
| namespace dart { |
| +#define I (isolate()) |
| +#define Z (zone()) |
| + |
| + |
| DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler."); |
| -static RawError* CompileFunction(const Function& func) { |
| - Thread* thread = Thread::Current(); |
| - Error& error = Error::Handle(); |
| - if (func.is_abstract() || func.IsRedirectingFactory()) { |
| +static void Jump(const Error& error) { |
| + Isolate::Current()->long_jump_base()->Jump(1, error); |
| +} |
| + |
| + |
| +RawError* Precompiler::CompileAll() { |
| + LongJumpScope jump; |
| + if (setjmp(*jump.Set()) == 0) { |
| + Precompiler precompiler(Thread::Current()); |
| + precompiler.DoCompileAll(); |
| + return Error::null(); |
| + } else { |
| + Isolate* isolate = Isolate::Current(); |
| + const Error& error = Error::Handle(isolate->object_store()->sticky_error()); |
| + isolate->object_store()->clear_sticky_error(); |
| return error.raw(); |
| } |
| +} |
| - if (!func.HasCode()) { |
| - if (FLAG_trace_precompiler) { |
| - OS::Print(" Precompiling %s (%" Pd ")\n", |
| - func.ToQualifiedCString(), |
| - func.token_pos()); |
| - } |
| - error = Compiler::CompileFunction(thread, func); |
| - if (!error.IsNull()) { |
| - return error.raw(); |
| - } |
| + |
| +Precompiler::Precompiler(Thread* thread) : |
| + thread_(thread), |
| + zone_(thread->zone()), |
| + isolate_(thread->isolate()), |
| + changed_(false), |
| + function_count_(0), |
| + class_count_(0), |
| + libraries_(GrowableObjectArray::Handle(Z, I->object_store()->libraries())), |
| + pending_functions_(GrowableObjectArray::Handle(Z, |
| + GrowableObjectArray::New())), |
| + collected_closures_(GrowableObjectArray::Handle(Z, I->collected_closures())), |
| + sent_selectors_(GrowableObjectArray::Handle(Z, GrowableObjectArray::New())), |
| + error_(Error::Handle(Z)) { |
| +} |
| + |
| + |
| +void Precompiler::DoCompileAll() { |
| + // Drop all existing code so we can use the presence of code as an indicator |
| + // that we have already looked for the function's callees. |
| + ClearAllCode(); |
| + |
| + // Start with the allocations and invocations that happen from C++. |
| + AddRoots(); |
| + |
| + // TODO(rmacnak): Eagerly add field-invocation functions to all signature |
| + // classes so closure calls don't go through the runtime. |
| + |
| + // Compile newly found targets and add their callees until we reach a fixed |
| + // point. |
| + Iterate(); |
| + |
| + CleanUp(); |
| + |
| + if (FLAG_trace_precompiler) { |
| + OS::Print("Precompiled %" Pd " functions, %" Pd " dynamic types," |
| + " %" Pd " dynamic selectors\n", |
| + function_count_, |
| + class_count_, |
| + sent_selectors_.Length()); |
| } |
| - if ((func.kind() == RawFunction::kRegularFunction) && !func.is_static()) { |
| - const String& name = String::Handle(func.name()); |
| - if (!Field::IsGetterName(name) && !Field::IsSetterName(name)) { |
| - const Function& closure_func = |
| - Function::Handle(func.ImplicitClosureFunction()); |
| - ASSERT(!closure_func.IsNull()); |
| - error = CompileFunction(closure_func); |
| + I->set_compilation_allowed(false); |
| +} |
| + |
| + |
| +void Precompiler::ClearAllCode() { |
| + Library& lib = Library::Handle(Z); |
| + Class& cls = Class::Handle(Z); |
| + Array& functions = Array::Handle(Z); |
| + Function& function = Function::Handle(Z); |
| + |
| + for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| + lib ^= libraries_.At(i); |
| + ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| + while (it.HasNext()) { |
| + cls = it.GetNextClass(); |
| + error_ = cls.EnsureIsFinalized(I); |
| + if (!error_.IsNull()) { |
| + Jump(error_); |
| + } |
| } |
| } |
| - return error.raw(); |
| + for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| + lib ^= libraries_.At(i); |
| + ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| + while (it.HasNext()) { |
| + cls = it.GetNextClass(); |
| + functions = cls.functions(); |
| + for (intptr_t i = 0; i < functions.Length(); i++) { |
| + function ^= functions.At(i); |
| + function.ClearCode(); |
| + } |
| + } |
| + } |
| } |
| -static RawError* CompileClass(const Class& cls) { |
| - if (FLAG_trace_precompiler) { |
| - OS::Print(" Precompiling %s\n", cls.ToCString()); |
| - } |
| +void Precompiler::AddRoots() { |
| + // Note that <rootlibrary>.main is not a root. The appropriate main will be |
| + // discovered through _getMainClosure. |
| - Error& error = Error::Handle(); |
| - error = cls.EnsureIsFinalized(Isolate::Current()); |
| - if (!error.IsNull()) { |
| - return error.raw(); |
| + AddSelector(Symbols::NoSuchMethod()); |
| + |
| + AddSelector(Symbols::Call()); // For speed, not correctness. |
| + |
| + // Allocated from C++. |
| + static const intptr_t kExternallyAllocatedCids[] = { |
| + kBoolCid, |
| + kNullCid, |
| + |
| + kSmiCid, |
| + kMintCid, |
| + kBigintCid, |
| + kDoubleCid, |
| + |
| + kOneByteStringCid, |
| + kTwoByteStringCid, |
| + kExternalOneByteStringCid, |
| + kExternalTwoByteStringCid, |
| + |
| + kArrayCid, |
| + kImmutableArrayCid, |
| + kGrowableObjectArrayCid, |
| + kLinkedHashMapCid, |
| + |
| + kTypedDataUint8ClampedArrayCid, |
| + kTypedDataUint8ArrayCid, |
| + kTypedDataUint16ArrayCid, |
| + kTypedDataUint32ArrayCid, |
| + kTypedDataUint64ArrayCid, |
| + |
| + kTypedDataInt8ArrayCid, |
| + kTypedDataInt16ArrayCid, |
| + kTypedDataInt32ArrayCid, |
| + kTypedDataInt64ArrayCid, |
| + |
| + kExternalTypedDataUint8ArrayCid, |
| + |
| + kTypedDataFloat32ArrayCid, |
| + kTypedDataFloat64ArrayCid, |
| + |
| + kTypedDataFloat32x4ArrayCid, |
| + kTypedDataInt32x4ArrayCid, |
| + kTypedDataFloat64x2ArrayCid, |
| + |
| + kInt32x4Cid, |
| + kFloat32x4Cid, |
| + kFloat64x2Cid, |
| + |
| + kTypeCid, |
| + kTypeRefCid, |
| + kTypeParameterCid, |
| + kBoundedTypeCid, |
| + kLibraryPrefixCid, |
| + |
| + kJSRegExpCid, |
| + kUserTagCid, |
| + kStacktraceCid, |
| + kWeakPropertyCid, |
| + kCapabilityCid, |
| + ReceivePort::kClassId, |
| + SendPort::kClassId, |
| + |
| + kIllegalCid |
| + }; |
| + |
| + Class& cls = Class::Handle(Z); |
| + for (intptr_t i = 0; kExternallyAllocatedCids[i] != kIllegalCid; i++) { |
| + cls = isolate()->class_table()->At(kExternallyAllocatedCids[i]); |
| + AddClass(cls); |
| } |
| - const Array& fields = Array::Handle(cls.fields()); |
| - Field& field = Field::Handle(); |
| - for (intptr_t i = 0; i < fields.Length(); i++) { |
| - field ^= fields.At(i); |
| - ASSERT(!field.IsNull()); |
| - if (field.is_static() && field.has_initializer()) { |
| + static const struct { |
| + const char* library_; |
| + const char* class_; |
| + const char* function_; |
| + } kExternallyCalled[] = { |
| + { "dart:_builtin", "::", "_getMainClosure" }, |
| + { "dart:_builtin", "::", "_getPrintClosure" }, |
| + { "dart:_builtin", "::", "_getUriBaseClosure" }, |
| + { "dart:_builtin", "::", "_resolveUri" }, |
| + { "dart:_builtin", "::", "_setWorkingDirectory" }, |
| + { "dart:async", "::", "_setScheduleImmediateClosure" }, |
| + { "dart:core", "_InternalError", "_InternalError." }, |
| + { "dart:core", "_InvocationMirror", "_allocateInvocationMirror" }, |
| + { "dart:io", "::", "_makeUint8ListView" }, |
| + { "dart:io", "::", "_makeDatagram" }, |
| + { "dart:io", "CertificateException", "CertificateException." }, |
| + { "dart:io", "HandshakeException", "HandshakeException." }, |
| + { "dart:io", "TlsException", "TlsException." }, |
| + { "dart:io", "X509Certificate", "X509Certificate." }, |
| + { "dart:io", "_ExternalBuffer", "set:data" }, |
| + { "dart:io", "_Platform", "set:_nativeScript" }, |
| + { "dart:io", "_ProcessStartStatus", "set:_errorCode" }, |
| + { "dart:io", "_ProcessStartStatus", "set:_errorMessage" }, |
| + { "dart:io", "_SecureFilterImpl", "get:ENCRYPTED_SIZE" }, |
| + { "dart:io", "_SecureFilterImpl", "get:SIZE" }, |
| + { "dart:isolate", "::", "_getIsolateScheduleImmediateClosure" }, |
| + { "dart:isolate", "::", "_startMainIsolate" }, |
| + { "dart:isolate", "_RawReceivePortImpl", "_handleMessage" }, |
| + { "dart:isolate", "_RawReceivePortImpl", "_lookupHandler" }, |
| + { "dart:vmservice", "::", "_registerIsolate" }, |
| + { "dart:vmservice", "::", "boot" }, |
| + { "dart:vmservice_io", "::", "_addResource" }, |
| + { "dart:vmservice_io", "::", "main" }, |
| + |
| + // Cf. Exceptions::Create |
| + { "dart:core", "RangeError", "RangeError." }, |
| + { "dart:core", "RangeError", "RangeError.range" }, |
| + { "dart:core", "ArgumentError", "ArgumentError." }, |
| + { "dart:core", "NoSuchMethodError", "NoSuchMethodError._withType" }, |
| + { "dart:core", "FormatException", "FormatException." }, |
| + { "dart:core", "UnsupportedError", "UnsupportedError." }, |
| + { "dart:core", "NullThrownError", "NullThrownError." }, |
| + { "dart:isolate", "IsolateSpawnException", "IsolateSpawnException." }, |
| + { "dart:isolate", "_IsolateUnhandledException", |
| + "_IsolateUnhandledException." }, |
| + { "dart:core", "_JavascriptIntegerOverflowError", |
| + "_JavascriptIntegerOverflowError." }, |
| + { "dart:core", "_JavascriptCompatibilityError", |
| + "_JavascriptCompatibilityError." }, |
| + { "dart:core", "AssertionError", "AssertionError." }, |
| + { "dart:core", "_CastError", "_CastError._create" }, |
| + { "dart:core", "_TypeError", "_TypeError._create" }, |
| + { "dart:core", "FallThroughError", "FallThroughError._create" }, |
| + { "dart:core", "AbstractClassInstantiationError", |
| + "AbstractClassInstantiationError._create" }, |
| + { "dart:core", "CyclicInitializationError", |
| + "CyclicInitializationError." }, |
| + { "dart:core", "StackOverflowError", "StackOverflowError." }, |
| + { "dart:core", "OutOfMemoryError", "OutOfMemoryError." }, |
| + { NULL, NULL, NULL } |
| + }; |
| + |
| + Library& lib = Library::Handle(Z); |
| + Function& func = Function::Handle(Z); |
| + String& library_name = String::Handle(Z); |
| + String& class_name = String::Handle(Z); |
| + String& function_name = String::Handle(Z); |
| + for (intptr_t i = 0; kExternallyCalled[i].library_ != NULL; i++) { |
| + library_name = Symbols::New(kExternallyCalled[i].library_); |
| + class_name = Symbols::New(kExternallyCalled[i].class_); |
| + function_name = Symbols::New(kExternallyCalled[i].function_); |
| + |
| + lib = Library::LookupLibrary(library_name); |
| + if (lib.IsNull()) { |
| if (FLAG_trace_precompiler) { |
| - OS::Print(" Precompiling initializer for %s\n", field.ToCString()); |
| + OS::Print("WARNING: Missing %s\n", kExternallyCalled[i].library_); |
| } |
| - Compiler::CompileStaticInitializer(field); |
| + continue; |
| + } |
| + |
| + if (class_name.raw() == Symbols::TopLevel().raw()) { |
| + func = lib.LookupFunctionAllowPrivate(function_name); |
| + } else { |
| + cls = lib.LookupClassAllowPrivate(class_name); |
| + if (cls.IsNull()) { |
| + if (FLAG_trace_precompiler) { |
| + OS::Print("WARNING: Missing %s %s\n", |
| + kExternallyCalled[i].library_, |
| + kExternallyCalled[i].class_); |
| + } |
| + continue; |
| + } |
| + |
| + ASSERT(!cls.IsNull()); |
| + func = cls.LookupFunctionAllowPrivate(function_name); |
| } |
| + |
| + if (func.IsNull()) { |
| + if (FLAG_trace_precompiler) { |
| + OS::Print("WARNING: Missing %s %s %s\n", |
| + kExternallyCalled[i].library_, |
| + kExternallyCalled[i].class_, |
| + kExternallyCalled[i].function_); |
| + } |
| + continue; |
| + } |
| + |
| + AddFunction(func); |
| } |
| +} |
| + |
| + |
| +void Precompiler::Iterate() { |
| + Function& function = Function::Handle(Z); |
| + |
| + while (changed_) { |
| + changed_ = false; |
| - const Array& functions = Array::Handle(cls.functions()); |
| - Function& func = Function::Handle(); |
| - for (intptr_t i = 0; i < functions.Length(); i++) { |
| - func ^= functions.At(i); |
| - ASSERT(!func.IsNull()); |
| - error = CompileFunction(func); |
| - if (!error.IsNull()) { |
| - return error.raw(); |
| + while (pending_functions_.Length() > 0) { |
| + function ^= pending_functions_.RemoveLast(); |
| + ProcessFunction(function); |
| + } |
| + |
| + CheckForNewDynamicFunctions(); |
| + |
| + // Drain collected_closures last because additions to this list come from |
| + // outside the Precompiler and so do not flip our changed_ flag. |
| + while (collected_closures_.Length() > 0) { |
| + function ^= collected_closures_.RemoveLast(); |
| + ProcessFunction(function); |
| } |
| } |
| +} |
| + |
| - return error.raw(); |
| +void Precompiler::CleanUp() { |
| + I->set_collected_closures(GrowableObjectArray::Handle(Z)); |
| + |
| + // TODO(rmacnak): Drop functions without code, classes without functions, etc. |
| } |
| -static RawError* CompileLibrary(const Library& lib) { |
| - if (FLAG_trace_precompiler) { |
| - OS::Print("Precompiling %s\n", lib.ToCString()); |
| - } |
| - ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| - Class& cls = Class::Handle(); |
| - Error& error = Error::Handle(); |
| - while (it.HasNext()) { |
| - cls = it.GetNextClass(); |
| - error = CompileClass(cls); |
| - if (!error.IsNull()) { |
| - return error.raw(); |
| +void Precompiler::ProcessFunction(const Function& function) { |
| + if (!function.HasCode()) { |
| + function_count_++; |
| + |
| + if (FLAG_trace_precompiler) { |
| + OS::Print("Precompiling %" Pd " %s (%" Pd ", %s)\n", |
| + function_count_, |
| + function.ToLibNamePrefixedQualifiedCString(), |
| + function.token_pos(), |
| + Function::KindToCString(function.kind())); |
| + } |
| + |
| + ASSERT(!function.is_abstract()); |
| + ASSERT(!function.IsRedirectingFactory()); |
| + |
| + error_ = Compiler::CompileFunction(thread_, function); |
| + if (!error_.IsNull()) { |
| + Jump(error_); |
| } |
| } |
| - return error.raw(); |
| + ASSERT(function.HasCode()); |
| + AddCalleesOf(function); |
| } |
| -static RawError* InnerCompileAll() { |
| - Error& error = Error::Handle(); |
| - const GrowableObjectArray& libs = GrowableObjectArray::Handle( |
| - Isolate::Current()->object_store()->libraries()); |
| - Library& lib = Library::Handle(); |
| - for (int i = 0; i < libs.Length(); i++) { |
| - lib ^= libs.At(i); |
| - error = CompileLibrary(lib); |
| - if (!error.IsNull()) { |
| - return error.raw(); |
| +void Precompiler::AddCalleesOf(const Function& function) { |
| + ASSERT(function.HasCode()); |
| + |
| + const Code& code = Code::Handle(Z, function.CurrentCode()); |
| + |
| + const Array& table = Array::Handle(Z, code.static_calls_target_table()); |
| + Object& entry = Object::Handle(Z); |
| + Function& target = Function::Handle(Z); |
| + for (intptr_t i = 0; i < table.Length(); i++) { |
| + entry = table.At(i); |
| + if (entry.IsFunction()) { |
| + target ^= table.At(i); |
| + AddFunction(target); |
| } |
| } |
| - if (FLAG_trace_precompiler) { |
| - OS::Print("** Precompiling collected closures **\n"); |
| - } |
| - |
| - const GrowableObjectArray& closures = |
| - GrowableObjectArray::Handle(Isolate::Current()->collected_closures()); |
| - Function& func = Function::Handle(); |
| - if (!closures.IsNull()) { |
| - for (int i = 0; i < closures.Length(); i++) { |
| - func ^= closures.At(i); |
| - error = CompileFunction(func); |
| - if (!error.IsNull()) { |
| - return error.raw(); |
| +#if defined(TARGET_ARCH_IA32) |
| + FATAL("Callee scanning unimplemented for IA32"); |
| +#endif |
| + |
| + const ObjectPool& pool = ObjectPool::Handle(Z, code.GetObjectPool()); |
| + ICData& call_site = ICData::Handle(Z); |
| + String& selector = String::Handle(Z); |
| + Field& field = Field::Handle(Z); |
| + Class& cls = Class::Handle(Z); |
| + for (intptr_t i = 0; i < pool.Length(); i++) { |
| + if (pool.InfoAt(i) == ObjectPool::kTaggedObject) { |
| + entry = pool.ObjectAt(i); |
| + if (entry.IsICData()) { |
| + call_site ^= entry.raw(); |
| + if (call_site.NumberOfChecks() == 1) { |
| + // Probably a static call. |
| + target = call_site.GetTargetAt(0); |
| + AddFunction(target); |
| + if (!target.is_static()) { |
| + // Super call (should not enqueue selector) or dynamic call with a |
| + // CHA prediction (should enqueue selector). |
| + selector = call_site.target_name(); |
| + AddSelector(selector); |
| + } |
| + } else { |
| + // A dynamic call. |
| + selector = call_site.target_name(); |
| + AddSelector(selector); |
| + } |
| + } else if (entry.IsField()) { |
| + // Potential need for field initializer. |
| + field ^= entry.raw(); |
| + AddField(field); |
| + } else if (entry.IsInstance()) { |
| + // Potential const object. |
| + cls = entry.clazz(); |
| + AddClass(cls); |
| + } |
| + } |
| + } |
| +} |
| + |
| + |
| +void Precompiler::AddField(const Field& field) { |
| + if (field.is_static()) { |
| + // Potential const object. Uninitialized field will harmlessly do a redudant |
|
Florian Schneider
2015/08/04 08:20:01
*redundant
rmacnak
2015/08/04 18:34:28
Done.
|
| + // add of the Null class. |
| + const Object& value = Object::Handle(Z, field.value()); |
| + const Class& cls = Class::Handle(Z, value.clazz()); |
| + AddClass(cls); |
| + |
| + if (field.has_initializer()) { |
| + if (field.initializer() != Function::null()) return; |
| + |
| + if (FLAG_trace_precompiler) { |
| + OS::Print("Precompiling initializer for %s\n", field.ToCString()); |
| } |
| + Compiler::CompileStaticInitializer(field); |
| + |
| + const Function& function = Function::Handle(Z, field.initializer()); |
| + AddCalleesOf(function); |
| } |
| } |
| - Isolate::Current()->set_collected_closures(GrowableObjectArray::Handle()); |
| +} |
| + |
| + |
| +void Precompiler::AddFunction(const Function& function) { |
| + if (function.HasCode()) return; |
| + |
| + pending_functions_.Add(function); |
| + changed_ = true; |
| +} |
| + |
| + |
| +bool Precompiler::IsSent(const String& selector) { |
| + ASSERT(selector.IsSymbol()); |
| + |
| + // TODO(rmacnak): Use a proper set. |
|
Florian Schneider
2015/08/04 08:20:01
You could use the one from runtime/vm/hash_map.h,
rmacnak
2015/08/04 18:34:28
I'll do that in the next CL.
|
| + for (intptr_t i = 0; i < sent_selectors_.Length(); i++) { |
| + if (sent_selectors_.At(i) == selector.raw()) { |
| + return true; |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| + |
| +void Precompiler::AddSelector(const String& selector) { |
| + if (!IsSent(selector)) { |
| + if (FLAG_trace_precompiler) { |
| + OS::Print("Enqueueing selector %" Pd " %s\n", |
| + sent_selectors_.Length(), |
| + selector.ToCString()); |
| + } |
| + |
| + sent_selectors_.Add(selector); |
| + changed_ = true; |
| + |
| + if (!Field::IsGetterName(selector) && |
| + !Field::IsSetterName(selector)) { |
| + // Regular method may be call-through-getter. |
| + // TODO(rmacnak): Do not create the symbol if it does not already exist. |
| + String& getter = String::Handle(Field::GetterName(selector)); |
| + getter = Symbols::New(getter); |
| + AddSelector(getter); |
| + } |
| + } |
| +} |
| + |
| + |
| +void Precompiler::AddClass(const Class& cls) { |
| + if (cls.is_allocated()) return; |
| + |
| + class_count_++; |
| + cls.set_is_allocated(); |
| + changed_ = true; |
| if (FLAG_trace_precompiler) { |
| - OS::Print("*** Done precompiling ***\n"); |
| + OS::Print("Allocation %ld %s\n", class_count_, cls.ToCString()); |
| + } |
| + |
| + const Class& superclass = Class::Handle(cls.SuperClass()); |
| + if (!superclass.IsNull()) { |
| + AddClass(superclass); |
| } |
| - Isolate::Current()->set_compilation_allowed(false); |
| - return error.raw(); |
| } |
| -RawError* Precompiler::CompileAll() { |
| - LongJumpScope jump; |
| - if (setjmp(*jump.Set()) == 0) { |
| - return InnerCompileAll(); |
| - } else { |
| - Isolate* isolate = Isolate::Current(); |
| - const Error& error = Error::Handle(isolate, |
| - isolate->object_store()->sticky_error()); |
| - isolate->object_store()->clear_sticky_error(); |
| - return error.raw(); |
| +void Precompiler::CheckForNewDynamicFunctions() { |
| + Library& lib = Library::Handle(Z); |
| + Class& cls = Class::Handle(Z); |
| + Array& functions = Array::Handle(Z); |
| + Function& function = Function::Handle(Z); |
| + String& selector = String::Handle(Z); |
| + |
| + for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| + lib ^= libraries_.At(i); |
| + ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| + while (it.HasNext()) { |
| + cls = it.GetNextClass(); |
| + |
| + if (!cls.is_allocated()) { |
| + bool has_compiled_constructor = false; |
| + if (cls.allocation_stub() != Code::null()) { |
| + // Regular objects. |
| + has_compiled_constructor = true; |
| + } else if (cls.is_synthesized_class()) { |
| + // Enums. |
| + has_compiled_constructor = true; |
| + } else { |
| + // Objects only allocated via const constructors, and not stored in a |
| + // static field or code. |
| + // E.g. A in |
| + // class A { |
| + // const A(); |
| + // toString() => "Don't drop me!"; |
| + // } |
| + // class B { |
| + // const a = const A(); |
| + // const B(); |
| + // static const theB = const B(); |
| + // } |
| + // main() => print(B.theB.a); |
| + functions = cls.functions(); |
| + for (intptr_t k = 0; k < functions.Length(); k++) { |
| + function ^= functions.At(k); |
| + if (function.IsGenerativeConstructor() && |
| + function.HasCode()) { |
| + has_compiled_constructor = true; |
| + break; |
| + } |
| + } |
| + } |
| + if (!has_compiled_constructor) { |
| + continue; |
| + } |
| + AddClass(cls); |
| + } |
| + |
| + functions = cls.functions(); |
| + for (intptr_t k = 0; k < functions.Length(); k++) { |
| + function ^= functions.At(k); |
| + |
| + if (function.is_static() || function.is_abstract()) continue; |
| + |
| + // Don't bail out early if there is already code because we may discover |
| + // the corresponding getter selector is sent in some later iteration. |
| + // if (function.HasCode()) continue; |
| + |
| + selector = function.name(); |
| + if (IsSent(selector)) { |
| + AddFunction(function); |
| + } |
| + |
| + if (function.kind() == RawFunction::kRegularFunction && |
| + !Field::IsGetterName(selector) && |
| + !Field::IsSetterName(selector)) { |
| + // TODO(rmacnak): Do not create the symbol if it does not already |
| + // exist. |
| + selector = Field::GetterName(selector); |
| + selector = Symbols::New(selector); |
| + if (IsSent(selector)) { |
| + function = function.ImplicitClosureFunction(); |
| + AddFunction(function); |
| + } |
| + } |
| + } |
| + } |
| } |
| } |