text: // Copyright (c) 2011, 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/resolver.h"
  
  #include "vm/dart_entry.h"
  #include "vm/flags.h"
  #include "vm/isolate.h"
  #include "vm/log.h"
  #include "vm/object.h"
  #include "vm/object_store.h"
  #include "vm/symbols.h"
  
  namespace dart {
  
  DEFINE_FLAG(bool, trace_resolving, false, "Trace resolving.");
  
  // The actual names of named arguments are not checked by the dynamic resolver,
  // but by the method entry code. It is important that the dynamic resolver
  // checks that no named arguments are passed to a method that does not accept
  // them, since the entry code of such a method does not check for named
  // arguments. The dynamic resolver actually checks that a valid number of named
  // arguments is passed in.
  RawFunction* Resolver::ResolveDynamic(const Instance& receiver,
                                        const String& function_name,
                                        const ArgumentsDescriptor& args_desc) {
    // Figure out type of receiver first.
    const Class& cls = Class::Handle(receiver.clazz());
    return ResolveDynamicForReceiverClass(cls, function_name, args_desc);
  }
  
  RawFunction* Resolver::ResolveDynamicForReceiverClass(
      const Class& receiver_class,
      const String& function_name,
      const ArgumentsDescriptor& args_desc,
      bool allow_add) {
    Thread* thread = Thread::Current();
    Zone* zone = thread->zone();
  
    Function& function = Function::Handle(
        zone,
        ResolveDynamicAnyArgs(zone, receiver_class, function_name, allow_add));
  
    if (function.IsNull() ||
        !function.AreValidArguments(NNBDMode::kLegacy, args_desc, NULL)) {
      // Return a null function to signal to the upper levels to dispatch to
      // "noSuchMethod" function.
      if (FLAG_trace_resolving) {
        String& error_message =
            String::Handle(zone, Symbols::New(thread, "function not found"));
        if (!function.IsNull()) {
          // Obtain more detailed error message.
          function.AreValidArguments(NNBDMode::kLegacy, args_desc,
                                     &error_message);
        }
        THR_Print("ResolveDynamic error '%s': %s.\n", function_name.ToCString(),
                  error_message.ToCString());
      }
      return Function::null();
    }
    return function.raw();
  }
  
  RawFunction* Resolver::ResolveDynamicAnyArgs(Zone* zone,
                                               const Class& receiver_class,
                                               const String& function_name,
                                               bool allow_add) {
    Class& cls = Class::Handle(zone, receiver_class.raw());
    if (FLAG_trace_resolving) {
      THR_Print("ResolveDynamic '%s' for class %s\n", function_name.ToCString(),
                String::Handle(zone, cls.Name()).ToCString());
    }
    Function& function = Function::Handle(zone);
  
    String& demangled = String::Handle(zone);
  
    const bool is_getter = Field::IsGetterName(function_name);
    if (is_getter) {
      demangled = Field::NameFromGetter(function_name);
    }
  
    if (Function::IsDynamicInvocationForwarderName(function_name)) {
      demangled = Function::DemangleDynamicInvocationForwarderName(function_name);
  #ifdef DART_PRECOMPILED_RUNTIME
      // In precompiled mode, the non-dynamic version of the function may be
      // tree-shaken away, so can't necessarily resolve the demanged name.
      while (!cls.IsNull()) {
        function = cls.GetInvocationDispatcher(
            function_name, Array::null_array(),
            RawFunction::kDynamicInvocationForwarder, /*create_if_absent=*/false);
        if (!function.IsNull()) break;
        cls = cls.SuperClass();
      }
      // Some functions don't require dynamic invocation forwarders, for example
      // if there are no parameters or all the parameters are marked
      // `generic-covariant` (meaning there's no work for the dynamic invocation
      // forwarder to do, see `kernel::DynamicInvocationForwarder`). For these
      // functions, we won't have built a `dyn:` version, but it's safe to just
      // return the original version directly.
      return !function.IsNull() ? function.raw()
                                : ResolveDynamicAnyArgs(zone, receiver_class,
                                                        demangled, allow_add);
  #else
      function =
          ResolveDynamicAnyArgs(zone, receiver_class, demangled, allow_add);
      return function.IsNull() ? function.raw()
                               : function.GetDynamicInvocationForwarder(
                                     function_name, allow_add);
  #endif
    }
  
    // Now look for an instance function whose name matches function_name
    // in the class.
    while (!cls.IsNull()) {
      function = cls.LookupDynamicFunction(function_name);
      if (!function.IsNull()) {
        return function.raw();
      }
      // Getter invocation might actually be a method extraction.
      if (is_getter && function.IsNull()) {
        function = cls.LookupDynamicFunction(demangled);
        if (!function.IsNull()) {
          if (allow_add && FLAG_lazy_dispatchers) {
            // We were looking for the getter but found a method with the same
            // name. Create a method extractor and return it.
            // The extractor does not exist yet, so using GetMethodExtractor is
            // not necessary here.
            function = function.CreateMethodExtractor(function_name);
            return function.raw();
          } else {
            return Function::null();
          }
        }
      }
      cls = cls.SuperClass();
    }
    return function.raw();
  }
  
  RawFunction* Resolver::ResolveStatic(const Library& library,
                                       const String& class_name,
                                       const String& function_name,
                                       intptr_t type_args_len,
                                       intptr_t num_arguments,
                                       const Array& argument_names) {
    ASSERT(!library.IsNull());
    Function& function = Function::Handle();
    if (class_name.IsNull() || (class_name.Length() == 0)) {
      // Check if we are referring to a top level function.
      const Object& object = Object::Handle(library.ResolveName(function_name));
      if (!object.IsNull() && object.IsFunction()) {
        function ^= object.raw();
        if (!function.AreValidArguments(NNBDMode::kLegacy, type_args_len,
                                        num_arguments, argument_names, NULL)) {
          if (FLAG_trace_resolving) {
            String& error_message = String::Handle();
            // Obtain more detailed error message.
            function.AreValidArguments(NNBDMode::kLegacy, type_args_len,
                                       num_arguments, argument_names,
                                       &error_message);
            THR_Print("ResolveStatic error '%s': %s.\n",
                      function_name.ToCString(), error_message.ToCString());
          }
          function = Function::null();
        }
      } else {
        if (FLAG_trace_resolving) {
          THR_Print("ResolveStatic error: function '%s' not found.\n",
                    function_name.ToCString());
        }
      }
    } else {
      // Lookup class_name in the library's class dictionary to get at
      // the dart class object. If class_name is not found in the dictionary
      // ResolveStatic will return a NULL function object.
      const Class& cls = Class::Handle(library.LookupClass(class_name));
      if (!cls.IsNull()) {
        function = ResolveStatic(cls, function_name, type_args_len, num_arguments,
                                 argument_names);
      }
      if (FLAG_trace_resolving && function.IsNull()) {
        THR_Print("ResolveStatic error: function '%s.%s' not found.\n",
                  class_name.ToCString(), function_name.ToCString());
      }
    }
    return function.raw();
  }
  
  RawFunction* Resolver::ResolveStatic(const Class& cls,
                                       const String& function_name,
                                       intptr_t type_args_len,
                                       intptr_t num_arguments,
                                       const Array& argument_names) {
    ASSERT(!cls.IsNull());
    if (FLAG_trace_resolving) {
      THR_Print("ResolveStatic '%s'\n", function_name.ToCString());
    }
    const Function& function =
        Function::Handle(cls.LookupStaticFunction(function_name));
    if (function.IsNull() ||
        !function.AreValidArguments(NNBDMode::kLegacy, type_args_len,
                                    num_arguments, argument_names, NULL)) {
      // Return a null function to signal to the upper levels to throw a
      // resolution error or maybe throw the error right here.
      if (FLAG_trace_resolving) {
        String& error_message = String::Handle(String::New("function not found"));
        if (!function.IsNull()) {
          // Obtain more detailed error message.
          function.AreValidArguments(NNBDMode::kLegacy, type_args_len,
                                     num_arguments, argument_names,
                                     &error_message);
        }
        THR_Print("ResolveStatic error '%s': %s.\n", function_name.ToCString(),
                  error_message.ToCString());
      }
      return Function::null();
    }
    return function.raw();
  }
  
  RawFunction* Resolver::ResolveStaticAllowPrivate(const Class& cls,
                                                   const String& function_name,
                                                   intptr_t type_args_len,
                                                   intptr_t num_arguments,
                                                   const Array& argument_names) {
    ASSERT(!cls.IsNull());
    if (FLAG_trace_resolving) {
      THR_Print("ResolveStaticAllowPrivate '%s'\n", function_name.ToCString());
    }
    const Function& function =
        Function::Handle(cls.LookupStaticFunctionAllowPrivate(function_name));
    if (function.IsNull() ||
        !function.AreValidArguments(NNBDMode::kLegacy, type_args_len,
                                    num_arguments, argument_names, NULL)) {
      // Return a null function to signal to the upper levels to throw a
      // resolution error or maybe throw the error right here.
      if (FLAG_trace_resolving) {
        String& error_message = String::Handle(String::New("function not found"));
        if (!function.IsNull()) {
          // Obtain more detailed error message.
          function.AreValidArguments(NNBDMode::kLegacy, type_args_len,
                                     num_arguments, argument_names,
                                     &error_message);
        }
        THR_Print("ResolveStaticAllowPrivate error '%s': %s.\n",
                  function_name.ToCString(), error_message.ToCString());
      }
      return Function::null();
    }
    return function.raw();
  }
  
  }  // namespace dart
  