HHVMBLOG DOCS GITHUB HACKThe Journey of a Thousand BytecodesPosted on  dịch - HHVMBLOG DOCS GITHUB HACKThe Journey of a Thousand BytecodesPosted on  Việt làm thế nào để nói

HHVMBLOG DOCS GITHUB HACKThe Journe

HHVM
BLOG DOCS GITHUB HACK
The Journey of a Thousand Bytecodes

Posted on October 3, 2014 by Sara Golemon

Compilers are fun. They take nice, human readable languages like PHP or Hack and turn them into lean, mean, CPU executin’ turing machines. Some of these are simple enough a CS student can write one up in a weekend, some are the products of decades of fine tuning and careful architecting. Somewhere in that proud tradition stands HHVM; In fact it’s several compilers stacked in an ever-growing chain of logic manipulation and abstractions. This article will attempt to take the reader through the HHVM compilation process from PHP-script to x86 machine code, one step at a time.

Step One: Lexing

The first step of our journey should be familiar to many PHP users since it’s actually exposed to user space as the token_get_all() function. In this phase, we reduce a human-readable PHP script to a series of tokens; Numeric identifiers which the compiler will be able to recognize as having some intrinsic meaning. For this, HHVM uses a tool called Flex to generate a program from a meta definition at hphp/parser/hphp.ll.

The several rules in this file describe the how to recognize token-words. For example, encountering “echo” inside of a
0/5000
Từ: -
Sang: -
Kết quả (Việt) 1: [Sao chép]
Sao chép!
HHVM
BLOG DOCS GITHUB HACK
The Journey of a Thousand Bytecodes

Posted on October 3, 2014 by Sara Golemon

Compilers are fun. They take nice, human readable languages like PHP or Hack and turn them into lean, mean, CPU executin’ turing machines. Some of these are simple enough a CS student can write one up in a weekend, some are the products of decades of fine tuning and careful architecting. Somewhere in that proud tradition stands HHVM; In fact it’s several compilers stacked in an ever-growing chain of logic manipulation and abstractions. This article will attempt to take the reader through the HHVM compilation process from PHP-script to x86 machine code, one step at a time.

Step One: Lexing

The first step of our journey should be familiar to many PHP users since it’s actually exposed to user space as the token_get_all() function. In this phase, we reduce a human-readable PHP script to a series of tokens; Numeric identifiers which the compiler will be able to recognize as having some intrinsic meaning. For this, HHVM uses a tool called Flex to generate a program from a meta definition at hphp/parser/hphp.ll.

The several rules in this file describe the how to recognize token-words. For example, encountering “echo” inside of a
echo $a + $b;
If we ran this through token_get_all(), we’d get the following sequence of tokens:

T_OPEN_TAG // This represents the "T_WHITESPACE("
")
T_ECHO
T_WHITESPACE(" ")
T_VARIABLE("$a")
T_WHITESPACE(" ")
'+'
T_WHITESPACE(" ")
T_VARIABLE("$b")
';'
Notice that simple, single character tokens aren’t given a name, they’re simply passed through as the characters which they are. Now that we have a set of machine-readable tokens, we can start to build expressions from them.

Step Two: Parsing

The rules found in hphp/parser/hphp.y are used by Bison to create a parser engine. This piece of the compiler reads in tokens, fed to it by the lexer, to build an Abstract Syntax Tree (AST); A representation of the expressions contained in your PHP script.

Considering a slightly more complex piece of script:

echo "Does 1+2 equal 3?";
if ((1+2) == 3) {
echo "Yes it does!";
} else {
echo "No it doesn't...";
}
We generate an AST which looks something like the following:

EchoStatement
ConstantExpression("Does 1+2 equal 3?")
IfBranchStatement
BinaryOpExpression(EQUAL)
BinaryOpExpression(ADD)
ConstantExpression(1)
ConstantExpression(2)
ConstantExpression(3)
EchoStatement
ConstantExpression("Yes it does!")
EchoStatement
ConstantExpression("No it doesn't...")
Here we can see that our disorganized tokens have been grouped together such that logical expressions are nested into groups the way we, as humans, would expect to make sense of them. This is at the heart of the definition of the language and it’s what determines everything from order of operations (precedence) to the association of one statement into another.

Step Two and a Half: Optimization

I call this two and a half, because we’re not really changing the representation of the original script’s code at this point. Rather, we’re reducing expressions which can be simplified before the code is ever run (also, some of this happens in step 3). For example, we can evaluate “1+2″ to 3, and our AST becomes:

EchoStatement
ConstantExpression("Does 1+2 equal 3?")
IfBranchStatement
BinaryOpExpression(EQUAL)
ConstantExpression(3)
ConstantExpression(3)
EchoStatement
ConstantExpression("Yes it does!")
EchoStatement
ConstantExpression("No it doesn't...")
Then evaluate “3 == 3″ to “true”:

EchoStatement
ConstantExpression("Does 1+2 equal 3?")
IfBranchStatement
ConstantExpression(true)
EchoStatement
ConstantExpression("Yes it does!")
EchoStatement
ConstantExpression("No it doesn't...")
Now we know that we’ll always take the truth branch and never take the false branch, so:

EchoStatement
ConstantExpression("Does 1+2 equal 3?")
EchoStatement
ConstantExpression("Yes it does!")
Which of course can be reduced to a single EchoStatement. This script was obviously a bit contrived to show off this optimizer stage, but it’s worth seeing what’s possible.

Question: What’s the difference between a Statement and an Expression?
Answer: Statements are terminal, they have no output value to be consumed by later nodes in the AST, and must therefor appear only as top-level siblings in an HHVM AST. Expressions produce a value as a result, and may, as such, be used as input to other expressions or statements.

Step 3: Compilation to Bytecode

The last stage of our compiler’s “front end” brings us through hphp/compiler/analysis/emitter.cpp where we turn that AST into bytecodes which can be executed by HHVM’s interpreter. We do this by “visiting” each expression from the base of the tree upwards, with each expression describing itself as a series of bytecodes.

For example, an echo statement has one or more children which, by virtue of being expressions themselves, may in turn be made up of other expressions. For each child expression of an EchoStatement we first visit the expression, which will produce a number of bytecodes resulting in a single value having been pushed onto the “stack”. We then emit a “Print” bytecode which will pop that value, outputting its contents, then push it’s own result value of int(1). Because Echo is a Statement, however, and therefore must not have an output value, we immediately pop it back off using PopC.

Question: Why push a value to the stack if Echo is a Statement, and not an Expression?
Answer: Because Echo cheats by reusing the same bytecode meant for the print() expression, which if you’ll recall, does have a return value. The PopC instruction throws that away as unneeded.

Question: Isn’t that inefficient?
Answer: Yes, but only in interp mode, this disappears by the time we reach the JIT.

Let’s take a look at that in practice:

echo "Hello ", $name, "
";
Produces an AST for an EchoStatement with three child expressions:

EchoStatement
ConstantExpression("Hello ")
VariableExpression($name)
ConstantExpression("
")
Which visits the EchoStatement, who in turns visits each child:

String "Hello " // Push "Hello " onto the stack
Print // Consume "Hello " from the stack, and push int(1)
PopC // Consume int(1) and throw it away
CGetL 0 // Fetch Local#0 ($name) from the symbol table and push it onto the stack
Print // etc... etc... etc...
PopC
String "
"
Print
PopC
Generating Bytecode Dumps

To see byte codes for a given script, similar to how PHP allows you to view Opcodes via VLD, you have two options. The first is to run it through HHVM as usual with the -vEval.DumpBytecode=1 option. This format is designed for human readability and is meant primarily for runtime development and debugging. For example, a simple Hello World program:

echo "Hello World
";
Becomes:

$ hhvm -vEval.DumpBytecode=1 /tmp/hello-world.php

Pseudo-main at 0
maxStackCells: 1
numLocals: 0
numIterators: 0

// line 2
0: String "Hello World
"
5: Print
6: PopC
7: Int 1
16: RetC

Pseudo-main at 0
maxStackCells: 1
numLocals: 0
numIterators: 0
If you’re wondering about the Int 1/RetC at the end, that’s the implicit “return 1;” which comes at the end of every file (which doesn’t explicitly return something) so that the include expression has a return value. Remember, the “global scope” acts like a nameless function which is invoked implicitly on startup, and shares its variable scope with all other top-level psuedo-main functions.

The second option is nearly identical in invocation, but produces noticeably different output:

$ hhvm -vEval.DumpHhas=1 /tmp/hello.php

.filepath "/tmp/hello.php";

.main {
String "Hello World
"
Print
PopC
Int 1
RetC
}
This is actually HHVM’s third language (PHP and Hack being the first two), HipHop Assembly. This hidden language, which is normally only recognized in Systemlib files, allows developers to bypass the first two steps of compilation and create raw bytecodes. You can find an existing example of this (hphp/system/php/array_map.hhas) in the implementation of array_map() where HHVM uses a custom bytecode explicitly designed to make the function more efficient.

For more on the meaning and uses of all of HHVM bytecodes, see hphp/doc/bytecode.specification.

Note: You should not develop applications or libraries in HHAS. The syntax of HHAS and the behavior of specific bytecodes are NOT GUARANTEED between releases of HHVM and there is almost never any benefit to be had over writing scripts in normal PHP. The coverage in this article is meant for informational purposes only.

Step 3.5: HHBBC Optimization

The optimizations we performed in step 2.5 were nice quick, obvious things we could shake out during the normal course of on-the-fly code compilation, but they’re not the only improvements we can make. When building in RepoAuthoritative mode, it’s possible to look at the program a bit deeper and cut out even more unnecessary work. Consider this script:


define('FOO', "Hello World
");
echo FOO;

$x = 1 + 2;
if ($x == 3) {
echo "X is three
";
} else {
echo "X is not three
";
}
Again, a human can look at this and know that a simplified version of this script could be heavily reduced, but keeping track of that state is too complex for the first-pass compiler to do. So we see bytecode looking like this:

$ hhvm -vEval.DumpBytecode=1 /tmp/three.php

// line 3
0: String "Hello World
"
5: DefCns "FOO"
10: PopC
// line 4
11: Cns "FOO"
16: Print
17: PopC
// line 6
18: Int 3
27: SetL 0
29: PopC
// line 7
30: Int 3
39: CGetL2 0
41: Eq
42: JmpZ 17 (59)
// line 8
47: String "X is three
"
52:
đang được dịch, vui lòng đợi..
Kết quả (Việt) 2:[Sao chép]
Sao chép!
HHVM
BLOG DOCS GitHub HACK
The Journey of a Thousand bytecode đăng vào 03 Tháng 10 2014 bởi Sara Golemon Trình biên dịch là vui vẻ. Họ mất đẹp, ngôn ngữ có thể đọc được con người như PHP hoặc Hack và biến chúng thành nạc, trung bình, CPU executin 'Turing máy. Một số trong số này là đơn giản, đủ một sinh viên CS có thể viết lên trong một ngày cuối tuần, một số là những sản phẩm của thập kỷ điều chỉnh và architecting cẩn thận. Một nơi nào đó trong đó truyền thống tự hào đứng HHVM; Trong thực tế, nó là một số trình biên dịch xếp chồng lên nhau trong một chuỗi ngày càng tăng của các thao tác logic và trừu tượng. Bài viết này sẽ cố gắng để đưa người đọc thông qua quá trình biên dịch HHVM từ PHP-script để mã máy x86, một bước tại một thời điểm. Bước Một: Lexing Các bước đầu tiên của cuộc hành trình của chúng tôi nên quen thuộc với nhiều người sử dụng PHP vì nó thực sự tiếp xúc với không gian sử dụng như các token_get_all () chức năng. Trong giai đoạn này, chúng tôi giảm một PHP script của con người có thể đọc được một loạt các thẻ; Nhận dạng số mà các trình biên dịch sẽ có thể nhận ra là có một số ý nghĩa nội tại. Đối với điều này, HHVM sử dụng một công cụ gọi là Flex để tạo ra một chương trình từ một định nghĩa meta tại hphp / phân tích cú pháp / hphp.ll. Việc một số quy định trong tập tin này mô tả làm thế nào để nhận token-từ. Ví dụ, gặp phải "tiếng vang" bên trong của một echo $ a + $ b; Nếu chúng ta chạy này qua token_get_all (), chúng tôi nhận được các trình tự sau của thẻ: // T_OPEN_TAG này đại diện cho " T_WHITESPACE (" n") T_ECHO T_WHITESPACE ("") T_VARIABLE ("USD") T_WHITESPACE ("") '+' T_WHITESPACE ("") T_VARIABLE ("$ b") ';' Chú ý rằng đơn giản, thẻ nhân vật duy nhất không được đặt một cái tên, họ chỉ đơn giản là đi qua như các nhân vật mà họ đang Bây giờ chúng ta có một tập hợp các thẻ máy có thể đọc được, chúng ta có thể bắt đầu xây dựng biểu thức từ họ.. Bước Hai: Phân tích cú pháp Các quy tắc tìm thấy trong hphp / phân tích cú pháp / hphp.y là . được sử dụng bởi Bison để tạo ra một công cụ phân tích cú pháp này phần của các trình biên dịch hiện đọc thẻ, thức ăn cho nó bằng các lexer, để xây dựng một Abstract Syntax Tree (AST); Một đại diện của các biểu thức chứa trong kịch bản PHP của bạn. Xem xét một chút nhiều hơn đoạn phức tạp của kịch bản: echo "? Có 1 + 2 bằng 3"; if ((1 + 2) == 3) { echo "! Có nó"; } else { echo "Không có nó không ..." ; } Chúng tôi tạo ra một AST mà trông giống như sau: EchoStatement ConstantExpression ("Có 1 + 2 bằng nó! ") EchoStatement ConstantExpression ("Không có nó không ...") Ở đây chúng ta có thể thấy rằng thẻ vô tổ chức của chúng tôi đã được nhóm lại với nhau như vậy mà các biểu thức logic được lồng nhau thành các nhóm theo cách chúng ta, những con người, sẽ mong đợi để làm . cảm giác của họ này là trung tâm của các định nghĩa của ngôn ngữ và đó là những gì sẽ quyết định tất cả mọi thứ từ đơn đặt hàng của hoạt động (được ưu tiên) sự kết hợp của một tuyên bố thành khác. Bước Two and a Half: Tối ưu hóa tôi gọi hai này và một nửa , bởi vì chúng ta không thực sự thay đổi đại diện của mã kịch bản gốc tại thời điểm này. Thay vào đó, chúng tôi đang giảm hiện nào có thể được đơn giản hóa trước khi mã là bao giờ chạy (cũng được, một số điều này xảy ra ở bước 3). Ví dụ , chúng ta có thể đánh giá "1 + 2" đến 3, và AST của chúng tôi trở thành: EchoStatement ConstantExpression ("Có 1 + 2 bằng nó ")! EchoStatement ConstantExpression ("Không có nó không ...") Sau đó đánh giá "3 == 3" thành "true": EchoStatement ConstantExpression ("? Có 1 + 2 bằng 3") IfBranchStatement ConstantExpression (true) EchoStatement ConstantExpression ("Có nó!") EchoStatement ConstantExpression ("Không có nó không ...") Bây giờ chúng ta biết rằng chúng ta sẽ luôn có những chi nhánh thật và không bao giờ chi nhánh giả, vì vậy: EchoStatement ConstantExpression ("Liệu 1 + 2 bằng 3? ") EchoStatement ConstantExpression ("Có nó!") Trong đó tất nhiên có thể được giảm đến một EchoStatement duy nhất. Kịch bản này rõ ràng là một chút giả tạo để thể hiện giai đoạn tối ưu này, nhưng nó có giá trị nhìn thấy những gì có thể. Câu hỏi: Sự khác nhau giữa một Tuyên bố và một biểu thức là gì? Trả lời:. báo cáo là thiết bị đầu cuối, họ không có giá trị sản lượng được tiêu thụ bởi các nút sau trong AST, và do phải xuất hiện chỉ như là anh chị em ruột cấp cao nhất trong một HHVM AST Expressions sản xuất một giá trị như vậy, và có thể, như vậy, được sử dụng như là đầu vào cho các biểu thức khác hoặc báo cáo. Bước 3: Lập để bytecode Các giai đoạn cuối cùng của "mặt trước" trình biên dịch của chúng tôi mang đến cho chúng ta qua hphp / biên dịch / phân tích / emitter.cpp nơi chúng tôi quay AST đó vào bytecode mà có thể được thực hiện bằng cách thông dịch viên của HHVM. Chúng tôi làm điều này bằng cách "thăm" mỗi biểu từ các cơ sở của các cây trở lên, với mỗi biểu thức mô tả chính nó như là một loạt các bytecode. Ví dụ, một echo tuyên bố có một hoặc nhiều trẻ em trong đó, nhờ được biểu hiện bản thân, có thể trong lần lượt được tạo thành từ các biểu thức khác. Đối với mỗi biểu thức con của một EchoStatement chúng tôi lần đầu đến biểu thức, trong đó sẽ sản xuất một số bytecode dẫn đến một giá trị duy nhất đã được đẩy lên "stack". Chúng tôi sau đó phát ra một "Print" bytecode sẽ bật giá trị đó, xuất ra nội dung của nó, sau đó đẩy nó giá trị kết quả riêng của int (1). Bởi vì Echo là một tuyên bố, tuy nhiên, và do đó không có giá trị sản lượng, chúng tôi ngay lập tức bật nó trở lại bằng cách sử dụng PopC. Câu hỏi: Tại sao đẩy một giá trị vào stack nếu Echo là một tuyên bố, và không phải là một biểu thức trả lời: Bởi vì Echo lận bằng cách dùng lại các bytecode cùng ý nghĩa cho các print () biểu hiện, mà nếu bạn sẽ nhớ lại, không có một giá trị trả về. Các hướng dẫn PopC ném mà đi như không cần thiết. Câu hỏi: Không phải là không hiệu quả? trả lời:. Có, nhưng chỉ trong chế độ interp, điều này sẽ biến mất khi chúng ta đạt đến JIT Chúng ta hãy nhìn vào mà trong thực tế: echo "Hello" , $ name, " n"; Tạo một AST cho một EchoStatement với ba biểu thức con: EchoStatement ConstantExpression ("Hello") VariableExpression ($ name) ConstantExpression (" n") Mà thăm EchoStatement, người trong lượt thăm mỗi con : String "Hello" // Push "Hello" vào stack Print // Tiêu thụ "Hello" từ stack, và push int (1) Tiêu thụ PopC // int (1) và ném nó đi CGetL 0 // Lấy địa phương # 0 ($ name) từ bảng ký hiệu và đẩy nó vào stack Print // vv ... vv ... vv ... PopC String " n" In PopC Tạo Bytecode Dumps Để xem mã số byte cho một kịch bản nhất định, tương tự như cách PHP cho phép bạn xem Opcodes qua VLD, bạn có hai lựa chọn. Đầu tiên là để chạy nó thông qua HHVM như thường lệ với -vEval.DumpBytecode = 1 tùy chọn. Định dạng này được thiết kế cho khả năng đọc của con người và có ý nghĩa chính cho sự phát triển thời gian chạy và gỡ lỗi. Ví dụ, một chương trình Hello World đơn giản: echo "Hello World n"; trở thành: $ hhvm -vEval.DumpBytecode = 1 /tmp/hello-world.php Pseudo-chính tại 0 maxStackCells: 1 numLocals: 0 numIterators: 0 / / line 2 0: String "Hello World n" 5: In 6: PopC 7: Int 1 16: RetC Pseudo-chính tại 0 maxStackCells: 1 numLocals: 0 numIterators: 0 Nếu bạn đang tự hỏi về Int 1 / RetC lúc kết thúc, đó là tiềm ẩn "return 1;" mà đi kèm ở cuối của mỗi tập tin (mà không trả lại một cái gì đó một cách rõ ràng) để bao gồm biểu có giá trị trả. Hãy nhớ rằng, các "phạm vi toàn cầu" hoạt động như một hàm không tên mà được gọi ngầm khi khởi động, và chia sẻ phạm vi biến của nó với tất cả các chức năng psuedo-chính cấp cao khác. Lựa chọn thứ hai là gần như giống hệt nhau trong invocation, nhưng sản lượng sản xuất đáng chú ý khác: $ hhvm -vEval.DumpHhas = 1 /tmp/hello.php .filepath "/tmp/hello.php"; .main { String "Hello World n" In PopC Int 1 RetC } Điều này thực sự là ngôn ngữ thứ ba của HHVM (PHP và Hack là lần đầu tiên hai), HipHop hội. Ngôn ngữ này ẩn, mà thường chỉ được công nhận trong các tập tin Systemlib, cho phép các nhà phát triển để vượt qua hai bước đầu tiên của việc biên dịch và tạo ra bytecode liệu. Bạn có thể tìm thấy một ví dụ hiện điều này (hphp / hệ thống / php / array_map.hhas) trong việc thực hiện array_map () nơi HHVM sử dụng một bytecode tùy chỉnh thiết kế rõ ràng để thực hiện các chức năng hiệu quả hơn. Để biết thêm về ý nghĩa và mục đích sử dụng của tất cả các của HHVM bytecode, xem hphp / doc / bytecode.specification. Lưu ý: Bạn không nên phát triển các ứng dụng hoặc các thư viện trong HHAS. Cú pháp của HHAS và hành vi của bytecode cụ thể không được đảm bảo giữa các phiên bản của HHVM và hầu như không bao giờ bất kỳ lợi ích để có được qua viết kịch bản trong PHP bình thường. Phạm vi bảo hiểm trong bài viết này là có nghĩa là cho mục đích thông tin. Bước 3.5: HHBBC Optimization Các tối ưu, chúng tôi thực hiện trong bước 2.5 đã nhanh chóng, mọi thứ rõ ràng tốt đẹp chúng tôi có thể lắc ra ngoài trong quá trình bình thường của on-the-fly đang biên soạn, nhưng chúng lại không phải là cải tiến duy nhất chúng ta có thể làm. Khi xây dựng trong chế độ RepoAuthoritative, nó có thể nhìn vào chương trình một chút sâu hơn và cắt ra, ngay cả việc không cần thiết nữa. Hãy xem xét kịch bản này: define ('FOO "," Hello World n "); vang FOO; $ x = 1 + 2; if ($ x == 3) { echo "X là ba n"; } else { vang "X không phải là ba n"; } Một lần nữa, một con người có thể nhìn vào điều này và biết rằng một phiên bản đơn giản hóa của kịch bản này có thể giảm rất nhiều, nhưng theo dõi các trạng thái đó là quá phức tạp cho các trình biên dịch đầu tiên vượt qua để làm. Vì vậy, chúng ta thấy bytecode tìm kiếm như thế này: $ hhvm -vEval.DumpBytecode = 1 /tmp/three.php // dòng 3 0: String "Hello World n" 5: DefCns "FOO" 10: PopC // dòng 4 11: thần kinh trung ương "FOO" 16: In 17: PopC // dòng 6 18: Int 3 27: SETL 0 29: PopC // dòng 7 30: Int 3 39: CGetL2 0 41: Eq 42: JmpZ 17 (59) // dòng 8 47: String "X là ba n" 52:










































































































































































































đang được dịch, vui lòng đợi..
 
Các ngôn ngữ khác
Hỗ trợ công cụ dịch thuật: Albania, Amharic, Anh, Armenia, Azerbaijan, Ba Lan, Ba Tư, Bantu, Basque, Belarus, Bengal, Bosnia, Bulgaria, Bồ Đào Nha, Catalan, Cebuano, Chichewa, Corsi, Creole (Haiti), Croatia, Do Thái, Estonia, Filipino, Frisia, Gael Scotland, Galicia, George, Gujarat, Hausa, Hawaii, Hindi, Hmong, Hungary, Hy Lạp, Hà Lan, Hà Lan (Nam Phi), Hàn, Iceland, Igbo, Ireland, Java, Kannada, Kazakh, Khmer, Kinyarwanda, Klingon, Kurd, Kyrgyz, Latinh, Latvia, Litva, Luxembourg, Lào, Macedonia, Malagasy, Malayalam, Malta, Maori, Marathi, Myanmar, Mã Lai, Mông Cổ, Na Uy, Nepal, Nga, Nhật, Odia (Oriya), Pashto, Pháp, Phát hiện ngôn ngữ, Phần Lan, Punjab, Quốc tế ngữ, Rumani, Samoa, Serbia, Sesotho, Shona, Sindhi, Sinhala, Slovak, Slovenia, Somali, Sunda, Swahili, Séc, Tajik, Tamil, Tatar, Telugu, Thái, Thổ Nhĩ Kỳ, Thụy Điển, Tiếng Indonesia, Tiếng Ý, Trung, Trung (Phồn thể), Turkmen, Tây Ban Nha, Ukraina, Urdu, Uyghur, Uzbek, Việt, Xứ Wales, Yiddish, Yoruba, Zulu, Đan Mạch, Đức, Ả Rập, dịch ngôn ngữ.

Copyright ©2025 I Love Translation. All reserved.

E-mail: