38
Динамический код: модифицируем таблицу символов во время выполнения

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

Embed Size (px)

DESCRIPTION

Видео: http://video.mail.ru/corp/p.scherbinin/6/7.html Tаблица символов — это только небольшой шаг в мир внутреннего устройства Perl, но и он открывает программисту огромные возможности: — Runtime-кодогенерация. — Генерация по запросу. — Изменение кода сторонних модулей на лету и многое другое.

Citation preview

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

Динамический код:модифицируем таблицу символов во время

выполнения

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

• Какие-то проблемы?• Методы runtime-

кодогенерации• Таблица символов:

матчасть• От теории к практике• Как не выстрелить себе

в ногу• RTFM

2

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

• Конструкторы• Методы-аксессоры• Идентичная предварительная обработка

данных• Похожие по функционалу функции с

небольшими отличиями

corp.mail.ru

Повторяющийся код

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

www.mail.ru 4

package Foo;

sub new {

return bless {}, shift;

}

 

package Bar;

sub new {

return bless {}, shift;

}

Конструкторы

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

www.mail.ru 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};

}

Аксессоры

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

www.mail.ru 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;

...

}

Предварительная обработка

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

www.mail.ru 7

sub error {

my $message = shift;

my ($package, $line, $sub) = (caller(0))[0, 2, 3];

print $log scalar localtime, "ERROR: ${package}::$sub ($line): $message\n";

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): $message\n";

}

 

sub info { ... }

sub warning { ... }

Похожие функции

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

• Потеря времени на перепечатывание/копирование

• Ошибки из-за невнимательности• Трудоемкость сопровождения

corp.mail.ru

Проблемы повторяющегося кода

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

• Ошибки в реализации• Отсутствие поддержки кириллицы• Недостаточный функционал

corp.mail.ru

Сторонние модули

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

www.mail.ru 10

print Dumper {test => 'Тестовая строка'};

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}" };

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

www.mail.ru 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);

};

Решение: CPAN

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

www.mail.ru 12

• Необходимость доказательства целесообразности

• Замусоривание системы• Замусоривание блоков use в коде• Снижение производительности• Увеличение времени компиляции• Расход памяти• Уменьшение контроля над кодом («чужой

код»)

Проблемы использования сторонних модулей

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

• Какие-то проблемы?• Методы runtime-

кодогенерации• Таблица символов:

матчасть• От теории к практике• Как не выстрелить себе

в ногу• RTFM

13

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

www.mail.ru 14

• Переопределение подпрограмм• eval• Изменение таблицы символов

Модификация кода

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

www.mail.ru 15

use Data::Dumper;

 

$Data::Dumper::Useqq = 1;

{

no warnings 'redefine';

package Data::Dumper;

sub Data::Dumper::qquote {

my $s = shift;

return "'$s'";

}

}

Переопределение

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

www.mail.ru 16

package Wrapper;sub make_accessors { my $package = caller(0); for (@_) { eval qq{ package $package; sub $_ { my \$self = shift; \$self->{$_} = \$_[0] if \@_; return \$self->{$_}; } }; }} 

eval

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

www.mail.ru 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;

 

eval

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

• Какие-то проблемы?• Методы runtime-

кодогенерации• Таблица символов:

матчасть• От теории к практике• Как не выстрелить себе

в ногу• RTFM

18

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

www.mail.ru 19

• Таблица символов – это хэш

Таблица символов

• Ключи – глобальные переменные и подпрограммы

• Значения - тайпглобы

%PackageName::

%main::

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

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;

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

Слот Назначение

*glob{PACKAGE} имя пакета

*glob{NAME} имя элемента (переменной или функции)

*glob{SCALAR} ссылка на значение-скаляр

*glob{ARRAY} ссылка на значение-массив

*glob{HASH} ссылка на значение-хэш

*glob{CODE} ссылка на подпрограмму

corp.mail.ru

Структура тайпглоба

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

www.mail.ru 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; };

 

Работа с тайпглобом

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

• Какие-то проблемы?• Методы runtime-

кодогенерации• Таблица символов:

матчасть• От теории к практике• Как не выстрелить себе

в ногу• RTFM

23

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

www.mail.ru 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};

};

}

Генерация аксессоров

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;

 

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

www.mail.ru 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};

};

}

 

Боремся с __ANON__

MakeAccessor::__ANON__

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

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

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

www.mail.ru 27

• Генераторы классов: десериализация, ORM• Реализация паттерна «прокси»• Тестирование: mock, stub, fake object• Хуки для подпрограмм: before, after, around• Патчи во время выполнения• Расширение функционала сторонних модулей• Синонимы для устаревших функций при

рефакторинге

От теории к практике

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

www.mail.ru 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->(@_);}

Прокси

package main;

my $obj = Response->new;

print $obj->header('text/html');

 

Content-Type: text/html; charset=ISO-8859-1

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

www.mail.ru 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\::$method\n"; 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(); 

Синонимы

testTest1::old_func called at test12.pl (40) is deprecated. Use Test2::Test2::new_functest

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

www.mail.ru 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...');

 

Stub

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

www.mail.ru 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(); 

Хуки

wrappertest

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

• Какие-то проблемы?• Методы runtime-

кодогенерации• Таблица символов:

матчасть• От теории к практике• Как не выстрелить себе

в ногу• RTFM

32

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

www.mail.ru 33

• Прагмы strict и warnings• __ANON__ в трассировке стэка• Ссылки на переопределяемые функции• Снижение читабельности кода и повышение

требований к профессиональному уровню программистов

• Рост стэка при использовании хуков• Пространство имен модуля

Проблемы кодогенерации

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

www.mail.ru 34

use Data::Dumper;

 

*{Data::Dumper::Dumper} = sub {

return 'Hacked!';

};

 

say Dumper({key1 => 1, key2 => 2});

say Data::Dumper::Dumper({key1 => 1, key2 => 2});

 

Ссылки

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

www.mail.ru 35

package Wrapper;

our $name = 'Ann';

 

*{Test::test} = sub {

say $name;

say $Test::name;

};

 

package Test;

our $name = 'Bob';

 

package main;

Test::test();

 

Пространство имен

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

www.mail.ru 36

eval

• Компиляция во время выполнения

• Ускоренная загрузка, возможность компиляции по запросу

• Создание символов в пространстве имен нужного модуля

• Неэффективная генерация большого количества похожих функций

Таблица символов

• Компиляция при загрузке• Проверка синтаксиса

компилятором при запуске• Высокая эффективность

повторного использования генераторов

• Невозможность создания символов в пространстве имен нужного модуля, если имя последнего не известно во время компиляции

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

www.mail.ru 37

• perlmod: Symbol tables• perlref• perldata: Typeglobs and

Filehandlers• Sriram Srinivasan. Advanced

Perl Programming• Modern Perl

(http://modernperlbooks.com)

Что почитать

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

Шишкина Елена Владимировна

[email protected]