Motivation
tie
overload
Transparente Objekte mit tie
+overload
Verständnis von tie
Verständnis von overload
Verständnis von AUTOLOAD
Ideen für Objekte ausserhalb von Perl
Automatisierung von Firefox
Firefox ist in Javascript geschrieben
Zugriff aus Perl
... nicht ueber Javascript
1: Perl <-> FireFox <-> Website
1: Perl <-> FireFox <-> Website
Javascript:
1: alert( window.tab 2: .linkedBrowser 3: .document 4: .innerHTML)
1: Perl <-> FireFox <-> Website
Javascript:
1: alert( window.tab 2: .linkedBrowser 3: .document 4: .innerHTML)
Perl:
1: print $window->{tab} 2: ->{linkedBrowser} 3: ->{document} 4: ->{innerHTML}
1: Perl <-> FireFox <-> Website
Javascript:
1: alert( window.tab 2: .linkedBrowser 3: .document 4: .innerHTML)
Perl:
1: print $window->{tab} 2: ->{linkedBrowser} 3: ->{document} 4: ->{innerHTML}
$window->
...
Datenzugriffe
1: $window->{title}
Datenverwendung
1: $browser = $window->{linkedBrowser}
Definition
Beispiel
Vorteile / Nachteile
tie
leitet den Zugriff auf eine Variable um auf Perl Code.
tie
leitet den Zugriff auf eine Variable um auf Perl Code.
Syntax
1: tie %myhash, 'Meine::Tie::Klasse', ... 2: 3: tie @lines, 'Tie::File' => 'einedatei.txt'; 4: 5: tie $zeilen, 'Tie::Cycle' => 1,5;
Problemstellung:
(Web)Server-Anwendung mit Usern
1: user_id | name | seit | zuletzt 2: 1 | admin | 1.1.1970 | 6.06.2010 3: 2 | anonym | 1.1.1970 | - 4: 3 | Max | 1.1.1970 | 6.06.2010 5: ...
1: my $res = $dbh->selectall_arrayref(<<'SQL', 2: {Slice => {}}); 3: SELECT * 4: FROM users 5: SQL 6: my %users = map { $_->{user_id} => { %{ $_ } }} 7: @{ $res }; 8: 9: __END__
1: my $res = $dbh->selectall_arrayref(<<'SQL', 2: {Slice => {}}); 3: SELECT * 4: FROM users 5: SQL 6: my %users = map { $_->{user_id} => { %{ $_ } }} 7: @{ $res }; 8: 9: __END__ 10: %users = ( 11: '1' => { 12: 'seit' => '1970-01-01 00:00:01', 13: 'name' => 'admin', 14: 'zuletzt' => '1970-01-01 00:00:01', 15: 'user_id' => '1' 16: }, 17: ...
1: my $res = $dbh->selectall_arrayref(<<'SQL', {Slice => {}}); 2: SELECT * 3: FROM users 4: SQL 5: my %users = map { $_->{user_id} => { %{ $_ } }} 6: @{ $res };
Nachteile:
Langsam
Viele Daten abgefragt für wenig Nutzung
Vorteile
Exisitiert
1: my $res = $dbh->selectall_arrayref(<<'SQL', {Slice => {}}); 2: SELECT * 3: FROM users 4: WHERE user_id = ? 5: SQL
Vorteile
Weniger Datentransfer
Nachteile:
Existiert nicht
Umstellung des Codes von Hash-Zugriff auf Prozedur
tie
Idee: Ersetzen des Prozeduraufrufs durch ein Hash
Zugriff auf
1: $users{ 1 }
... soll
1: fetch_user(1)
aufrufen
tie
Idee: Ersetzen des Prozeduraufrufs durch ein Hash:
1: tie %users, 'MyApp::Tie::Users', ... 2: 3: package MyApp::Tie::Users; 4: sub TIEHASH { ... } 5: sub FETCH { ... } 6: sub STORE { ... } 7: sub FIRSTKEY { ... } 8: sub NEXTKEY { ... }
tie
Idee: Ersetzen des Prozeduraufrufs durch ein Hash:
1: tie %users, 'MyApp::Tie::Users', ... 2: sub TIEHASH { ... }
Deklaration / Konstruktor
tie
Idee: Ersetzen des Prozeduraufrufs durch ein Hash:
1: tie %users, 'MyApp::Tie::Users', ... 2: sub TIEHASH { ... } 3: sub FETCH { ... }
Methode für Lesezugriff
tie
Idee: Ersetzen des Prozeduraufrufs durch ein Hash:
1: tie %users, 'MyApp::Tie::Users', ... 2: sub TIEHASH { ... } 3: sub FETCH { ... } 4: sub STORE { ... }
Methode für Schreibzugriff
tie
Idee: Ersetzen des Prozeduraufrufs durch ein Hash:
1: tie %users, 'MyApp::Tie::Users', ... 2: sub TIEHASH { ... } 3: sub FETCH { ... } 4: sub STORE { ... } 5: sub FIRSTKEY { ... } 6: sub NEXTKEY { ... }
Methoden für Schlüsselaufzählung
tie
1: sub FETCH { 2: ... 3: SELECT * 4: FROM users 5: WHERE user_id = ? 6: ... 7: }
Achtung: Dieser Ansatz geht nur bei einer vorhandenen Schlüsselspalte.
Ansonsten könnten wir Arrayreferenzen zurückliefern.
tie
1: package MyApp::Tie::Users; 2: use parent 'Tie::Hash'; 3: ...
Tie::Hash liefert das normale Verhalten eines Hashes als Grundlage
tie
1: package MyApp::Tie::Users; 2: use parent 'Tie::Hash'; 3: ... 4: sub TIEHASH { 5: my ($class,$dbh) = @_; 6: my $self = { 7: dbh => $dbh, 8: }; 9: bless $self, $class 10: }
tie
FETCH1: sub FETCH { 2: my ($self,$key) = @_; 3: my $sth = $dbh->prepare_cached(<<'SQL'); 4: SELECT * 5: FROM users 6: WHERE user_id = ? 7: SQL 8: my $res = $sth->execute($key); 9: if ($res) { 10: $res = $sth->fetchall_arrayref({}); 11: return $res->[0] 12: } 13: return undef 14: }
tie
STORE1: sub STORE { 2: my ($self,$key,$value) = @_; 3: croak "Can only store (user) hashrefs" 4: unless ref $value eq 'HASH'; 5: croak "Key '$key' inconsistent with user id '$value->{user_id}'" 6: if (defined $value->{user_id} and $key ne $value->{user_id}); 7: 8: my $sth = $self->{dbh}->prepare_cached(<<'SQL'); 9: INSERT INTO users 10: (user_id,name,seit,zuletzt) VALUES (?,?,?,?) 11: SQL 12: my $res = $sth->execute(@{ $value }{qw[user_id name seit zuletzt]}); 13: return $value 14: }
Demo
02-tiehash.pl
1: use MyApp::Tie::Users; 2: my $dbh = DBI->connect(...); 3: tie my %users, 'MyApp::Tie::Users' 4: => $dbh, 5: ; 6: print $users{2}->{name};
Leicht ein/ersetzbar
1: tie my %users, 'MyApp::Tie::Users' 2: => 'dbi:SQLite:users.sqlite', 3: 'users' 4: ;
Leicht ein/ersetzbar
1: #tie my %users, 'MyApp::Tie::Users' 2: # => 'dbi:SQLite:users.sqlite', 3: # 'users' 4: #; 5: my %users = { 2 => {name => 'Max', ...}, ... };
Fehlerquelle leicht lokalisierbar
Tests leicht implementierbar
tie
ist relativ langsam im Vergleich zum direkten
Variablenzugriff und auch im Vergleich zum Methodenaufruf
1: my $tied_obj = tied %users_tied; 2: 3: cmpthese( 4: -10, { 5: 'tied' => sub { $users_tied{ rand(6) } }, 6: 'object' => sub { $tied_obj->FETCH(rand(6)) }, 7: 'plain' => sub { $users{ rand(6) } }, 8: 'full' => sub { fetch_all_and_return() }, 9: } 10: );
05-benchmark-tiehash-plain.pl
1: Rate full tied object plain 2: full 2444/s -- -74% -75% -99% 3: tied 9309/s 281% -- -6% -96% 4: object 9886/s 304% 6% -- -95% 5: plain 215698/s 8725% 2217% 2082% --
05-benchmark-tiehash-plain.pl
1: Rate full tied object plain 2: full 2444/s -- -74% -75% -99% 3: tied 9309/s 281% -- -6% -96%
Datenmenge und ->prepare
d Statement
05-benchmark-tiehash-plain.pl
1: Rate full tied object plain 2: full 2444/s -- -74% -75% -99% 3: tied 9309/s 281% -- -6% -96% 4: object 9886/s 304% 6% -- -95%
6% für tie
1: my $max = $users{ 2 }; 2: ... 3: 4: $users{ 2 } => tied(%users)->FETCH(2) 5: Perl -> 'tie magic' -> Perl -> Datenbank -> Daten
1. Abfangen des Zugriffs
2. Methodenaufruf
3. Perl Code
tie
geht auch mit Skalaren
TIESCALAR
, STORE
, FETCH
1: sub FETCH { 'Max' }
... und Arrays
TIEARRAY
, STORE
, FETCH
tie
mit ArraysBessere Verwendung für die Userdatenbank:
1: push @users, { name => 'Max' };
tie
Tie::File - Datei als Array mit Zeilen als Array-Elementen
Tie::DBI - gerade gesehen
Tie::DBM - Hash auf Festplatte statt im Speicher
Tie::Memcached - Hash in memcached
statt im (eigenen) Speicher
<Tie::Cycle> - ...
tie
leitet den Variablenzugriff um auf Perl Code.
tie
leitet den Variablenzugriff um auf Perl Code.
Syntax
1: tie %myhash, 'MyApp::Tie::Users', ... 2: 3: tie @lines, 'Tie::File' => 'einedatei.txt'; 4: 5: tie $zeilen, 'Tie::Cycle' => 1,5;
tie
leitet den Variablenzugriff um auf Perl Code.
Syntax
1: tie %myhash, 'MyApp::Tie::Users', ... 2: 3: tie @lines, 'Tie::File' => 'einedatei.txt'; 4: 5: tie $zeilen, 'Tie::Cycle' => 1,5;
tie
ist langsamer als andere Zugriffsformen.
Tie einrichten (TIEHASH
, TIESCALAR
, TIEARRAY
)
Lesen (FETCH
)
Schreiben (STORE
)
Löschen (DELETE
)
Aufzählen (FIRSTKEY
, NEXTKEY
)
Tie::Hash
Tie::StdHash
perltie
overload
tie
overload
Das overload-Pragma leitet die Werteverwendung auf Perl-Code um.
Verschiedene Klassen von Operatoren
Interpolation
Arithmetische Operationen ( +-*/
)
Vergleichsoperationen (<=> cmp
)
Dereferenzierung ( ->
)
1: package MyApp::User; 2: use overload OPERATOR => SUBROUTINE, 3: OPERATOR => SUBROUTINE, 4: ... 5: fallback => 1;
1: package MyApp::User; 2: use overload q{""} => 'as_string' 3: ; 4: 5: sub as_string { 6: $_[0]->{name} 7: };
*
1: print $user_b ~~ 'admin';
Komplexe Zahlen (Math::Complex)
Rationale Zahlen (Math::BigRat)
Definition:
1: $user_a > $user_b
soll bedeuten "$user_a
ist älter als $user_b
"
1: @user_nach_alter = sort { $a <=> $b } @users;
1: use overload 2: '<=>' => 'cmp_timestamps' 3: 4: ; 5: 6: sub cmp_timestamps { 7: $_[0]->{seit} <=> $_[1]->{seit} 8: }
1: use overload 2: '<=>' => 'cmp_timestamps' 3: fallback => 1, # für '>', '<', '==' 4: ; 5: 6: sub cmp_timestamps { 7: $_[0]->{seit} <=> $_[1]->{seit} 8: }
1: my $ref = ..; 2: 3: $ref->[0] # Array-Referenz
1: my $ref = ..; 2: 3: $ref->[0] # Array-Referenz 4: 5: $ref->{max} # Hash-Referenz
1: $ref->... # Dereferenzierung 2: 3: use overload '@{}'; # Array 4: use overload '%{}'; # Hash 5: use overload '${}'; # Skalar 6: use overload '&{}'; # Code
Wofür?
1: sub find { 2: my ($name) = @_; 3: return MyApp::Query::Delayed->new(<<'SQL', $name); 4: SELECT * 5: FROM users 6: WHERE name LIKE ? 7: SQL 8: }
liefert eine "verzögerte" Abfrage zurück
Die Abfrage wird erst beim Aufruf von ->fetch
ausgeführt
Verwendung der verzögerten Abfrage
<07-overload.pl
>
1: ... 2: my $users_a = find('a%'); 3: $users_a->fetch(); 4: print Dumper $users_a->data->[0];
Expliziter Aufruf ist unpraktisch:
1: $users_a->fetch();
Schöner wäre ein automatischer Aufruf von ->fetch
bei Zugriff auf $users_a->[...]
.
Expliziter Aufruf bei Zugriff als Array(referenz):
1: use overload '@{}'; 2: 3: sub as_array { 4: my ($self) = @_; 5: $self->fetch; 6: $self->{data} 7: };
<08-overload-array.pl
>
1: my $max = find('Max'); 2: # Es gibt höchstens einen! 3: print $max->[0]->{name};
1: my $max = find('Max'); 2: # Es gibt höchstens einen! 3: print $max->[0]->{name};
Idee:
1: $max->{name}
soll das selbe sein, wie
1: $max->[0]->{name}
Idee:
1: $max->{name}
soll das selbe sein, wie
1: $max->[0]->{name}
Implementation
1: use overload '%{}' => 'as_hash'; 2: sub as_hash { 3: $_[0]->[0] 4: };
1: $max->{name} 2: sub as_hash { 3: $_[0]->[0] 4: };
1: $max->{name} 2: sub as_hash { 3: $_[0]->[0] 4: }; 5: sub as_array { 6: my ($self) = @_; 7: $self->fetch; 8: $self->{data} 9: };
1: $max->{name} 2: sub as_hash { 3: $_[0]->[0] 4: }; 5: sub as_array { 6: my ($self) = @_; 7: $self->fetch; 8: $self->{data} # Uhoh 9: };
1. Rebless in nicht überladene Klasse
2. Zugriff auf Hash
3. Zurückblessen
09-overload-hash.pl
1: sub __field { 2: my ($self,$field,$value) = @_; 3: my $class = ref $_[0]; 4: bless $self, __PACKAGE__ . "::Unblessed"; 5: if (@_ == 3) { 6: $self->{$field} = $value; 7: }; 8: my $res = $self->{$field}; 9: bless $self => $class; 10: $res 11: }
overload
leitet die Werteverwendung auf Perl Code um
String, ~~
Arithmetische Operationen
Vergleichsoperationen
Verwendung als Referenz
tie
overload
Transparente Objekte mit tie
+overload
Kombination der beiden Techniken:
Zugriff auf Variablen: tie
Verwendung von Werten: overload
Kombination der beiden Techniken:
Zugriff auf Variablen: tie
Verwendung von Werten: overload
Totale Kontrolle über Werte und Verwendung
1: Perl <-> FireFox <-> Website 2: 3: alert( window.tab 4: .linkedBrowser 5: .document 6: .innerHTML) 7: 8: print $window->{tab} 9: ->{linkedBrowser} 10: ->{document} 11: ->{innerHTML}
Voraussetzungen
Implementation der transparenten Objekte
Performance
Anwendungsmöglichkeiten, Ausblick
(Weitere Features)
Perl
Firefox
mozrepl
- http://github.com/bard/mozrepl
MozRepl::RemoteObject (CPAN)
Javascript String eval
Lesen und Setzen von Objekteigenschaften
Aufruf von Methoden
REPL - Read Eval Print Loop
REPL - Read Eval Print Loop
REPL - Read Eval Print Loop
Javascript Object Notation als Serialisierungsformat
Skalare
1: "Hallo", 123
Arrays
1: [ ... ]
Hashes
1: { foo: "bar", baz: "blurble" }
Kombinationen aus allen dreien
Einschränkungen
Objekte sind Hashes
Keine Filehandles
Keine Objekttypen
Serielle Kommunikation
Serielle Kommunikation
Perl initiiert die Kommunikation
Perl initiiert die Kommunikation
Perl schickt den Befehl an Javascript
Perl initiiert die Kommunikation
Perl schickt den Befehl an Javascript
Javascript verarbeitet den Befehl
Perl initiiert die Kommunikation
Perl schickt den Befehl an Javascript
Javascript verarbeitet den Befehl
Javascript sendet das Ergebnis zurück
Perl initiiert die Kommunikation
Perl schickt den Befehl an Javascript
Javascript verarbeitet den Befehl
Javascript sendet das Ergebnis zurück
Perl verarbeitet das Ergebnis
Annehmen des Befehls und der Parameter
Verpacken der Parameter in JSON
Senden des Befehls und der Parameter an Javascript
... Javascript sendet Ergebnis
Auspacken der Antwort aus JSON
Zurückliefern der ausgepackten Datenstruktur
Lesen eines Befehls
Wandeln von JSON in Javascript Struktur
Ausführen des Befehls
Wandeln des Ergebnisses in Javascript Struktur
Rückgabe an Perl
Pädagogische Vereinfachungen
Knallharte Lügen
1: window
window
ist ein Objekt
Objekte sind nicht über JSON serialisierbar
Und jetzt?
1: $window->open() <==> window.open 2: 3: $window->{title} <==> window.title 4: 5: $window->[3] <==> window[3]
1: $window->open() <==> window.open 2: 3: $window->{title} <==> window.title 4: 5: $window->[3] <==> window[3]
AUTOLOAD
, overload
+ tie
1: $window->open()
1: $window->open()
AUTOLOAD
1: sub AUTOLOAD { 2: my ($self,@args) = @_; 3: my $method = $AUTOLOAD; 4: $self->bridge->callMethod($self,$method,@args); 5: };
1: $window->open()
AUTOLOAD
1: sub AUTOLOAD { 2: my ($self,@args) = @_; 3: my $method = $AUTOLOAD; 4: $self->bridge->callMethod($self,$method,@args); 5: }; 6: 7: JS: repl.callMethod(repl.getLink(42),'open'); 8: 9: JS: return JSON.stringify( repl.wrapResult( 10: repl.callMethod(repl.getLink(42),'open')));
AUTOFETCH
Gibt's nicht
overload
und tie
overload
- Wertverwendung auf Routine umleiten
tie
- Variablenzugriff auf Routine umleiten
overload
liefert getie
te Variablen zurück
tie
reicht Daten an Firefox durch und verpackt die Daten
in Objekten mit overload
overload
overload
Erlaubt es, Objektzugriff auf eine Subroutine umzugbiegen
use overload '%{}' => '__as_hash'
1: sub __as_hash { 2: my ($self) = @_; 3: tie my %tie, 4: 'MozRepl::RemoteObject::TiedHash', $self; 5: %tie 6: }
tie
erlaubt es, Variablen wie Objekte zu behandeln.
1: Zugriff auf Variable -> Objektmethode 2: $window{ title } -> FETCH(%window, 'title') 3: $window{ title }='Hallo' -> STORE(%window, 'title', 'Hallo' )
overload
und tie
für HashesBeispiel
1: $window->{title}
overload
und tie
für HashesBeispiel
1: $window->{title}
==>
1: $window->__as_hash()->FETCH('title')
overload
und tie
für HashesBeispiel
1: $window->{title}
==>
1: $window->__as_hash()->FETCH('title') 2: return getAttr($id, 'title') 3: return $firefox->eval(" 4: repl.wrapResult(repl.getLink($id)['title']) 5: ");
overload
und tie
für Arraysuse overload '@{}' => '__as_array'
1: sub __as_array { 2: my ($self) = @_; 3: tie my @tie, 4: 'MozRepl::RemoteObject::TiedArray', $self; 5: @tie 6: }
tie
1: Zugriff auf Variable -> Objektmethode 2: $array[ 3 ] -> FETCH(@array, 3) 3: $array[ 3 ] = 'Hallo' -> STORE(@array, 3, 'Hallo' )
overload
und tie
zusammen1: overload tie 2: @{} Array-Reflektor auf getAttr / setAttr 3: %{} Hash-Reflektor auf getAttr / setAttr
1: print $window->{title}; 2: $window->{tab} 3: ->{linkedBrowser} 4: ->{document} 5: ->{innerHTML} = 'Hello World';
10-demo-firefox.pl
1: var open = window.open; 2: open('http://corion.net'); 3: // window.open('http://corion.net')
1: my $open = $window->{open}; 2: $open->('http://corion.net')
1: my $open = $window->{open}; 2: $open->('http://corion.net')
Overload auch für Funktionsaufrufe:
use overload '&{}' => '__as_code'
1: sub __as_code { 2: my ($self) = @_; 3: return sub { 4: $self->bridge->applyFunc($self,@_) 5: } 6: } 7: 8: my $open = $window->{open}; 9: $open->('http://corion.net')
Objektbrücke sammelt JS Objekte
Referenzen in JS bleiben ewig am Leben
Speicherlöcher
Bild: (Zerbrochene Kette)
Bild: (Zerbrochene Kette)
Bild: (Zerbrochene Kette)
Bild: (Zerbrochene Kette)
Verwaltung der Objekte
wrapResult
1: { result: "1", type: "object" }
getLink
1: [ id -> object ]
link
1: [ id <- object ]
breakLink
1: [ id -> undefined ]
Jedes Lesen eines Attributs läuft einmal durch die ganze Kette
Hohe Latenz
Jedes Schreiben
Jeder Funktionsaufruf
Jede Freigabe eines Perl Objekts
Beispiel:
1: $window->{document}->{body}->{title}
3 Lesezugriffe
.document
.body
.title
2 Objektfreigaben
.document
.body
HTTP::Cookies::MozRepl - Aufzählen der Cookies
1: while ($iter->hasNext) { 2: $iter = $iter->getNext(); 3: 4: }
HTTP::Cookies::MozRepl - Aufzählen der Cookies
1: while ($iter->hasNext) { # 1x hasNext() aufrufen 2: $iter = $iter->getNext(); # 1x getNext() aufrufen 3: # 1x altes $iter freigeben 4: }
3 Anfragen pro Cookie!
Objekte müssen nicht tatsächlich in Perl sein
tie
+ overload
kann Objektbäume vorspiegeln
overload
fängt alle Zugriffsarten ab
tie
liefert passende Werte zurück
"Richtiges"/kooperatives Event-System (AnyEvent)
Integration mit XUL::Gui
Fragen?
Stasi 2.0, "Schäublone", Dirk Adler, http://www.dataloo.de/stasi-20-525.html
Perl Onion, The Perl Foundation
1: $window->{onload} = sub { 2: print "Seite geladen"; 3: }
Demo
Wie funktioniert das?
1: $window->{onload} = sub { 2: print "Seite geladen"; 3: }
Javascript hat Callbacks und anonyme Funktionen.
Perl-Funktionen bekommen IDs (cbid
)
Javascript-Proxy-Funktion zu IDs
Javascript-Proxy-Funktion senden Event an Perl
Perl führt den Callback aus
1: sub { ... } -> [Brücke] (callback id) --> Javascript Proxy 2: | 3: window.onload = proxy; 4: ~~~ ~~~ 5: window.onload(); 6: | 7: v 8: eventQueue.push([arguments,e]) 9: | 10: v 11: [Javascript] eventQueue <--------+ 12: 13: $repl->poll() ---> 14: <--- { result:{}, events: [...] } 15: $repl 16: ->dispatch_events();
1: $document->{title} 2: 3: M:R:Instance [ ->__as_hash, M:R:TiedHash -> ] 4: -> MozRepl::RemoteObject [ repl.getAttr, $document, 'title' ] 5: -> JSON [ repl.getAttr, repl.getLink(42), 'title' ] 6: -> Net::Telnet -> mozrepl 7: -> JS Brücke, storedObjects[] [ return repl.getAttr(repl.getLink(42),'title') ] 8: -> Javascript [ document.title ] 9: -> JS Brücke [ JSON.stringify(repl.wrapResults( document.title )) ] 10: [ { result: 'Hello', type: 'string' } ] 11: -> mozrepl -> Net::Telnet 12: -> "hello"
1: my $doc1 = $window->{document}; # id 43 2: 3: my $doc2 = $window->{document}; # id 44 4: 5: $doc1 == $doc2 ?
objectIdentity
Javascript ===
Operator
Javascript Validation
Reagieren auf Adressbalken
Filtern von eBay-Auktionen
Nicht nur für FireFox
Thunderbird (Mailablage)
ChatZilla (irc)
1: $window->{linkedBrowser}->{tab}{document}->{title} 2: 3: getLink($window) 4: getAttr('linkedBrowser') 5: breakLink($window) 6: getAttr('tab') 7: breakLink($linkedBrowser) 8: getAttr('document') 9: ...
Lokale Implementation
1: ->__dive(qw( linkedBrowser tab document )); 2: 3: repl.dive(...)
Schönere Fehlermeldungen
1: $window->{document} 2: 3: M:R:Instance [ ->__as_hash, M:R:TiedHash -> ] 4: -> MozRepl::RemoteObject [ repl.getAttr, $document, 'title' ] 5: -> JSON [ repl.getAttr, repl.getLink(42), 'title' ] 6: -> Net::Telnet -> mozrepl 7: -> JS Brücke, storedObjects[] [ return repl.getAttr(repl.getLink(42),'title') ] 8: -> Javascript [ document.title ] 9: -> JS Brücke [ JSON.stringify(repl.wrapResults( document.title )) ] 10: [ { result: 43, type: 'HTMLDocument' } ] 11: -> mozrepl -> Net::Telnet 12: -> bless 43, 'M:R:I'
??? Gutes Beispiel, das direkte Callbacks demonstriert
z.B. Enumeration?
??? Gutes Beispiel, das asynchrone Callbacks demonstriert
z.B. Timer?