Czy „call site” to jakiś automatyczny kod generowany przez kompilator – często spotykam ten termin i wygląda na to, że metoda wywoływania jest po prostu określana jako „call site” – co dosłownie brzmi dobrze, ale wydaje mi się, że ma kilka głębszych zawiłości. A jeśli „call site” jest kodem wygenerowanym przez kompilator – w jakich okolicznościach jest to wymagane?
Chociaż mówię w kontekście C # .Net, ale wygląda to na standardową terminologię. Byłby wdzięczny za jasne / precyzyjne wyjaśnienie z kilkoma przykładami.
Komentarze
- Istnieje pojęcie strony wywołania zarówno w kodzie źródłowym, jak i -generowany kod obiektowy.
- Możesz chcieć przyjrzeć się, jak witryny wywołań są generowane dla operacji dynamicznej w C # 4 i nowszych. Są całkiem interesujące.
- @EricLippert Dzięki. Na szczęście znalazłem link , w którym całkiem pięknie to opisałeś. Nie udało mi się jednak znaleźć żadnego Twojego bloga szczegółowo opisującego generowanie witryn z połączeniami.
Odpowiedź
„Zadzwoń do witryny ”Odnosi się do miejsca, w którym wywoływana jest funkcja lub metoda. Jest to termin powszechnie stosowany w konstrukcji kompilatora i nie jest specyficzny dla języka C #.
foo(); // call site void foo() { ... } // function definition
Witryna wywołań nie jest żadnym specjalnie wygenerowanym kodem. Ale w miejscu wywołania kompilator emituje instrukcje wywołania funkcji. Zazwyczaj te:
- Zapisuje bieżący kontekst w ramce stosu.
- Przechowuje wszystkie argumenty zgodnie z konwencją wywoływania funkcji docelowej.
- Wywołaj funkcję docelową, która może wymagać wysłania metody.
- Pobierz wartość zwracaną zgodnie z konwencją wywoływania.
- Przywróć kontekst z ramki stosu.
Wysyłanie metod może wymagać wykonania skomplikowanego kodu w celu znalezienia funkcji, która powinna zostać wywołana. Jest to konieczne podczas wywoływania metod virtual
, zwłaszcza metod za pośrednictwem interfejsu. Pełna procedura wysyłania metod może być dość kosztowna (często O (n) w liczbie zaimplementowanych interfejsów lub O (n) w liczbie dostępnych metod). Jednak przez większość czasu (zazwyczaj 70–90%) w witrynie wywołania będą widoczne obiekty tego samego typu. Wówczas sensowne jest buforowanie wyniku wyszukiwania metody. Kompilator może następnie wygenerować taki kod w witrynie wywołania:
static lastType = null; static lastMethod = null; if (type(instance) != lastType) { lastType = type(instance); lastMethod = performMethodLookup(instance, methodName); } result = lastMethod(instance, args);
Jest to znane jako monomorficzny wbudowana pamięć podręczna . W środowisku .NET CLR opracowano nieco bardziej skomplikowane podejście o nazwie Virtual Stub Dispatch . Zamiast wykonywać wysyłanie w miejscu wywołania, środowisko wykonawcze JIT kompiluje metody pośredniczące. Istnieją różne kody pośredniczące do wykonywania pełnej procedury wysyłania metod i do korzystania z metody buforowanej. W miejscu wywołania mamy wtedy tylko wywołanie do kodu pośredniczącego, który przekieruje wywołanie do rzeczywistego celu. W zależności od statystyk dla tej witryny wywołania kod maszynowy w tej witrynie zostanie poprawiony tak, aby używał innego kodu.
Odpowiedź
Najbardziej typowym zastosowaniem call-site jest prawdopodobnie to, że jak obecnie podaje Wikipedia : jest to nic innego jak miejsce w jakimś kodzie, gdzie wywołanie wykonywana jest funkcja lub podprogram. Jest to całkowicie ogólny termin, który można zastosować do wszystkich rodzajów języków (iz pewnością nie ma to nic wspólnego z programowaniem dynamicznym!).
Zwykle słyszę używany termin (w przeciwieństwie do samego termin „wywołanie”), aby było jasne, że omawiamy składnię i semantykę kodu, w którym wywoływana jest funkcja / metoda / podprogram, w przeciwieństwie do podstawowej semantyki wywołania lub semantyki w definicji funkcji / metody: w w ten sposób absolutnie nie dotyczy kompilatora ani żadnego kodu generującego (nawet jeśli termin ten będzie nadal odnosił się do kodu generowanego przez kompilator / środowisko uruchomieniowe, jak myślę, że amon już omówił), jest to o przeniesieniu konkretnego kodu, który tłumaczy wywołanie, do kontekstu, a czasami kodu w bezpośrednim sąsiedztwie.
Oto rodzaj obrazu, który mam w głowie:
call-site call call-target/method definition --> --> int.TryParse(str, out i); (runtime) public static bool TryPars(...
” call-site „jest użytecznym terminem do omówienia składni wykonywania połączenia oraz semantyki, która wpływa na dzwoniącego (szczegółowo opisana przez amon). Rozważmy na przykład nowy modyfikator in
w C #, „wywołanie”, które przekazuje argument jako „in” ma skojarzoną z nim pewną semantykę (jest przekazywane przez ref, prawdopodobnie po kopia), ale „niekoniecznie wynika to ze składni„ w miejscu wywołania ”, gdzie in
nie jest obowiązkowe. Chodzi o to, że semantyka witryny wywołania (tj. Zachowanie, które wpływa na osobę dzwoniącą / witrynę wywołującą) przekazywania argumentu przez odwołanie z in
jest zbliżona wystarczająco dużo do tych, które przekazują przez wartość, że nie trzeba ich ujednoznaczniać przez składnię w miejscu wywołania .Nie zgadzam się z tym twierdzeniem i trudno byłoby mi go omówić bez określenia takiego jak „call-site”!
Inny przykład: dynamiczne wywołanie (np. Dla metody wirtualnej / interfejsu) ma trochę skomplikowane zachowania w czasie wykonywania (ponownie, szczegółowo opisane przez amon) i nie wiadomo w czasie kompilacji, która metoda zostanie wywołana, ale cała „strona wywołania” jest ważna to semantyka wysyłania tego wywołania: jeśli wywołujesz ToString()
na object
, nie naprawdę obchodzi Cię, że to faktycznie string
w miejscu wywołania; obchodzi Cię tylko to, że object
ujawnia wirtualną metodę ToString()
(zależy to od środowiska wykonawczego aby dowiedzieć się, którą metodę faktycznie wywołać). Rzeczywiście, w miejscu wywołania (np. W C #) ze składni niekoniecznie jasno wynika, czy jest to połączenie wirtualne: rozróżnienie między połączeniem wirtualnym i niewirtualnym jest często uważane za wystarczająco nieważne podczas połączenia- site , że nie musi być ujednoznaczniane dla programisty w miejscu wywołania (nawet jeśli jest niezwykle ważne, aby kompilator / środowisko wykonawcze wykonały znaczące wywołanie)
Ostatni przykład: rozważmy C # „s ref
i C ++” s &
: semantykę w miejscu wywołania w obu język jest prawie taki sam w obu językach: zamiast wartości przekazywana jest referencja, ale każdy język ma inną składnię w miejscu wywołania, gdzie C # wymaga ref
, a C ++ nie wymagają &
(lub czegoś podobnego). Ponownie, projektanci języka podjęli pewne decyzje dotyczące składni dotyczące reprezentacji wywołań w miejscu wywołania, na podstawie semantyki w miejscu wywołania. (Wolę składnię call-site w języku C #, ponieważ uwidacznia ona semantykę call-site, którą moim zdaniem jako programista powinienem uwzględnić, wykonując takie wywołanie). Jest miejmy nadzieję, że definicja metody musi wiedzieć, że otrzymuje parametr przez odniesienie, ponieważ zmienia zachowanie podczas jego modyfikacji (np. Za pomocą =
). Mógłbyś jednak argumentować, że środowisko wykonawcze nie obchodzi (w takich statycznie typowanych językach, gdzie wysyłanie w czasie wykonywania jest nominalnie informowane): po prostu musi pobrać tę wartość od wywołującego do odbiorcy , nie obchodzi go, czy jest to odniesienie, czy nie, jest to problem dotyczący witryny wywołującej i celu połączenia.
Mówiąc bardziej luźno, „call-site” może odnosić się do kod, który wskazuje wywołanie, a także „powiązany” kod wokół niego (w przeciwieństwie do samego „połączenia”). Na przykład w C # można by się odnieść do definiowania fikcyjnej zmiennej „w” miejscu wywołania, np. podczas wywoływania metody TryParse
:
int dummy; bool looksLikeAnInt = int.TryParse(str, out dummy);
Cały ten „blok” można uznać za część witryny wywoławczej. Uznałbym to za mniej „precyzyjne” znaczenie, ale to nie przeszkadza mi w jego używaniu.