Successfully reported this slideshow.
Your SlideShare is downloading. ×

Динамический код: модифицируем таблицу символов во время выполнения. Елена Шишкина. Moscow.pm 4 апреля

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 38 Ad

Динамический код: модифицируем таблицу символов во время выполнения. Елена Шишкина. Moscow.pm 4 апреля

Download to read offline

Видео: http://video.mail.ru/corp/p.scherbinin/6/7.html

Tаблица символов — это только небольшой шаг в мир внутреннего устройства Perl, но и он открывает программисту огромные возможности:

— Runtime-кодогенерация.

— Генерация по запросу.

— Изменение кода сторонних модулей на лету и многое другое.

Видео: http://video.mail.ru/corp/p.scherbinin/6/7.html

Tаблица символов — это только небольшой шаг в мир внутреннего устройства Perl, но и он открывает программисту огромные возможности:

— Runtime-кодогенерация.

— Генерация по запросу.

— Изменение кода сторонних модулей на лету и многое другое.

Advertisement
Advertisement

More Related Content

Slideshows for you (19)

Similar to Динамический код: модифицируем таблицу символов во время выполнения. Елена Шишкина. Moscow.pm 4 апреля (20)

Advertisement

More from Moscow.pm (9)

Recently uploaded (18)

Advertisement

Динамический код: модифицируем таблицу символов во время выполнения. Елена Шишкина. Moscow.pm 4 апреля

  1. 1. Динамический код: модифицируем таблицу символов во время выполнения
  2. 2. • Какие-то проблемы? • Методы runtime- кодогенерации • Таблица символов: матчасть • От теории к практике • Как не выстрелить себе в ногу • RTFM 2
  3. 3. • Конструкторы • Методы-аксессоры • Идентичная предварительная обработка данных • Похожие по функционалу функции с небольшими отличиями corp.mail.ru Повторяющийся код
  4. 4. package Foo; sub new { return bless {}, shift; } package Bar; sub new { return bless {}, shift; } www.mail.ru 4 Конструкторы
  5. 5. sub field1 { my $self = shift; $self->{field1} = $_[0] if @_; return $self->{field1}; } sub field2 { my $self = shift; $self->{field2} = $_[0] if @_; return $self->{field2}; } sub field3 { my $self = shift; $self->{field3} = $_[0] if @_; return $self->{field3}; } www.mail.ru 5 Аксессоры
  6. 6. sub do_something { my $self = shift; $self->check_cookies; return $self->redirect('/login') unless $self->check_auth; my $form = $self->load_form('do_something'); $form->fetch; return $self->render_error() unless $form->validate; my $some_user_data = $self->load_user_data; ... } sub do_another_thing { my $self = shift; $self->check_cookies; ... } www.mail.ru 6 Предварительная обработка
  7. 7. sub error { my $message = shift; my ($package, $line, $sub) = (caller(0))[0, 2, 3]; print $log scalar localtime, "ERROR: ${package}::$sub ($line): $messagen"; print $log Carp::longmess if $Trace_Errors; } sub debug { my $message = shift; my ($package, $line, $sub) = (caller(0))[0, 2, 3]; print $log scalar localtime, "DEBUG: ${package}::$sub ($line): $messagen"; } sub info { ... } sub warning { ... } www.mail.ru 7 Похожие функции
  8. 8. • Потеря времени на перепечатывание/копирование • Ошибки из-за невнимательности • Трудоемкость сопровождения corp.mail.ru Проблемы повторяющегося кода
  9. 9. • Ошибки в реализации • Отсутствие поддержки кириллицы • Недостаточный функционал corp.mail.ru Сторонние модули
  10. 10. print Dumper {test => 'Тестовая строка'}; www.mail.ru 10 Data::Dumper и кириллица в utf8 $VAR1 = { "test" => "x{422}x{435}x{441}x{442}x{43e}x{432}x{430}x {44f} x{441}x{442}x{440}x{43e}x{43a}x{430}" };
  11. 11. package Foo; use Moose; has field1 => (is => 'rw'); has field2 => (is => 'rw'); has field3 => (is => 'rw'); around [qw(do_something do_another_thing)] => sub { my ($orig, $self) = @_; ... $self->$orig(form => $form, user_data => $some_user_data); }; www.mail.ru 11 Решение: CPAN
  12. 12. • Необходимость доказательства целесообразности • Замусоривание системы • Замусоривание блоков use в коде • Снижение производительности • Увеличение времени компиляции • Расход памяти • Уменьшение контроля над кодом («чужой код») www.mail.ru 12 Проблемы использования сторонних модулей
  13. 13. • Какие-то проблемы? • Методы runtime- кодогенерации • Таблица символов: матчасть • От теории к практике • Как не выстрелить себе в ногу • RTFM 13
  14. 14. • Переопределение подпрограмм • eval • Изменение таблицы символов www.mail.ru 14 Модификация кода
  15. 15. use Data::Dumper; $Data::Dumper::Useqq = 1; { no warnings 'redefine'; package Data::Dumper; sub Data::Dumper::qquote { my $s = shift; return "'$s'"; } } www.mail.ru 15 Переопределение
  16. 16. package Wrapper; sub make_accessors { my $package = caller(0); for (@_) { eval qq{ package $package; sub $_ { my $self = shift; $self->{$_} = $_[0] if @_; return $self->{$_}; } }; } } www.mail.ru 16 eval
  17. 17. package Test; use Wrapper; sub new { return bless {}, shift; } Wrapper::make_accessors( qw(name age) ); package main; use Test; my $obj = Test->new; $obj->name('Ann'); say $obj->name; www.mail.ru 17 eval
  18. 18. • Какие-то проблемы? • Методы runtime- кодогенерации • Таблица символов: матчасть • От теории к практике • Как не выстрелить себе в ногу • RTFM 18
  19. 19. • Таблица символов – это хэш www.mail.ru 19 Таблица символов • Ключи – глобальные переменные и подпрограммы • Значения - тайпглобы %PackageName:: %main::
  20. 20. www.mail.ru 20 Таблица символов package Test; our $data = 'test'; our @data = qw(1 2 3); our %data = (key1 => 'value1', key2 => 'value2'); sub data { return 0; } package main; say $Test::{$_} for keys %Test::; *Test::data my $fh = *FH;
  21. 21. *glob{PACKAGE} имя пакета *glob{NAME} имя элемента (переменной или функции) *glob{SCALAR} ссылка на значение-скаляр *glob{ARRAY} ссылка на значение-массив *glob{HASH} ссылка на значение-хэш *glob{CODE} ссылка на подпрограмму corp.mail.ru Структура тайпглоба
  22. 22. Получение данных my $scalar = ${ *Test::data }; my %hash = %{ *Test::data }; my @array = @{ *Test::data }; &{ *Test::data }(); Запись данных *Test::data = 'new value'; *Test::data = [4, 5, 6]; *Test::data = { key3 => 'value3', key4 => 'value4‘ }; *Test::data = sub { return 1; }; www.mail.ru 22 Работа с тайпглобом
  23. 23. • Какие-то проблемы? • Методы runtime- кодогенерации • Таблица символов: матчасть • От теории к практике • Как не выстрелить себе в ногу • RTFM 23
  24. 24. package MakeAccessor; sub import { my $package = caller(0); no strict 'refs'; *{"$package::has"} = &has; } sub has ($) { my $name = shift; my $package = caller(0); no strict 'refs'; *{"$package::$name"} = sub { my $self = shift; $self->{$name} = $_[0] if @_; return $self->{$name}; }; } www.mail.ru 24 Генерация аксессоров package Test; use MakeAccessor; has 'name'; has 'age'; sub new { return bless {}, shift; } package main; my $o = Test->new; $o->name('Ann'); say $o->name;
  25. 25. package MakeAccessor; sub import { my $package = caller(0); no strict 'refs'; *{"$package::has"} = &has; } sub has ($) { my $name = shift; my $package = caller(0); no strict 'refs'; *{"$package::$name"} = sub { my $self = shift; say ((caller(0))[3]); $self->{$name} = $_[0] if @_; return $self->{$name}; }; } www.mail.ru 25 Боремся с __ANON__ MakeAccessor::__ANON__
  26. 26. www.mail.ru 26 Боремся с __ANON__ package MakeAccessor; sub import { my $package = caller(0); no strict 'refs'; *{"$package::has"} = &has; } sub has ($) { my $name = shift; my $package = caller(0); my $method = sub { local *__ANON__ = "$package::$name"; my $self = shift; $self->{$name} = $_[0] if @_; return $self->{$name}; }; no strict 'refs'; *{"$package::$name"} = $method; } Test::name
  27. 27. • Генераторы классов: десериализация, ORM • Реализация паттерна «прокси» • Тестирование: mock, stub, fake object • Хуки для подпрограмм: before, after, around • Патчи во время выполнения • Расширение функционала сторонних модулей • Синонимы для устаревших функций при рефакторинге www.mail.ru 27 От теории к практике
  28. 28. package Response; use CGI; sub new { return bless { cgi => CGI->new }, shift; } our $AUTOLOAD; sub AUTOLOAD { my ($method) = $AUTOLOAD =~ /([^:]+)$/; return if $method eq 'DESTROY'; return unless CGI->can($method); my $sub = sub { local *__ANON__ = $AUTOLOAD; my $self = shift; return $self->{cgi}->$method(@_); }; { no strict 'refs'; *{$AUTOLOAD} = $sub; }; return $sub->(@_); } www.mail.ru 28 Прокси package main; my $obj = Response->new; print $obj->header('text/html'); Content-Type:text/html; charset=ISO-8859-1
  29. 29. package Wrapper; sub make_deprecated { my $deprecated = shift; { no strict 'refs'; return if *{$deprecated}{CODE}; }; my $package = scalar caller(0); my $method = (caller(1))[3]; my $func = sub { local *__ANON__ = $deprecated; warn "$deprecated called at " . sprintf("%s (%s)", (caller)[1, 2]) . " is deprecated. Use $package::$methodn"; eval "use $package;" unless $package->can('can'); my $sub = $package->can($method); $sub->(@_); }; no strict 'refs'; *{$deprecated} = $func; } package Test1; package Test2; sub new_func { Wrapper::make_deprecated( 'Test1::old_func'); return 'test'; } package main; say Test2::new_func(); say Test1::old_func(); www.mail.ru 29 Синонимы test Test1::old_func called at test12.pl (40) is deprecated. Use Test2::Test2::new_func test
  30. 30. use Test::More; my $user_data = { ... }; { no strict 'refs'; *{'Cache::Memcached::set'} = sub { return 1; }; *{'Cache::Memcached::get'} = sub { return to_json($user_data); }; }; is_deeply($user->get_info($session_id), $user_data, 'Some test...'); www.mail.ru 30 Stub
  31. 31. package Wrapper; sub before(@&) { my ($methods, $wrapper) = @_; my $package = caller(0); for my $method (@$methods) { my $orig = $package->can($method); my $sub = sub { local *__ANON__ = "$package::$method"; $wrapper->(@_); $orig->(@_); }; no strict 'refs'; *{"$package::$method"} = $sub; } } package Test; sub test { say 'test'; } Wrapper::before [ 'test' ], sub { say 'wrapper'; }; package main; Test::test(); www.mail.ru 31 Хуки wrapper test
  32. 32. • Какие-то проблемы? • Методы runtime- кодогенерации • Таблица символов: матчасть • От теории к практике • Как не выстрелить себе в ногу • RTFM 32
  33. 33. • Прагмы strict и warnings • __ANON__ в трассировке стэка • Ссылки на переопределяемые функции • Снижение читабельности кода и повышение требований к профессиональному уровню программистов • Рост стэка при использовании хуков • Пространство имен модуля www.mail.ru 33 Проблемы кодогенерации
  34. 34. use Data::Dumper; *{Data::Dumper::Dumper} = sub { return 'Hacked!'; }; say Dumper({key1 => 1, key2 => 2}); say Data::Dumper::Dumper({key1 => 1, key2 => 2}); www.mail.ru 34 Ссылки
  35. 35. package Wrapper; our $name = 'Ann'; *{Test::test} = sub { say $name; say $Test::name; }; package Test; our $name = 'Bob'; package main; Test::test(); www.mail.ru 35 Пространство имен
  36. 36. eval • Компиляция во время выполнения • Ускоренная загрузка, возможность компиляции по запросу • Создание символов в пространстве имен нужного модуля • Неэффективная генерация большого количества похожих функций Таблица символов • Компиляция при загрузке • Проверка синтаксиса компилятором при запуске • Высокая эффективность повторного использования генераторов • Невозможность создания символов в пространстве имен нужного модуля, если имя последнего не известно во время компиляции www.mail.ru 36
  37. 37. • perlmod: Symbol tables • perlref • perldata: Typeglobs and Filehandlers • Sriram Srinivasan.Advanced Perl Programming • Modern Perl (http://modernperlbooks.com) www.mail.ru 37 Что почитать
  38. 38. Шишкина Елена Владимировна e.shishkina@corp.mail.ru

×