|  | // Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/compilation_trace.h" | 
|  |  | 
|  | #include "vm/globals.h" | 
|  | #include "vm/log.h" | 
|  | #include "vm/longjump.h" | 
|  | #include "vm/object_store.h" | 
|  | #include "vm/resolver.h" | 
|  | #include "vm/symbols.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | #if !defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | DEFINE_FLAG(bool, trace_compilation_trace, false, "Trace compilation trace."); | 
|  |  | 
|  | CompilationTraceSaver::CompilationTraceSaver(Zone* zone) | 
|  | : buf_(zone, 4 * KB), | 
|  | func_name_(String::Handle(zone)), | 
|  | cls_(Class::Handle(zone)), | 
|  | cls_name_(String::Handle(zone)), | 
|  | lib_(Library::Handle(zone)), | 
|  | uri_(String::Handle(zone)) {} | 
|  |  | 
|  | void CompilationTraceSaver::Visit(const Function& function) { | 
|  | if (!function.HasCode()) { | 
|  | return;  // Not compiled. | 
|  | } | 
|  | if (function.parent_function() != Function::null()) { | 
|  | // Lookup works poorly for local functions. We compile all local functions | 
|  | // in a compiled function instead. | 
|  | return; | 
|  | } | 
|  |  | 
|  | func_name_ = function.name(); | 
|  | func_name_ = String::RemovePrivateKey(func_name_); | 
|  | cls_ = function.Owner(); | 
|  | cls_name_ = cls_.Name(); | 
|  | cls_name_ = String::RemovePrivateKey(cls_name_); | 
|  | lib_ = cls_.library(); | 
|  | uri_ = lib_.url(); | 
|  | buf_.Printf("%s,%s,%s\n", uri_.ToCString(), cls_name_.ToCString(), | 
|  | func_name_.ToCString()); | 
|  | } | 
|  |  | 
|  | CompilationTraceLoader::CompilationTraceLoader(Thread* thread) | 
|  | : thread_(thread), | 
|  | zone_(thread->zone()), | 
|  | uri_(String::Handle(zone_)), | 
|  | class_name_(String::Handle(zone_)), | 
|  | function_name_(String::Handle(zone_)), | 
|  | function_name2_(String::Handle(zone_)), | 
|  | lib_(Library::Handle(zone_)), | 
|  | cls_(Class::Handle(zone_)), | 
|  | function_(Function::Handle(zone_)), | 
|  | function2_(Function::Handle(zone_)), | 
|  | field_(Field::Handle(zone_)), | 
|  | error_(Object::Handle(zone_)) {} | 
|  |  | 
|  | static char* FindCharacter(char* str, char goal, char* limit) { | 
|  | while (str < limit) { | 
|  | if (*str == goal) { | 
|  | return str; | 
|  | } | 
|  | str++; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | RawObject* CompilationTraceLoader::CompileTrace(uint8_t* buffer, | 
|  | intptr_t size) { | 
|  | // First compile functions named in the trace. | 
|  | char* cursor = reinterpret_cast<char*>(buffer); | 
|  | char* limit = cursor + size; | 
|  | while (cursor < limit) { | 
|  | char* uri = cursor; | 
|  | char* comma1 = FindCharacter(uri, ',', limit); | 
|  | if (comma1 == NULL) { | 
|  | break; | 
|  | } | 
|  | *comma1 = 0; | 
|  | char* cls_name = comma1 + 1; | 
|  | char* comma2 = FindCharacter(cls_name, ',', limit); | 
|  | if (comma2 == NULL) { | 
|  | break; | 
|  | } | 
|  | *comma2 = 0; | 
|  | char* func_name = comma2 + 1; | 
|  | char* newline = FindCharacter(func_name, '\n', limit); | 
|  | if (newline == NULL) { | 
|  | break; | 
|  | } | 
|  | *newline = 0; | 
|  | error_ = CompileTriple(uri, cls_name, func_name); | 
|  | if (error_.IsError()) { | 
|  | return error_.raw(); | 
|  | } | 
|  | cursor = newline + 1; | 
|  | } | 
|  |  | 
|  | // Next, compile common dispatchers. These aren't found with the normal | 
|  | // lookup above because they have irregular lookup that depends on the | 
|  | // arguments descriptor (e.g. call() versus call(x)). | 
|  | const Class& closure_class = | 
|  | Class::Handle(zone_, thread_->isolate()->object_store()->closure_class()); | 
|  | Array& arguments_descriptor = Array::Handle(zone_); | 
|  | Function& dispatcher = Function::Handle(zone_); | 
|  | for (intptr_t argc = 1; argc <= 4; argc++) { | 
|  | const intptr_t kTypeArgsLen = 0; | 
|  | arguments_descriptor = ArgumentsDescriptor::New(kTypeArgsLen, argc); | 
|  | dispatcher = closure_class.GetInvocationDispatcher( | 
|  | Symbols::Call(), arguments_descriptor, | 
|  | RawFunction::kInvokeFieldDispatcher, true /* create_if_absent */); | 
|  | error_ = CompileFunction(dispatcher); | 
|  | if (error_.IsError()) { | 
|  | return error_.raw(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Finally, compile closures in all compiled functions. Don't cache the | 
|  | // length since compiling may append to this list. | 
|  | const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle( | 
|  | zone_, thread_->isolate()->object_store()->closure_functions()); | 
|  | for (intptr_t i = 0; i < closure_functions.Length(); i++) { | 
|  | function_ ^= closure_functions.At(i); | 
|  | function2_ = function_.parent_function(); | 
|  | if (function2_.HasCode()) { | 
|  | error_ = CompileFunction(function_); | 
|  | if (error_.IsError()) { | 
|  | return error_.raw(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return Object::null(); | 
|  | } | 
|  |  | 
|  | // Use a fuzzy match to find the right function to compile. This allows a | 
|  | // compilation trace to remain mostly valid in the face of program changes, and | 
|  | // deals with implicit/dispatcher functions that don't have proper names. | 
|  | //  - Ignore private name mangling | 
|  | //  - If looking for a getter and we only have the corresponding regular method, | 
|  | //    compile the regular method, create its implicit closure and compile that. | 
|  | //  - If looking for a regular method and we only have the corresponding getter, | 
|  | //    compile the getter, create its method extractor and compile that. | 
|  | //  - If looking for a getter and we only have a const field, evaluate the const | 
|  | //    field. | 
|  | RawObject* CompilationTraceLoader::CompileTriple(const char* uri_cstr, | 
|  | const char* cls_cstr, | 
|  | const char* func_cstr) { | 
|  | uri_ = Symbols::New(thread_, uri_cstr); | 
|  | class_name_ = Symbols::New(thread_, cls_cstr); | 
|  | function_name_ = Symbols::New(thread_, func_cstr); | 
|  |  | 
|  | if (function_name_.Equals("_getMainClosure")) { | 
|  | // The scheme for invoking main relies on compiling _getMainClosure after | 
|  | // synthetically importing the root library. | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print("Compilation trace: skip %s,%s,%s\n", uri_.ToCString(), | 
|  | class_name_.ToCString(), function_name_.ToCString()); | 
|  | } | 
|  | return Object::null(); | 
|  | } | 
|  |  | 
|  | lib_ = Library::LookupLibrary(thread_, uri_); | 
|  | if (lib_.IsNull()) { | 
|  | // Missing library. | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print("Compilation trace: missing library %s,%s,%s\n", | 
|  | uri_.ToCString(), class_name_.ToCString(), | 
|  | function_name_.ToCString()); | 
|  | } | 
|  | return Object::null(); | 
|  | } | 
|  |  | 
|  | bool is_getter = Field::IsGetterName(function_name_); | 
|  | bool add_closure = false; | 
|  | bool processed = false; | 
|  |  | 
|  | if (class_name_.Equals(Symbols::TopLevel())) { | 
|  | function_ = lib_.LookupFunctionAllowPrivate(function_name_); | 
|  | field_ = lib_.LookupFieldAllowPrivate(function_name_); | 
|  | if (function_.IsNull() && is_getter) { | 
|  | // Maybe this was a tear off. | 
|  | add_closure = true; | 
|  | function_name2_ = Field::NameFromGetter(function_name_); | 
|  | function_ = lib_.LookupFunctionAllowPrivate(function_name2_); | 
|  | field_ = lib_.LookupFieldAllowPrivate(function_name2_); | 
|  | } | 
|  | } else { | 
|  | cls_ = lib_.SlowLookupClassAllowMultiPartPrivate(class_name_); | 
|  | if (cls_.IsNull()) { | 
|  | // Missing class. | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print("Compilation trace: missing class %s,%s,%s\n", | 
|  | uri_.ToCString(), class_name_.ToCString(), | 
|  | function_name_.ToCString()); | 
|  | } | 
|  | return Object::null(); | 
|  | } | 
|  |  | 
|  | error_ = cls_.EnsureIsFinalized(thread_); | 
|  | if (error_.IsError()) { | 
|  | // Non-finalized class. | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print("Compilation trace: non-finalized class %s,%s,%s (%s)\n", | 
|  | uri_.ToCString(), class_name_.ToCString(), | 
|  | function_name_.ToCString(), | 
|  | Error::Cast(error_).ToErrorCString()); | 
|  | } | 
|  | return error_.raw(); | 
|  | } | 
|  |  | 
|  | function_ = cls_.LookupFunctionAllowPrivate(function_name_); | 
|  | field_ = cls_.LookupFieldAllowPrivate(function_name_); | 
|  | if (function_.IsNull() && is_getter) { | 
|  | // Maybe this was a tear off. | 
|  | add_closure = true; | 
|  | function_name2_ = Field::NameFromGetter(function_name_); | 
|  | function_ = cls_.LookupFunctionAllowPrivate(function_name2_); | 
|  | field_ = cls_.LookupFieldAllowPrivate(function_name2_); | 
|  | if (!function_.IsNull() && !function_.is_static()) { | 
|  | // Maybe this was a method extractor. | 
|  | function2_ = | 
|  | Resolver::ResolveDynamicAnyArgs(zone_, cls_, function_name_); | 
|  | if (!function2_.IsNull()) { | 
|  | error_ = CompileFunction(function2_); | 
|  | if (error_.IsError()) { | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print( | 
|  | "Compilation trace: error compiling extractor %s for " | 
|  | "%s,%s,%s (%s)\n", | 
|  | function2_.ToCString(), uri_.ToCString(), | 
|  | class_name_.ToCString(), function_name_.ToCString(), | 
|  | Error::Cast(error_).ToErrorCString()); | 
|  | } | 
|  | return error_.raw(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!field_.IsNull() && field_.is_const() && field_.is_static() && | 
|  | (field_.StaticValue() == Object::sentinel().raw())) { | 
|  | processed = true; | 
|  | error_ = EvaluateInitializer(field_); | 
|  | if (error_.IsError()) { | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print( | 
|  | "Compilation trace: error initializing field %s for %s,%s,%s " | 
|  | "(%s)\n", | 
|  | field_.ToCString(), uri_.ToCString(), class_name_.ToCString(), | 
|  | function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); | 
|  | } | 
|  | return error_.raw(); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!function_.IsNull()) { | 
|  | processed = true; | 
|  | error_ = CompileFunction(function_); | 
|  | if (error_.IsError()) { | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print("Compilation trace: error compiling %s,%s,%s (%s)\n", | 
|  | uri_.ToCString(), class_name_.ToCString(), | 
|  | function_name_.ToCString(), | 
|  | Error::Cast(error_).ToErrorCString()); | 
|  | } | 
|  | return error_.raw(); | 
|  | } | 
|  | if (add_closure) { | 
|  | function_ = function_.ImplicitClosureFunction(); | 
|  | error_ = CompileFunction(function_); | 
|  | if (error_.IsError()) { | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | THR_Print( | 
|  | "Compilation trace: error compiling closure %s,%s,%s (%s)\n", | 
|  | uri_.ToCString(), class_name_.ToCString(), | 
|  | function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); | 
|  | } | 
|  | return error_.raw(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (FLAG_trace_compilation_trace) { | 
|  | if (!processed) { | 
|  | THR_Print("Compilation trace: ignored %s,%s,%s\n", uri_.ToCString(), | 
|  | class_name_.ToCString(), function_name_.ToCString()); | 
|  | } | 
|  | } | 
|  | return Object::null(); | 
|  | } | 
|  |  | 
|  | RawObject* CompilationTraceLoader::CompileFunction(const Function& function) { | 
|  | if (function.is_abstract()) { | 
|  | return Object::null(); | 
|  | } | 
|  | return Compiler::CompileFunction(thread_, function); | 
|  | } | 
|  |  | 
|  | RawObject* CompilationTraceLoader::EvaluateInitializer(const Field& field) { | 
|  | LongJumpScope jump; | 
|  | if (setjmp(*jump.Set()) == 0) { | 
|  | field_.EvaluateInitializer(); | 
|  | } else { | 
|  | Thread* thread = Thread::Current(); | 
|  | const Error& error = Error::Handle(thread->sticky_error()); | 
|  | thread->clear_sticky_error(); | 
|  | return error.raw(); | 
|  | } | 
|  | return Object::null(); | 
|  | } | 
|  |  | 
|  | #endif  // !defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | }  // namespace dart |