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

Perl: Symbol table

Embed Size (px)

DESCRIPTION

Слайды к докладу на конференции Moscow.pm 04.04.2013 Статья по мотивам доклада: http://blog.miralita.me/?p=81

Citation preview

Page 1: Perl: Symbol table

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

выполнения

Page 2: Perl: Symbol table

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

• Методы runtime-

кодогенерации

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

матчасть

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

• Как не выстрелить себе

в ногу

• RTFM

2

Page 3: Perl: Symbol table

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

• Методы-аксессоры

• Идентичная предварительная обработка

данных

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

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

corp.mail.ru

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

Page 4: Perl: Symbol table

package Foo;

sub new {

return bless {}, shift;

}

package Bar;

sub new {

return bless {}, shift;

}

www.mail.ru 4

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

Page 5: Perl: Symbol table

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

Аксессоры

Page 6: Perl: Symbol table

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

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

обработка

Page 7: Perl: Symbol table

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 { ... }

www.mail.ru 7

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

Page 8: Perl: Symbol table

• Потеря времени на

перепечатывание/копирование

• Ошибки из-за невнимательности

• Трудоемкость сопровождения

corp.mail.ru

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

Page 9: Perl: Symbol table

• Ошибки в реализации

• Отсутствие поддержки кириллицы

• Недостаточный функционал

corp.mail.ru

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

Page 10: Perl: Symbol table

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

};

Page 11: Perl: Symbol table

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

Page 12: Perl: Symbol table

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

целесообразности

• Замусоривание системы

• Замусоривание блоков use в коде

• Снижение производительности

• Увеличение времени компиляции

• Расход памяти

• Уменьшение контроля над кодом («чужой

код»)

www.mail.ru 12

Проблемы использования

сторонних модулей

Page 13: Perl: Symbol table

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

• Методы runtime-

кодогенерации

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

матчасть

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

• Как не выстрелить себе

в ногу

• RTFM

13

Page 14: Perl: Symbol table

• Переопределение подпрограмм

• eval

• Изменение таблицы символов

www.mail.ru 14

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

Page 15: Perl: Symbol table

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

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

Page 16: Perl: Symbol table

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

Page 17: Perl: Symbol table

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

Page 18: Perl: Symbol table

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

• Методы runtime-

кодогенерации

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

матчасть

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

• Как не выстрелить себе

в ногу

• RTFM

18

Page 19: Perl: Symbol table

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

www.mail.ru 19

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

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

подпрограммы

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

%PackageName::

%main::

Page 20: Perl: Symbol table

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: Perl: Symbol table

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

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

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

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

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

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

corp.mail.ru

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

Page 22: Perl: Symbol table

Получение данных

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

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

Page 23: Perl: Symbol table

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

• Методы runtime-

кодогенерации

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

матчасть

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

• Как не выстрелить себе

в ногу

• RTFM

23

Page 24: Perl: Symbol table

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;

Page 25: Perl: Symbol table

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__

Page 26: Perl: Symbol table

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: Perl: Symbol table

• Генераторы классов: десериализация, ORM

• Реализация паттерна «прокси»

• Тестирование: mock, stub, fake object

• Хуки для подпрограмм: before, after, around

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

• Расширение функционала сторонних модулей

• Синонимы для устаревших функций при

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

www.mail.ru 27

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

Page 28: Perl: Symbol table

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

Page 29: Perl: Symbol table

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();

www.mail.ru 29

Синонимы

test

Test1::old_func called at test12.pl

(40) is deprecated. Use

Test2::Test2::new_func

test

Page 30: Perl: Symbol table

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

Page 31: Perl: Symbol table

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

Page 32: Perl: Symbol table

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

• Методы runtime-

кодогенерации

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

матчасть

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

• Как не выстрелить себе

в ногу

• RTFM

32

Page 33: Perl: Symbol table

• Прагмы strict и warnings

• __ANON__ в трассировке стэка

• Ссылки на переопределяемые функции

• Снижение читабельности кода и повышение

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

программистов

• Рост стэка при использовании хуков

• Пространство имен модуля

www.mail.ru 33

Проблемы

кодогенерации

Page 34: Perl: Symbol table

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

Ссылки

Page 35: Perl: Symbol table

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

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

Page 36: Perl: Symbol table

eval

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

выполнения

• Ускоренная загрузка,

возможность компиляции по

запросу

• Создание символов в

пространстве имен нужного

модуля

• Неэффективная генерация

большого количества

похожих функций

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

• Компиляция при загрузке

• Проверка синтаксиса

компилятором при запуске

• Высокая эффективность

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

генераторов

• Невозможность создания

символов в пространстве

имен нужного модуля, если

имя последнего не известно

во время компиляции

www.mail.ru 36

Page 37: Perl: Symbol table

• perlmod: Symbol tables

• perlref

• perldata: Typeglobs and

Filehandlers

• Sriram Srinivasan. Advanced

Perl Programming

• Modern Perl

(http://modernperlbooks.com)

www.mail.ru 37

Что почитать

Page 38: Perl: Symbol table

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

[email protected]