4514a79c60
They can no longer be generated. Also remove all the associated code from the emitter.
4348 linhas
167 KiB
Plaintext
4348 linhas
167 KiB
Plaintext
|
|
**********************************
|
|
* HipHop Bytecode v1 revision 17 *
|
|
**********************************
|
|
|
|
|
|
Introduction
|
|
------------
|
|
|
|
HipHop bytecode (HHBC) v1 is intended to serve as the conceptual basis for
|
|
encoding the semantic meaning of HipHop source code into a format that is
|
|
appropriate for consumption by interpreters and just-in-time compilers. By
|
|
using simpler constructs to encode more complex expressesion and statements,
|
|
HHBC makes it straightforward for an interpreter or a compiler to determine
|
|
the order of execution for a program.
|
|
|
|
HHBC was designed with several competing goals in mind:
|
|
|
|
1) Run-time efficiency. The design of HHBC should be congruous to implementing
|
|
an efficient execution engine, whether it be an interpreter or a just-in-time
|
|
compiler.
|
|
|
|
2) PHP 5.4 compatibility. It should be possible to compile valid PHP 5.4 source
|
|
code into HipHop bytecode in a way that preserves the semantic meaning of the
|
|
source.
|
|
|
|
3) Simplicity. The design of HHBC should avoid features that could be removed
|
|
or simplified without comprimising PHP 5.4 compatibility, run-time efficiency,
|
|
or design cleanliness.
|
|
|
|
|
|
Compilation units
|
|
-----------------
|
|
|
|
Each HipHop source file is compiled into a separate "compilation unit", or
|
|
"unit" for short. Units are composed of bytecode and metadata.
|
|
|
|
A unit's bytecode is an array of bytes encoding a sequence of HHBC
|
|
instructions, where each instruction is encoded using one or more bytes. This
|
|
specification defines an instruction set and defines the behavior of each HHBC
|
|
instruction, but the exact byte values used to encode HHBC instructions is
|
|
currently unspecified.
|
|
|
|
A unit's metadata is a set of structures that provide essential information
|
|
that is needed at run time by the execution engine. This specification will
|
|
describe a unit's metadata as a set of named tables with ordered rows, but the
|
|
exact format of the metadata is currently unspecified.
|
|
|
|
Each instruction in a unit's bytecode can be referred to using a "bytecode
|
|
offset", which is the distance in bytes from the first byte of a unit's
|
|
bytecode to the first byte of the instruction.
|
|
|
|
A unit's bytecode is partitioned into sections called "functions". The unit's
|
|
metadata uses bytecode offsets to specify which instructions belong to which
|
|
functions.
|
|
|
|
When a unit is loaded at run time, the execution engine assigns the unit's
|
|
bytecode a logical range of addresses called "bytecode addresses". An
|
|
instruction is referred to at run time using its bytecode address.
|
|
|
|
|
|
Flow of execution
|
|
-----------------
|
|
|
|
HipHop bytecode models the flow of execution using a stack of frames referred
|
|
to as the "call stack". A "frame" is a structure that logically consists of a
|
|
header, a program counter (PC), a local variable store, an iterator variable
|
|
store, an evaluation stack, and a function parameter info (FPI) stack.
|
|
|
|
The frame at the top of the call stack is referred to as the "current frame".
|
|
The current frame represents the function that is currently executing. The
|
|
program counter (PC) of the current frame is referred to as the "current PC".
|
|
At any given time, the current PC holds the bytecode address of the current
|
|
instruction to execute. When the execution engine executes an instruction, the
|
|
current PC is updated to point to the next instruction. By default, the current
|
|
PC is updated to point to the byte that sequentially follows the last byte of
|
|
the current instruction in the bytecode. Some instructions override the default
|
|
behavior and explicitly update the current PC in a specific way.
|
|
|
|
HHBC provides special instructions to allow for calling a function and
|
|
returning from a function. When a function is called, a new frame is pushed
|
|
onto the call stack, and the PC of the new frame is initialized to the
|
|
appropriate entry point (typically the instruction of the function that is
|
|
sequentially first in the bytecode). The new frame becomes the current frame,
|
|
and the PC of the new frame becomes the current PC. When a function returns,
|
|
the current frame is popped off the call stack. The previous frame becomes the
|
|
current frame, and its PC becomes the current PC. The facility provided by the
|
|
execution engine that is responsible for handling function calls and returns is
|
|
called the "dispatcher".
|
|
|
|
Typically, a frame is removed from the call stack when its corresponding
|
|
function returns. However, a frame may be removed from the call stack before
|
|
its corresponding function returns in the course of processing an exception.
|
|
The facility provided by the execution engine that is responsible for
|
|
processing exceptions is called the "unwinder".
|
|
|
|
|
|
Values
|
|
------
|
|
|
|
HHBC instructions may push and pop values on the current frame's evaluation
|
|
stack and they may read and write values to the current frame's local
|
|
variables. Values come in three flavors: cells, vars, and classrefs.
|
|
|
|
A "cell" is a structure that contains a type identifier and either data (for
|
|
non-refcounted types) or a reference to data (for refcounted types). When a
|
|
cell containing a reference is duplicated, the new cell will point to the same
|
|
data as the original cell. When a cell containing a reference is duplicated or
|
|
discarded, the execution engine is responsible for honoring the data's refcount
|
|
logic.
|
|
|
|
A "var" is a structure that contains a reference to a cell. When a var is
|
|
duplicated, the new var will point to the same cell as the original var. When
|
|
a var is duplicated or destroyed, the execution engine is responsible for
|
|
honoring the cell's refcount logic.
|
|
|
|
A "classref" is a structure that contains a reference to a class. When a
|
|
classref is pushed onto the stack or popped of the stack, no refcounting is
|
|
required.
|
|
|
|
Values on the evaluation stack may be any of the three flavors listed above.
|
|
Values stored in local variables may only be cells or vars.
|
|
|
|
|
|
Functions
|
|
---------
|
|
|
|
A unit's bytecode is organized into functions. Each function has its own
|
|
metadata that provdes essential information about the function, such as the
|
|
name of the function, how many local variables it has, how many iterator
|
|
variables it has, how many formal parameters it has, the names of the local
|
|
variables, the names of the formal parameters, how each parameter should be
|
|
passed (pass by value vs. pass by reference), whether each parameter has a
|
|
default value, and an upper bound for the maximum depth the evaluation stack
|
|
can reach at run time.
|
|
|
|
Each local variable and iterator variable has an id, and HHBC instructions can
|
|
reference these variables using these ids. The id space for local variables is
|
|
distinct from the id space for iterator variables. Thus local id 1 refers to a
|
|
different variable than iterator id 1. Local variable ids and iterator ids are
|
|
signed 32-bit integer values. No function may have more than 2^31 - 1 local
|
|
variables, and no function may have more than 2^31 - 1 iterator variables.
|
|
|
|
Some local variables have names associated with them (called "named local
|
|
variables"), while other local variables do not have names associated with them
|
|
(called "unnamed local variables"). All local variables that reference formally
|
|
declared parameters have names associated with them. Iterator variables do not
|
|
have names associated with them. Variables that have a name associated with
|
|
them will appear in the current variable environment (if they are defined),
|
|
while variables that do not have a name associated with them will never appear
|
|
in the current variable environment.
|
|
|
|
Formally declared parameters are considered to be local variables. Given a
|
|
function with n formally declared parameters, local ids 0 through n-1 will be
|
|
used to reference the formally declared parameters. Formal parameters without
|
|
default values are called "required parameters", while formal parmeters with
|
|
default values are called "optional parameters".
|
|
|
|
The bytecode for each function consists of the instructions of the primary
|
|
function body, optionally followed by the instructions for one or more fault
|
|
funclets. The metadata for each function specifies one or more entry points for
|
|
the primary function body, along with information about each fault funclet.
|
|
Entry points and fault funclets are discussed in more detail in the next
|
|
section. The total size of the bytecode for the primary function body and all
|
|
the fault funclets must not exceed 2^31 - 1 bytes. The primary function body
|
|
and each fault funclet must be a continguous range of bytecode.
|
|
|
|
Each function's metadata provides a "line number table" to allow mapping
|
|
bytecode offsets back to source line numbers. Each row in the line number table
|
|
consists of a source line number and a range of bytecode. The table is sorted
|
|
by starting bytecode offset, lowest offset first. The bytecode offset of the
|
|
beginning of each instruction in the function must belong to exactly one of the
|
|
ranges of bytecode in the line number table.
|
|
|
|
|
|
Entry points and fault funclets
|
|
-------------------------------
|
|
|
|
Entry points come in three varieties: the main entry point, DV entry points,
|
|
and catch entry points.
|
|
|
|
Every function has exactly one main entry point. When a function is called, the
|
|
dispatcher will set the PC of the new frame to point to the main entry point if
|
|
either (1) the function does not have any optional parameters or (2) the caller
|
|
provides values for all of the optional parameters.
|
|
|
|
DV entry points are used to handle initializing optional parameters that the
|
|
caller did not provide. Each DV entry point enters into a corresponding basic
|
|
block of instructions that operates directly on the appropriate local variable
|
|
to set it to its default value. These basic blocks fall through directly into
|
|
one another and the last basic block ends with a jump to the main entry point.
|
|
The dispatcher selects the appropriate DV entry point based on the number of
|
|
arguments passed into the function.
|
|
|
|
The main entry point and DV entry points are used by the dispatcher when
|
|
handling a function call. Each function's metadata provides an "entry point
|
|
table". Each row in the entry point table consists of a number of arguments and
|
|
the bytecode offset of the entry point that should be used by the dispatcher
|
|
(either the main entry point or a DV entry point).
|
|
|
|
Catch entry points are used to implement "catch" blocks in source code. When an
|
|
exception is thrown and the unwinder identifies a matching catch, after it
|
|
unwinds the stack it will transfer control to the catch entry point that
|
|
corresponds to the matching catch. The caught exception object can be retrieved
|
|
by the bytecode using the Catch instruction.
|
|
|
|
Fault funclets are used to perform necessary cleanup when a region of code
|
|
exits abnormally through an exception. When an exception is thrown that exits a
|
|
region protected by a fault funclet, the unwinder will transfer control to the
|
|
fault funclet. When a fault funclet executes the Unwind instruction, it
|
|
transfers control back to the unwinder. Fault funclets are referred to by the
|
|
bytecode offset of their first instruction.
|
|
|
|
Catch entry points and fault funclets are used by the unwinder when processing
|
|
an exception. The exact details about how the unwinder uses catch entry points
|
|
and fault funclets is covered in the next section.
|
|
|
|
|
|
Exceptions
|
|
----------
|
|
|
|
The metadata for each function provides an "exception handler (EH) table".
|
|
Each row in the EH table consists of a kind ("fault" or "catch"), a range
|
|
of bytecode that constitues the protected region, and an offset of a fault
|
|
funclet or list of (classname, offset) pairs describing catch entry points.
|
|
|
|
Each range of bytecode is given by a starting offset and an ending
|
|
offset, where the starting offset is the bytecode offset at the beginning of
|
|
the first instruction in the range and the ending offset is the bytecode offset
|
|
after the last instruction in the range. For any pair of protected regions,
|
|
one of the following must hold: (1) the regions are disjoint and do not
|
|
overlap, or (2) one of the regions must be nested inside the other region.
|
|
|
|
The offset for each catch target in a "catch" EH row must be within the
|
|
primary function body, and no two "fault" rows may target the same offset.
|
|
|
|
When an exception is thrown, control is transferred to the unwinder. The
|
|
unwinder starts with the current frame and consults the EH table of the
|
|
corresponding function. The unwinder visits each row in the EH table whose
|
|
protected region protects the instruction pointed to by the current PC,
|
|
starting with the row of the innermost protected region, then the row of the
|
|
next innermost protected region, and so forth. If two rows have identical
|
|
protected regions, the row occurring first in the EH table will be visited
|
|
first.
|
|
|
|
When the unwinder visits a "fault" kind protected region, it transfers control
|
|
to the corresponding fault funclet. When the fault funclet ends, it transfers
|
|
control back to the unwinder with the Unwind instruction.
|
|
|
|
When the unwinder visits a "catch" kind protected region, it considers each
|
|
catch target in the order they appear in the list. If the exception's type is
|
|
compatible with type of exception handled by the catch block, the unwinder
|
|
transfers control to the corresponding catch entry point, and normal execution
|
|
resumes. Otherwise, the unwinder continues visiting protected regions
|
|
searching for a matching catch.
|
|
|
|
If the unwinder visits all of the relevant protected regions in the current
|
|
frame's EH table and is unable to find a matching catch, the unwinder pops the
|
|
current frame off of the call stack, and repeats the process with the previous
|
|
frame on the call stack. If the unwinder is unable to find a matching catch and
|
|
pops every frame off the call stack, it transfers control to the unhandled
|
|
exception facility.
|
|
|
|
|
|
Unit metadata
|
|
-------------
|
|
|
|
Every compilation unit has a litstr table, a scalar array table, a function
|
|
table, and a class table.
|
|
|
|
The litstr table maps litstr ids to literal strings. Litstr ids are signed
|
|
32-bit integer values. Each litstr id must be between 0 and 2^31 - 2 inclusive.
|
|
|
|
The scalar array table maps scalar array ids to a description of the contents
|
|
of a scalar array. An array is a scalar array if and only if each element of
|
|
the array is a null, boolean, integer, double, string, or a scalar array.
|
|
Furthermore, each element of a scalar array must be a cell. Finally, scalar
|
|
arrays may not recurse infinitely. Each scalar array id must be between 0 and
|
|
2^31 - 2 inclusive.
|
|
|
|
Each row in the function table contains a unique function id, a function name
|
|
specified by a litstr id, the bytecode offset for the corresponding function,
|
|
a flag that indicates if the function is unconditionally declared in the
|
|
outermost scope, and the function metadata. Note that there may be multiple
|
|
rows in the function table with same function name. However, there may not be
|
|
multiple rows that are marked as being unconditionally declared in the
|
|
outermost scope with the same function name. Each function id must be between 0
|
|
and 2^31 - 2 inclusive.
|
|
|
|
Each row in the class table contains a unique class id, a class name specified
|
|
by a litstr id, a flag that indicates if the class declaration is hoisted to
|
|
the prelude of pseudo-main, and the class metadata. Note that there may be
|
|
multiple rows in the class table with same class name. However, there may not
|
|
be multiple rows that are marked as being hoisted with the same class name.
|
|
Each class id must be between 0 and 2^31 - 2 inclusive.
|
|
|
|
|
|
Function parameter info (FPI) structures and the FPI stack
|
|
----------------------------------------------------------
|
|
|
|
Every function has a function parameter info (FPI) structure associated with it
|
|
that can be retrieved at run time. The FPI structure contains the bytecode
|
|
address of the function, the number of parameters the function has, and a
|
|
parameter table that indicates whether each parameter is pass by value or pass
|
|
by reference.
|
|
|
|
In addition to the evaluation stack, each frame also contains another stack
|
|
called the FPI stack. Each entry on the FPI stack consists of a reference to an
|
|
FPI structure and a bytecode address of a function. The entry on the top of the
|
|
FPI stack is called the "current FPI".
|
|
|
|
The FPush* instructions push a new entry onto the FPI stack, initializing the
|
|
entry with a reference to the FPI structure for a given function and the
|
|
bytecode address of the appropriate entry point. The FPass* instructions
|
|
prepare the parameters that will be passed into the callee. The FCall*
|
|
instructions look at the current FPI to get the bytecode address of the
|
|
function (the callee), transfers the parameters from the evaluation stack to
|
|
the callee, pops the current FPI off of the FPI stack, and then invokes the
|
|
dispatcher to call the function.
|
|
|
|
Calls to builtin functions may be optimized to avoid pusing an entry on the FPI
|
|
stack if it is known that the builtin function does not need access to the call
|
|
stack. In this case, the arguments to the builtin are passed with the BPass*
|
|
instructions, and the builtin can be invoked with the FCallBuiltin instruction.
|
|
Unless otherwise noted, subsequent references to FCall* instructions should be
|
|
meant to refer to non-optimized FCall instructions, i.e. all FCall instructions
|
|
other than FCallBuiltin.
|
|
|
|
|
|
Calling convention
|
|
------------------
|
|
|
|
The caller may pass any number of parameters to the callee by executing FPass*
|
|
instructions zero or more times prior to executing an FCall* instruction. The
|
|
caller must pass the parameters in forward order, i.e. the first use of FPass*
|
|
passes the first parameter, the second use of FPass* passes the second
|
|
parameter, and so forth.
|
|
|
|
The FPush*/FPass*/FCall* instructions can be used to call a global function, a
|
|
method on an object, or a method from a class. The caller is responsible for
|
|
evaluating all of the parameters in forward order. When the caller executes an
|
|
FCall* instruction, the dispatcher creates a new frame and moves the parameters
|
|
prepared by the caller into the callee's variable environment. The dispatcher
|
|
then transfers control to the appropriate entry point of the callee (either the
|
|
main entry point or a DV entry point) based on the number of parameters passed.
|
|
|
|
When the callee executes the Ret* instruction, the dispatcher pushes the return
|
|
value onto the caller's evaluation stack. Then the dispatcher destroys the
|
|
callee's frame and transfers control back to the caller.
|
|
|
|
|
|
Property access
|
|
---------------
|
|
|
|
As object properties are accessed during execution, the execution engine is
|
|
responsible for following certain rules to honor each property's accessibility
|
|
and visibility.
|
|
|
|
The accessibility and visibility of a property in a given class is determined
|
|
by that class's definition and the definitons of all of that class's ancestors.
|
|
When a property is declared in a class definition (a "declared property") it
|
|
may be specified as being "public", "protected", or "private". Accessibility
|
|
and visibility are two related but distinct concepts. Depending on the current
|
|
context, a property may be visible and accessible, visible but inaccessible, or
|
|
invisible and inaccessible.
|
|
|
|
If a property P is declared with the "public" qualifier in the definition of
|
|
class C, for instances of class C and descendent classes the property P will be
|
|
visible and accessible in all contexts. If C has an ancestor that declares a
|
|
public property with the same name as P, C is said to "redeclare" property P,
|
|
and the declaration of P in class C is considered to refer to the same property
|
|
as the declaration in the ancestor class.
|
|
|
|
If a property P is declared as "protected" in the definition of class C, for
|
|
instances of class C the property P will be visible in all contexts, but only
|
|
accessible in the context of class C, an ancestor class, or descendent class.
|
|
When class C is loaded at run time, a semantic check must be performed to
|
|
ensure that all ancestor classes of C do not declare a property as "public"
|
|
with the same name as P. If C has an ancestor that declares a public property
|
|
with the same name as P, the execution engine must throw a fatal error when
|
|
class C is loaded. If C has an ancestor that declares a protected property with
|
|
the same name as P, C is said to "redeclare" property P, and the declaration of
|
|
P in class C is considered to refer to the same property as the declaration in
|
|
the ancestor class. Note that there may exist a class D that is a descendent of
|
|
C and declares a property as "public" with the same name as P. In such cases
|
|
the new "public" declaration in D is considered to refer to the same property
|
|
as the original "protected" declaration in C, and the "protected" qualifier
|
|
from the original declaration is effectively overriden by the "public"
|
|
qualifier from the new declaration. Class D is said to "redeclare" property P
|
|
with the "public" qualifier. Thus, for instances of class D and descendent
|
|
classes of D, property P will be visible and accessible in all contexts.
|
|
Finally, if a class E that is descendent of C does not redeclare P as public
|
|
and does not have an ancestor class that redeclares P as public, for instances
|
|
of class E the property P will be visibile in all contexts, but only accessible
|
|
in the context of class E, an ancestor class of E, or a descendent class of E.
|
|
|
|
If a property P is declared with the "private" qualifier in the definition of
|
|
class C, for instances of class C the property P will be visible in all
|
|
contexts, but only accessible in the context of class C. For instances of
|
|
descendent classes of C, the property P will be visible and accessible in the
|
|
context of the class C, and in all other contexts property P will be invisible
|
|
and inaccessible. When class C is loaded at run time, a semantic check must be
|
|
performed to ensure that all ancestor classes of C do not declare a property as
|
|
"public" or "protected" with the same as P. If C has an ancestor that declares
|
|
a public or protected property with the same name as P, the execution engine
|
|
must throw a fatal error when class C is loaded. Note that descendent classes
|
|
of C may declare another property with the same name as P. The declaration of
|
|
property P as "private" in class C is considered to define a separate property
|
|
that is distinct from all other properties of the same name declared in
|
|
ancestor classes and descendent classes of C.
|
|
|
|
An instruction that accesses a property specifies the property by a name N via
|
|
a litstr id, a local variable id, or a cell consumed from the evaluation stack.
|
|
As noted above, it is possible for a class to have multiple distinct properties
|
|
named N. In cases where there are multiple distinct properties named N, the
|
|
visibility rules are used to determine which property is retrieved. If there is
|
|
a visible private property P named N, then property P is retrieved. Otherwise,
|
|
if there is a visible non-private property Q named N, then property Q is
|
|
retrieved. If there is no visible property named N, the behavior is determined
|
|
by the specific instruction. The semantic checks and the visibility rules
|
|
ensure that for any context there cannot be more than one visible private
|
|
property, and there cannot be more than one visible non-private property.
|
|
|
|
Some instructions can create a new property at run time with a name that is
|
|
different than the names of all declared properties that are visible in the
|
|
current context. Such properties are called "non-declared properties" or
|
|
"dynamic properties". Dynamic properties are considered to be visible and
|
|
accessible in all contexts.
|
|
|
|
If a declared property is unset, and then re-accessed/re-created, then it is
|
|
treated the same way as an invisible property with the same attributes as the
|
|
original declared property. Specifically, if the property gets created again,
|
|
it must have the same access attributes as the original declared property.
|
|
|
|
|
|
Magic property access methods
|
|
-----------------------------
|
|
|
|
Instructions that access properties may in some cases invoke a magic property
|
|
access method (__get, __set, __isset, or __unset) if an object implements the
|
|
method and the method is considered eligible for invocation. A magic property
|
|
access method is considered "eligible" for a given object if there is not a
|
|
frame on the call stack that corresponds to an invocation of the same method on
|
|
the same object.
|
|
|
|
|
|
Static property access
|
|
----------------------
|
|
|
|
As a class's static properties are accessed during execution, the execution
|
|
engine is responsible for following certain rules to honor each static
|
|
property's accessibility and visibility.
|
|
|
|
The accessibility and visibility of a static property in a given class is
|
|
determined by that class's definition and the definitons of all of that class's
|
|
ancestors. When a static property is declared in a class definition it may be
|
|
specified as being "public", "protected", or "private". Depending on the
|
|
current context, a static property may be visible and accessible, visible but
|
|
inaccessible, or invisible and inaccessible.
|
|
|
|
Conceptually, each class has a "static store" associated with it at run time
|
|
that provides storage for the static properties declared in the class's
|
|
definition. Static properties are accessed at run time by name through the
|
|
scope of a class. When an instruction accesses a static property through the
|
|
scope of class C, it will search the static store of C and then the static
|
|
stores of C's ancestors (starting with C's base class and moving up the
|
|
inheritance chain) for the first static property with the given name that is
|
|
visible in the current context.
|
|
|
|
If a static property S is declared with the "public" qualifier in the
|
|
definition of class C, the static property S when accessed through the scope of
|
|
class C or a descendent of C will be visible and accessible in all contexts.
|
|
Note that descendent classes of C may declare another static property with the
|
|
same name as S. The declaration in class C is considered to define a separate
|
|
static property that is distinct from all other static properties declared in
|
|
descendent classes of C.
|
|
|
|
If a static property S is declared with the "protected" qualifier in the
|
|
definition of class C, the static property S when accessed through the scope of
|
|
class C or a descendent of C will be visible in all contexts, but only
|
|
accessible in the context of class C, an ancestor class of C, or descendent
|
|
class of C. When class C is loaded at run time, a semantic check must be
|
|
performed to ensure that all ancestor classes of C do not declare a static
|
|
property as "public" with the same name as S. If C has an ancestor that
|
|
declares a public static property with the same name as S, the execution engine
|
|
must throw a fatal error when class C is loaded. Note that descendent classes
|
|
of C may declare another static property with the same name as S. The
|
|
declaration in class C is considered to define a separate static property that
|
|
is distinct from all other static properties declared in descendent classes of
|
|
C.
|
|
|
|
If a static property S is declared with the "private" qualifier in the
|
|
definition of class C, the static property S when accessed through the scope of
|
|
class C will be visible in all contexts, but only accessible in the context of
|
|
class C. The static property S when accessed through the scope of a descendent
|
|
of C will only be visible and accessible in the context of class C. When class
|
|
C is loaded at run time, a semantic check must be performed to ensure that all
|
|
ancestor classes of C do not declare a static property as "public" or
|
|
"protected" with the same name as S. If C has an ancestor that declares a
|
|
public or protected static property with the same name as S, the execution
|
|
engine must throw a fatal error when class C is loaded. Note that descendent
|
|
classes of C may declare another static property with the same name as S. The
|
|
declaration in class C is considered to define a separate static property that
|
|
is distinct from all other static properties declared in descendent classes of
|
|
C.
|
|
|
|
Note that instructions cannot create new static properties in a class that were
|
|
not declared in the class definition.
|
|
|
|
|
|
FPI regions
|
|
-----------
|
|
|
|
An FPI region is a continguous range of bytecode that constitutes a call site.
|
|
Each FPI region begins immediately after an FPush* instruction that pushes an
|
|
FPI structure onto the FPI stack and must end with the corresponding FCall*
|
|
instruction that pops that FPI structure off of the FPI stack. If two FPI
|
|
regions overlap, one of the FPI regions must be completely enclosed by the other
|
|
FPI region. An FPI region may not contain backward jumps, nor may it contain
|
|
forward jumps that jump past the end of the FPI region.
|
|
|
|
Each function has an "FPI region table". Each row in the FPI region table
|
|
consists of the starting offset of the FPI region (the bytecode offset
|
|
immediately following the FPush* instruction), the ending offset of the FPI
|
|
region (the bytecode offset of the FCall* instruction), and the number of
|
|
parameters being passed.
|
|
|
|
|
|
Flavor descriptors
|
|
------------------
|
|
|
|
Any given value on the stack must either be a cell, var, or classref at run
|
|
time. However, at bytecode generation time the specific flavor of a value on
|
|
the stack is not always known. HipHop bytecode uses symbols called "flavor
|
|
descriptors" to precisely describe what is known at bytecode generation about
|
|
the state of the evaluation stack at each instruction boundary.
|
|
|
|
Each instruction description specifies the flavor descriptor produced for each
|
|
of its outputs. Each description also specifies the flavor descriptor consumed
|
|
for each of the instruction's inputs.
|
|
|
|
Here is a description of each flavor descriptor:
|
|
|
|
C - cell; specifies that the value must be a cell at run time
|
|
V - var; specifies that the value must be a var at run time
|
|
A - classref; specifies that the value must be a classref at run time
|
|
R - return value; specifies that the value may be a cell or a var at run
|
|
time; this flavor descriptor is used for return values from function
|
|
calls
|
|
F - function argument; specifies that the value may be a cell or a var at run
|
|
time; this flavor descriptor is used for parameter values that are
|
|
about to be passed into a function
|
|
|
|
|
|
Verifiability
|
|
-------------
|
|
|
|
Because instructions specify constraints on the flavor descriptor of each
|
|
input, it is important to be able to determine if a given HHBC program
|
|
satisfies these constraints. A program that satisfies the constraints on the
|
|
inputs to each instruction is said to be "flavor-safe".
|
|
|
|
HHBC provides a set of verification rules that can be mechanically applied to
|
|
verify that an HHBC program is flavor-safe. All valid HHBC programs must be
|
|
verifiably flavor-safe, and the execution execution may refuse to execute HHBC
|
|
programs that cannot be verified.
|
|
|
|
At bytecode generation time, what is known about the state of the evaluation
|
|
stack at a given instruction boundary can be precisely described using flavor
|
|
descriptors.
|
|
|
|
In addition to being flavor-safe, there are other invariants that valid
|
|
HHBC programs must uphold with respect to metadata and how certain instructions
|
|
are used.
|
|
|
|
Below is the complete list of verifiability rules. If the bytecode to be
|
|
executed does not come from a trusted source, it is the responsibility of the
|
|
bytecode execution engine to verify that these invariants hold.
|
|
|
|
1) The depth of the evaluation stack at any given point in the bytecode must
|
|
the same for all possible control flow paths. The flavor descriptor of any
|
|
given slot on the evaluation stack at any given point in the bytecode must the
|
|
same for all possible control flow paths.
|
|
|
|
2) No instruction may consume more values from the evaluation stack than are
|
|
available at that given point in the bytecode. Likewise, the flavor descriptor
|
|
of each slot on the evaluation stack must be compatible with the instruction's
|
|
inputs' flavor descriptors.
|
|
|
|
3) The evaluation stack must be empty at the beginning and end of each
|
|
try region. The evaluation stack must also be empty at any offset
|
|
listed as a catch entry point.
|
|
|
|
4) If a given instruction is not the target of a forward branch and it follows
|
|
a Jmp, Switch, SSwitch, RetC, RetV, Unwind, Fatal, Throw, NativeImpl, or
|
|
ContHandle instruction, the evaluation stack before executing the given
|
|
instruction must be empty.
|
|
|
|
5) Before executing the RetC instruction, the evaluation stack must contain
|
|
exactly one value and the flavor descriptor of the value must be cell.
|
|
Likewise, before executing the RetV instruction, the evaluation stack must
|
|
contain exactly one value and the flavor descriptor of the value must be the
|
|
var. Finally, before executing the Unwind instruction, the evaluation stack
|
|
must be empty.
|
|
|
|
6) The code for the primary function body and fault funclets must be laid out
|
|
in order in one contiguous block, starting with the primary function body and
|
|
optionally followed by one or more fault funclets. The code for primary
|
|
function body may not jump into the code for the funclets. Similarly, the code
|
|
for a funclet may not jump into the code for the primary function body or
|
|
another funclet.
|
|
|
|
7) The primary function body and each fault funclet must end with one of the
|
|
following instructions: Jmp, Switch, SSwitch, RetC, RetV, Unwind, Fatal, Throw,
|
|
NativeImpl, or ContHandle. The primary function body may not contain the Unwind
|
|
instruction. Also, fault funclets may not contain the Ret* instructions.
|
|
|
|
8) Each FPI region enumerated in the FPI region table must start with an FPush*
|
|
instruction and it must end with an FCall* instruction. Each use of the FPush*
|
|
instruction must be the first instruction in exactly one FPI region. Likewise,
|
|
each use of the FCall* instruction must be the last instruction in exactly one
|
|
FPI region. Finally, the FPass* instructions may not be used outside an FPI
|
|
region.
|
|
|
|
9) Each FPI region may not contain backward jumps, nor may it contain forward
|
|
jumps that jump outside the end of the FPI region. Also, there may not be jumps
|
|
anywhere in the function that transfer control from the outside of a given FPI
|
|
region to the inside of that region. Finally, an FPI region may not contain the
|
|
Ret*, Unwind, Throw, or Fatal instructions.
|
|
|
|
10) The depth of the FPI stack at any given point in the bytecode must be the
|
|
same for all possible control flow paths. Also, for any given FPI region that
|
|
passes n parameters, all possible control flow paths from the beginning of the
|
|
region to the end must pass through exactly n FPass* instructions associated
|
|
with the region which pass the parameters in forward order.
|
|
|
|
11) Given an evaluation stack of depth n after an FPush* instruction, the
|
|
evaluation stack before the corresponding FCall* instruction must also have a
|
|
depth of n. Likewise, the evaluation stack after corresponding FPass*
|
|
instructions must have a depth of n as well. Finally, no instruction between an
|
|
FPush* and its corresponding FCall* may consume any of the values from the
|
|
evaluation stack that were pushed onto the stack before the FPush* instruction.
|
|
|
|
12) The initialization 'state' of each iterator variable must be known at every
|
|
point in the code and must be the same for all control paths. There are three
|
|
possible states: (1) uninitialized, (2) "iter-initialized" (initialized via
|
|
IterInit*), and (3) "miter-initialized" (initialized via MIterInit*). Every
|
|
range of bytecode for which an iterator variable i is initialized must be
|
|
protected by a fault funclet that unsets i by calling IterFree or MIterFree so
|
|
that when the unwinder or dispatcher pops the current frame, each iterator
|
|
variable is uninitialized.
|
|
|
|
13) The iterator variable referenced by IterInit* or MIterInit* must be in
|
|
the uninitialized state when the instruction executes. An iterator variable
|
|
referenced by IterNext* and IterFree must be in the "iter-initialized" state,
|
|
and an iterator variable referenced by MIterNext* or MIterFree must be in the
|
|
"miter-initialized" state. Note that IterInit* and MIterInit* conditionally
|
|
initializes the iterator variable, and IterNext* and MIterNext* conditionally
|
|
frees the iterator variable.
|
|
|
|
|
|
Instruction set
|
|
---------------
|
|
|
|
Each instruction description below consists of a mnemonic, followed by 0 or
|
|
more immediate operands, followed by a stack transition description of the form
|
|
"[xn,...,x2,x1] -> [ym,...,y2,y1]", where "[xn,...,x2,x1]" is a list of flavor
|
|
descriptors describing what the instruction consumes from the evaluation stack
|
|
and "[ym,...,y2,y1]" is the list of flavor descriptors describing what the
|
|
instruction pushes onto the stack. x1 and y1 represent the topmost stack
|
|
elements before and after execution, respectively.
|
|
|
|
Each element of a stack transition may also contain an optional type
|
|
annotation. Here is the list of the type annotations used in instruction
|
|
descriptions:
|
|
|
|
Null - denotes the null type
|
|
Bool - denotes the boolean type
|
|
Int - denotes the integer type
|
|
Dbl - denotes the double-precision floating-point type
|
|
Str - denotes the string type
|
|
Arr - denotes the array type
|
|
Obj - denotes the object type
|
|
|
|
Multiple type annotations may be combined together using the "|" symbol. For
|
|
example, the type annotation "Int|Dbl" means that a value is either integer or
|
|
a double.
|
|
|
|
Some instructions may contain multiple stack transition descriptions to express
|
|
the relationship between the types of the values consumed from the stack and
|
|
types of the values pushed onto the stack. Also, in some stack transition
|
|
descriptions, "<T>" is used as shorthand to represent any one specific type.
|
|
For example, a transition such as "[C:<T>] -> [C:<T>]" indicates that the type
|
|
of value that the instruction pushes onto the stack will match the type of
|
|
value that it consumed from the stack. Likewise, "<F>" is used as shorthand to
|
|
represent any one specific flavor descriptor.
|
|
|
|
$1 is used to refer to the value at the top of the evaluation stack, $2 is used
|
|
to refer to the value directly below $1 on the evaluation stack, $3 is used to
|
|
refer to the value directly below $2, and so forth. Also, %1 is used to refer
|
|
to the first immediate argument, and %2 is used to refer to the second
|
|
immediate argument.
|
|
|
|
Note that the relative offset immediate used by a Jmp*, Iter*, MIter*, Switch,
|
|
or SSwitch instruction is relative the beginning of the instruction.
|
|
|
|
There are numerous instructions that operate on different kinds of locations.
|
|
Locations are specified using "location descriptors". The complete list of
|
|
location descriptors is given below:
|
|
|
|
L - local id; location is the local variable whose id is given by an
|
|
immediate.
|
|
N - local name; location is the local variable whose name is given by the
|
|
value of a cell.
|
|
G - global name; location is the global variable whose name is given by the
|
|
value of a cell.
|
|
S - static property; location is the static property whose class is given by
|
|
a classref and whose name is given by value of a cell.
|
|
C - cell; location is a temporary value given by a cell.
|
|
R - return value; location is a temporary value given by a cell or a var
|
|
|
|
There are several groups of similarly named instructions where the name of each
|
|
instruction ends with a different location descriptor (for example, Set*). Each
|
|
instruction in the group perform similar actions but take different kinds of
|
|
inputs to specify the location to access.
|
|
|
|
The Member instructions provide functionality to operate on elements and
|
|
properties. These instructions incorporate an immediate argument vector which
|
|
specifies a location descriptor (defined above) followed by one or more member
|
|
descriptors:
|
|
|
|
EC - consume a cell from the evaluation stack as an element
|
|
EL:<id> - consume a local given by an immediate id as an element
|
|
ET:<id> - consume a litstr given by an immediate id as an element
|
|
EI:<int>- consume a immediate integer as an element
|
|
PC - consume a cell from the evaluation stack as a property
|
|
PL:<id> - consume a local given by an immediate id as a property
|
|
PT:<id> - consume a litstr given by an immediate id as a property
|
|
W - synthesize a new element (no corresponding local variable or
|
|
evaluation stack slot)
|
|
|
|
For example, the following correspondence exists (ignoring setup bytecode):
|
|
|
|
Source code: $a[3][$b][]['hi'] = 42;
|
|
Bytecode: SetM <L:0 EI:3 EL:1 W ET:hi>
|
|
Stack: [] -> [42]
|
|
|
|
Instructions that have an immediate argument vector have different stack
|
|
transition descriptions depending on the kind of location descriptor and member
|
|
descriptors contained in the immediate argument vector. Member instructions
|
|
denote the immediate argument vector using the notation "<loc-desc/M-vector>"
|
|
and "C..C" is used to indicate that the member instructions consume a variable
|
|
number of cells from the stack. In most cases the immediate vector arguments
|
|
are ordered such that the loc-desc comes first (deepest in the stack), with the
|
|
last M-vector element last (shallowest in the stack). However, classrefs that
|
|
are part of the BaseSC and BaseSL loc-desc inputs always come last (the cell
|
|
input to BaseSC comes first though).
|
|
|
|
In addition to describing each instruction, this instruction set documentation
|
|
also describes several operations that encapsulate fundamental, but non-trivial
|
|
processes that are shared by the Member instructions.
|
|
|
|
The instruction set is organized into the following sections:
|
|
1. Basic instructions
|
|
2. Literal and constant instructions
|
|
3. Operator instructions
|
|
4. Control flow instructions
|
|
5. Get instructions
|
|
6. Isset, Empty and type querying instructions
|
|
7. Mutator instructions
|
|
8. Call instructions
|
|
9. Member operations
|
|
10. Member instructions
|
|
11. Iterator instructions
|
|
12. Include, eval, and define instructions
|
|
13. Miscellaneous instructions
|
|
14. Continuation creation and execution
|
|
|
|
|
|
1. Basic instructions
|
|
---------------------
|
|
|
|
Nop [] -> []
|
|
|
|
No operation. This instruction does nothing.
|
|
|
|
PopC [C] -> []
|
|
PopV [V] -> []
|
|
PopR [R] -> []
|
|
|
|
Pop. Discards the value on the top of the stack.
|
|
|
|
Dup [C:<T>] -> [C:<T> C:<T>]
|
|
|
|
Duplicate. Duplicates the cell $1 and pushes it onto the stack.
|
|
|
|
Box [C:<T>] -> [V:<T>]
|
|
|
|
Box. Creates a new var, sets the new var to point at a copy of cell $1, and
|
|
pushes the var onto the stack.
|
|
|
|
Unbox [V:<T>] -> [C:<T>]
|
|
|
|
Unbox. Creates a copy of the cell that var $1 points to, and pushes the cell
|
|
onto the stack.
|
|
|
|
BoxR [R:<T>] -> [V:<T>]
|
|
|
|
Box. If $1 is a var at run time, this instruction does nothing.
|
|
|
|
If $1 is a cell at run time, this instruction creates a new var, sets the new
|
|
var to point at a copy of cell $1, and pushes the var onto the stack.
|
|
|
|
UnboxR [R:<T>] -> [C:<T>]
|
|
|
|
Unbox. If $1 is a cell at run time, this instruction does nothing.
|
|
|
|
If $1 is a var at run time, this instruction creates a copy of the cell that
|
|
var $1 points to, and pushes the cell onto the stack.
|
|
|
|
|
|
2. Literal and constant instructions
|
|
------------------------------------
|
|
|
|
Null [] -> [C:Null]
|
|
True [] -> [C:Bool]
|
|
False [] -> [C:Bool]
|
|
|
|
Push constant. Null pushes null onto the stack, True pushes true onto
|
|
the stack, and False pushes false onto the stack.
|
|
|
|
NullUninit [] -> [C:Uninit]
|
|
|
|
Push Uninit Null Cell onto stack.
|
|
|
|
Int <signed 64-bit integer value> [] -> [C:Int]
|
|
Double <double value> [] -> [C:Dbl]
|
|
String <litstr id> [] -> [C:Str]
|
|
Array <scalar array id> [] -> [C:Arr]
|
|
|
|
Push immediate. Pushes %1 onto the stack.
|
|
|
|
NewArray [] -> [C:Arr]
|
|
|
|
New array. Creates a new empty array and pushes it onto the stack.
|
|
|
|
NewTuple <num elems> [C..C] -> [C]
|
|
|
|
New array. Creates a new array from the top %1 cells on the stack, pops those
|
|
cells, then pushes the new array onto the stack. Elements are implicitly
|
|
numbered from 0 to num elems - 1; $1 is at index 0, $2 is at 1, and so on.
|
|
|
|
AddElemC [C C C] -> [C:Arr]
|
|
|
|
Add element. If $3 is an array, this instruction executes $3[$2] = $1 and
|
|
then pushes $3 onto the stack.
|
|
|
|
If $3 is not an array, this instruction throws a fatal error.
|
|
|
|
AddElemV [C C V] -> [C:Arr]
|
|
|
|
Add element. If $3 is an array, this instruction executes $3[$2] = &$1 and
|
|
then pushes $3 onto the stack.
|
|
|
|
If $3 is not an array, this instruction throws a fatal error.
|
|
|
|
AddNewElemC [C C] -> [C:Arr]
|
|
|
|
Add new element. If $2 is an array, this instruction executes $2[] = $1 and
|
|
then pushes $2 onto the stack.
|
|
|
|
If $2 is not an array, this instruction throws a fatal error.
|
|
|
|
AddNewElemV [C V] -> [C:Arr]
|
|
|
|
Add new element. If $2 is an array, this instruction executes $2[] = &$1 and
|
|
then pushes $2 onto the stack.
|
|
|
|
If $2 is not an array, this instruction throws a fatal error.
|
|
|
|
NewCol <coll type> <num elems> [] -> [C:Obj]
|
|
|
|
New collection. Creates a new collection of type %1 with an initial capacity
|
|
sufficient to hold the number of elements specified by %2, and pushes the
|
|
collection onto the stack.
|
|
|
|
ColAddElemC [C C C] -> [C:Obj]
|
|
|
|
Collection add key/value pair. If $3 is a collection object, this instruction
|
|
executes $3[$2] = $1 and then pushes $3 onto the stack.
|
|
|
|
If $3 is not a collection object, this instruction throws a fatal error.
|
|
|
|
ColAddNewElemC [C C] -> [C:Obj]
|
|
|
|
Collection add value. If $2 is a collection object, this instruction executes
|
|
$2[] = $1 and then pushes $2 onto the stack.
|
|
|
|
If $2 is not a collection object, this instruction throws a fatal error.
|
|
|
|
Cns <litstr id> [] -> [C:Null|Bool|Int|Dbl|Str]
|
|
|
|
Get constant. Pushes the value of the global constant named %1 onto the stack
|
|
as a cell. If there is no constant named %1, this instruction raises a notice
|
|
and pushes the string %1 onto the stack as a cell.
|
|
|
|
ClsCns <litstr id> [A] -> [C:Null|Bool|Int|Dbl|Str]
|
|
|
|
Get class constant. This instruction pushes the value of the class constant
|
|
named %1 from class $1 onto the stack. If there is no class constant named %1
|
|
in class $1, this instruction throws a fatal error.
|
|
|
|
ClsCnsD <litstr id> <litstr id> [] -> [C:Null|Bool|Int|Dbl|Str]
|
|
|
|
Get class constant (direct). This instruction first checks if %2 matches the
|
|
name of a defined class. If %2 does not match the name of a defined class,
|
|
this instruction will invoke the autoload facility passing in the class name
|
|
%2, and then it will again check if %2 matches the name of a defined class.
|
|
If %2 still does not match the name of a defined class this instruction
|
|
throws a fatal error.
|
|
|
|
Next, this instruction pushes the value of the class constant named %1 from
|
|
class %2 onto the stack. If there is no class constant named %1 in class %2,
|
|
this instruction throws a fatal error.
|
|
|
|
File [] -> [C:Str]
|
|
Dir [] -> [C:Str]
|
|
|
|
Push string. File pushes __FILE__ onto the stack, and Dir pushes __DIR__ onto
|
|
the stack.
|
|
|
|
|
|
3. Operator instructions
|
|
------------------------
|
|
|
|
Concat [C C] -> [C:Str]
|
|
|
|
Concatenation (.). Pushes ((string)$2 . (string)$1) on the stack.
|
|
|
|
Add [C:Arr C:Arr] -> [C:Arr]
|
|
[C:<T2> C:<T1>] -> [C:Dbl] (where T1 == Dbl || T2 == Dbl)
|
|
[C:<T2> C:<T1>] -> [C:Int] (where T1 != Dbl && T2 != Dbl &&
|
|
(T1 != Arr || T2 != Arr))
|
|
|
|
Addition (+). Performs addition (or plus-merge if $1 and $2 are both arrays).
|
|
Pushes ($2 + $1) onto the stack. This instruction throws a fatal error if
|
|
is_array($1) xor is_array($2) is true.
|
|
|
|
Sub [C:<T2> C:<T1>] -> [C:Dbl] (where T1 == Dbl || T2 == Dbl)
|
|
[C:<T2> C:<T1>] -> [C:Int] (where T1 != Dbl && T2 != Dbl)
|
|
|
|
Subtraction (-). Pushes ($2 - $1) onto the stack. This instruction throws a
|
|
fatal error if is_array($1) || is_array($2) is true.
|
|
|
|
Mul [C:<T2> C:<T1>] -> [C:Dbl] (where T1 == Dbl || T2 == Dbl)
|
|
[C:<T2> C:<T1>] -> [C:Int] (where T1 != Dbl && T2 != Dbl)
|
|
|
|
Multiplication (*). Pushes ($2 * $1) onto the stack. This instruction throws
|
|
a fatal error if is_array($1) || is_array($2) is true.
|
|
|
|
Div [C C] -> [C:Bool|Int|Dbl]
|
|
[C:Dbl C:Int] -> [C:Bool|Dbl]
|
|
[C:Int C:Dbl] -> [C:Bool|Dbl]
|
|
[C:Dbl C:Dbl] -> [C:Bool|Dbl]
|
|
|
|
Division (/). Pushes ($2 / $1) onto the stack. This instruction throws a
|
|
fatal error if is_array($1) || is_array($2) is true.
|
|
|
|
Mod [C C] -> [C:Bool|Int]
|
|
|
|
Modulus (%). Pushes ((int)$2 % (int)$1) onto the stack. This instruction
|
|
never throws a fatal error.
|
|
|
|
Xor [C C] -> [C:Bool]
|
|
|
|
Logical xor (xor). Pushes ((bool)$2 xor (bool)$1) onto the stack.
|
|
|
|
Not [C] -> [C:Bool]
|
|
|
|
Logical not (!). Pushes (!(bool)$1) onto the stack.
|
|
|
|
Same [C C] -> [C:Bool]
|
|
|
|
Same (===). Pushes ($2 === $1) onto the stack.
|
|
|
|
NSame [C C] -> [C:Bool]
|
|
|
|
Not same (!==). Pushes ($2 !== $1) onto the stack.
|
|
|
|
Eq [C C] -> [C:Bool]
|
|
|
|
Equals (==). Pushes ($2 == $1) onto the stack.
|
|
|
|
Neq [C C] -> [C:Bool]
|
|
|
|
Not equal (!=). Pushes ($2 != $1) onto the stack.
|
|
|
|
Lt [C C] -> [C:Bool]
|
|
|
|
Less than (<). Pushes ($2 < $1) onto the stack.
|
|
|
|
Lte [C C] -> [C:Bool]
|
|
|
|
Less than or equal to (<=). Pushes ($2 <= $1) onto the stack.
|
|
|
|
Gt [C C] -> [C:Bool]
|
|
|
|
Greater than (>). Pushes ($2 > $1) onto the stack.
|
|
|
|
Gte [C C] -> [C:Bool]
|
|
|
|
Greater than or equal to (>=). Pushes ($2 >= $1) onto the stack.
|
|
|
|
BitAnd [C:<T2> C:<T1>] -> [C:Int] (where T1 != Str || T2 != Str)
|
|
[C:Str C:Str] -> [C:Str]
|
|
|
|
Bitwise and (&). Pushes ($2 & $1) onto the stack. If either $1 or $2 is an
|
|
object, this instruction throws a fatal error.
|
|
|
|
BitOr [C:<T2> C:<T1>] -> [C:Int] (where T1 != Str || T2 != Str)
|
|
[C:Str C:Str] -> [C:Str]
|
|
|
|
Bitwise or (|). Pushes ($2 | $1) onto the stack. If either $1 or $2 is an
|
|
object, this instruction throws a fatal error.
|
|
|
|
BitXor [C:<T2> C:<T1>] -> [C:Int] (where T1 != Str || T2 != Str)
|
|
[C:Str C:Str] -> [C:Str]
|
|
|
|
Bitwise xor (^). Pushes ($2 ^ $1) onto the stack. If either $1 or $2 is an
|
|
object, this instruction throws a fatal error.
|
|
|
|
BitNot [C:<T>] -> [C:Int] (where T != Str)
|
|
[C:Str] -> [C:Str]
|
|
|
|
Bitwise not (~). Pushes (~$1) onto the stack. If $1 is null, a boolean, an
|
|
array, or an object, this instruction throws a fatal error.
|
|
|
|
Shl [C C] -> [C:Int]
|
|
|
|
Shift left (<<). Pushes ((int)$2 << (int)$1) onto the stack. This instruction
|
|
never throws a fatal error.
|
|
|
|
Shr [C C] -> [C:Int]
|
|
|
|
Shift right (>>). Pushes ((int)$2 >> (int)$1) onto the stack. This
|
|
instruction never throws a fatal error.
|
|
|
|
CastBool [C] -> [C:Bool]
|
|
|
|
Cast to boolean ((bool),(boolean)). Pushes (bool)$1 onto the stack.
|
|
|
|
CastInt [C] -> [C:Int]
|
|
|
|
Cast to integer ((int),(integer)). Pushes (int)$1 onto the stack.
|
|
|
|
CastDouble [C] -> [C:Dbl]
|
|
|
|
Cast to double ((float),(double),(real)). Pushes (double)$1 onto the stack.
|
|
|
|
CastString [C] -> [C:Str]
|
|
|
|
Cast to string ((string),(binary)). Pushes (string)$1 onto the stack. If $1
|
|
is an object that implements the __toString method, the string cast returns
|
|
$1->__toString(). If $1 is an object that does not implement __toString
|
|
method, the string cast throws a fatal error.
|
|
|
|
CastArray [C] -> [C:Arr]
|
|
|
|
Cast to array ((array)). Pushes (array)$1 onto the stack.
|
|
|
|
CastObject [C] -> [C:Obj]
|
|
|
|
Cast to object ((object)). Pushes (object)$1 onto the stack.
|
|
|
|
InstanceOf [C C] -> [C:Bool]
|
|
|
|
Instance of (instanceof). If $1 is a string and it matches the name of a
|
|
defined class and $2 is an object that is an instance of $1, this instruction
|
|
pushes true onto the stack. If $1 is an object and get_class($1) matches the
|
|
name of a defined class and $2 is an object that is an instance of
|
|
get_class($1), this instruction pushes true onto the stack. If $1 is not a
|
|
string or an object, this instruction throws a fatal error.
|
|
|
|
InstanceOfD <litstr id> [C] -> [C:Bool]
|
|
|
|
Instance of direct (instanceof). If %1 matches the name of a defined class
|
|
and $1 is an instance of the %1, this instruction pushes true onto the stack,
|
|
otherwise it pushes false onto the stack.
|
|
|
|
Print [C] -> [C:Int]
|
|
|
|
Print (print). Outputs (string)$1 to STDOUT and pushes the integer value 1
|
|
onto the stack.
|
|
|
|
Clone [C] -> [C:Obj]
|
|
|
|
Clone (clone). Clones $1 and pushes it onto the stack. If $1 is not an
|
|
object, this instruction throws a fatal error.
|
|
|
|
Exit [C] -> [C:Null]
|
|
|
|
Exit (exit). Terminates execution of the program.
|
|
|
|
If $1 is an integer, this instruction will set the exit status to $1, push
|
|
null onto the stack, and then it will terminate execution.
|
|
|
|
If $1 is not an integer, this instruction will output (string)$1 to STDOUT,
|
|
set the exit status to 0, push null onto the stack, and then it will
|
|
terminate execution.
|
|
|
|
Fatal <skip frame> [C] -> []
|
|
|
|
Fatal. This instruction throws a fatal error using $1 as the error message.
|
|
If $1 is not a string, this instruction throws a fatal error with an error
|
|
message that indicates that the error message was not a string.
|
|
Setting %1 to 0 will include the full backtrace.
|
|
Setting %1 to 1 will make the backtrace not include the topmost frame. This
|
|
is useful when fatalling from functions that shouldn't be seen from userland.
|
|
|
|
|
|
4. Control flow instructions
|
|
----------------------------
|
|
|
|
Jmp <rel offset> [] -> []
|
|
|
|
Jump. Transfers control to the location specified by %1.
|
|
|
|
JmpZ <rel offset> [C] -> []
|
|
|
|
Jump if zero. Conditionally transfers control to the location specified by %1
|
|
if (bool)$1 == (bool)0.
|
|
|
|
JmpNZ <rel offset> [C] -> []
|
|
|
|
Jump if not zero. Conditionally transfers control to the location specified
|
|
by %1 if (bool)$1 != (bool)0.
|
|
|
|
Switch <offset vector> <base> <bounded> [C] -> []
|
|
|
|
Switch over integer case values. If bounded == 0, the implementation will
|
|
assume that $1 is an integer in the range [0, length(vector)) and
|
|
unconditionally transfer control to the location specified by
|
|
vector[$1]. Undefined behavior will result if $1 is not an integer inside
|
|
this range. If bounded != 0, the following rules take over:
|
|
|
|
For a bounded Switch, the last two elements of the offset vector are special:
|
|
they represent the first non-zero case and the default case,
|
|
respectively. base + length(vector) - 2 must not be greater than 2^63-1. If
|
|
$1 === true, control will be transferred to the location specified by
|
|
vector[length(vector) - 2]. If $1 is equal (as defined by Eq) to any integer
|
|
$n in the range [base, base + length(vector) - 2), control will be
|
|
transferred to the location specified by vector[$n - base]. Otherwise,
|
|
control will be transferred to the location specified by
|
|
vector[length(vector) - 1].
|
|
|
|
SSwitch <litstr id/offset vector> [C] -> []
|
|
|
|
Switch over string case values. This instruction will search the
|
|
string/offset vector from the beginning until it finds a string that is equal
|
|
to $1. If one is found, control will be transferred to the location specified
|
|
by the offset corresponding to that string. If a matching string is not
|
|
found, control is transferred to the location specified by the final element
|
|
in the vector.
|
|
|
|
RetC [C] -> []
|
|
RetV [V] -> []
|
|
|
|
Return. Returns $1 to the caller. This instruction may not be used inside
|
|
default value funclets or fault funclets.
|
|
|
|
Unwind [] -> []
|
|
|
|
Unwind. Transfers control back to the unwinder. This instruction may only be
|
|
used inside a fault funclet.
|
|
|
|
Throw [C] -> []
|
|
|
|
Throw. Throws the object $1. If $1 is not an object that extends the
|
|
Exception class, this instruction throws a fatal error.
|
|
|
|
|
|
5. Get instructions
|
|
-------------------
|
|
|
|
CGetL <local variable id> [] -> [C]
|
|
|
|
Get local as cell. If the local variable given by %1 is defined, this
|
|
instruction gets the value of the local variable and pushes it onto the stack
|
|
as a cell. If the local variable is not defined, this instruction raises a
|
|
warning and pushes null onto the stack.
|
|
|
|
CGetL2 <local variable id> [<F>:<T>] -> [C <F>:<T>]
|
|
|
|
Get local as cell. If the local variable given by %1 is defined, this
|
|
instruction gets the value of the local variable, pushes it onto the stack
|
|
as a cell, and then pushes $1 onto the stack.
|
|
|
|
If the local variable is not defined, this instruction raises a warning,
|
|
pushes null onto the stack, and then pushes $1 onto the stack.
|
|
|
|
CGetL3 <local variable id> [<F2>:<T2> <F1>:<T1>] -> [C <F2>:<T1> <F1>:<T1>]
|
|
|
|
Get local as cell. If the local variable given by %1 is defined, this
|
|
instruction gets the value of the local variable, pushes it onto the stack
|
|
as a cell, then pushes $2 onto the stack, and then pushes $1 onto the stack.
|
|
|
|
If the local variable given by %1 is not defined, this instruction raises a
|
|
warning, pushes null onto the stack, then pushes $2 onto the stack, and then
|
|
pushes $1 onto the stack.
|
|
|
|
CGetN [C] -> [C]
|
|
|
|
Get local as cell. This instruction first computes x = (string)$1. Next, this
|
|
instruction reads the local variable named x pushes its value onto the stack
|
|
as a cell.
|
|
|
|
If there is no local variable defined named x, this instruction pushes null
|
|
onto the stack and raises a warning.
|
|
|
|
CGetG [C] -> [C]
|
|
|
|
Get global as cell. This instruction first computes x = (string)$1. Next,
|
|
this instruction reads the global variable named x pushes its value onto the
|
|
stack as a cell.
|
|
|
|
If there is not a global variable defined named x, this instruction pushes
|
|
null onto the stack and raises a warning.
|
|
|
|
CGetS [C A] -> [C]
|
|
|
|
Get static property as cell. This instruction first checks if class $1 has a
|
|
visible and accessible static property named (string)$2. If it doesn't, this
|
|
instruction throws a fatal error. Otherwise, this instruction pushes the
|
|
static property onto the stack as a cell.
|
|
|
|
VGetL <local variable id> [] -> [V]
|
|
|
|
Get local as var. This instruction boxes the local variable given by %1 if
|
|
necessary and pushes it onto the stack as a var. If the given local variable
|
|
is not defined, this instruction defines it, sets it to null, boxes it, and
|
|
pushes a the value of the local variable onto the stack as a var.
|
|
|
|
VGetN [C] -> [V]
|
|
|
|
Get local as var. This instruction first computes x = (string)$1. Next, this
|
|
instruction boxes the local variable named x (if the local is a cell) and
|
|
pushes its value onto the stack as a var. If there is no local variable
|
|
defined named x, this instruction defines a local variable named x, sets it
|
|
to null, boxes it, and pushes the value of the local variable onto the stack
|
|
as a var.
|
|
|
|
VGetG [C] -> [V]
|
|
|
|
Get global as var. This instruction first computes x = (string)$1. Next, this
|
|
instruction boxes the global variable named x (if the local is a cell) and
|
|
pushes its value onto the stack as a var. If there is no global variable
|
|
defined named x, this instruction defines a global variable named x, sets it
|
|
to null, boxes it, and pushes the value of the global variable onto the stack
|
|
as a var.
|
|
|
|
VGetS [C A] -> [V]
|
|
|
|
Get static property as var. This instruction first checks if class $1 has a
|
|
visible and accessible static property named (string)$2. If it doesn't, this
|
|
instruction throws a fatal error. Otherwise, this instruction boxes the
|
|
static property and pushes it onto the stack as a var.
|
|
|
|
AGetC [C] -> [A]
|
|
AGetL <local variable id> [] -> [A]
|
|
|
|
Fetch class. This instruction first loads a value into x as shown by the
|
|
following table:
|
|
|
|
instruction x
|
|
------------+----
|
|
AGetC | $1
|
|
AGetL | %1
|
|
|
|
Next this instruction checks if x is a string or an object. If x is not a
|
|
string or object, this instruction throws a fatal error. Otherwise, this
|
|
instruction executes y = (is_object(x) ? get_class(x) : (string)x) and checks
|
|
if y matches the name of a defined class. If y does not match the name of a
|
|
defined class, this instruction will invoke the autoload facility passing in
|
|
the class name y, and then it will again check if y matches the name of a
|
|
defined class. If y still does not match the name of a defined class this
|
|
instruction throws a fatal error.
|
|
|
|
Next, this instruction pushs a classref that refers to the class named y.
|
|
|
|
|
|
6. Isset, Empty, and type querying instructions
|
|
-----------------------------------------------
|
|
|
|
IssetC [C] -> [C:Bool]
|
|
|
|
Isset. If $1 is null this instruction pushes false onto the stack, otherwise
|
|
it pushes true.
|
|
|
|
IssetL <local variable id> [] -> [C:Bool]
|
|
|
|
Isset local. This instruction reads the local variable given by %1. If the
|
|
local variable is undefined or null, this instruction pushes false onto the
|
|
stack, otherwise it pushes true.
|
|
|
|
IssetN [C] -> [C:Bool]
|
|
|
|
Isset local. This instruction reads the local variable named (string)$1. If
|
|
the local variable is undefined or null, this instruction pushes false onto
|
|
the stack, otherwise it pushes true.
|
|
|
|
IssetG [C] -> [C:Bool]
|
|
|
|
Isset global. This instruction reads the global variable named (string)$1. If
|
|
the global variable is undefined or null, this instruction pushes false onto
|
|
the stack, otherwise it pushes true.
|
|
|
|
IssetS [C A] -> [C:Bool]
|
|
|
|
Isset static property. This instruction first computes x = (string)$2. Next
|
|
it checks if class $1 has an accessible static property named x. If it
|
|
doesn't, this instruction pushes false.
|
|
|
|
If class $1 does have an accessible property named x, this instruction reads
|
|
the static property named x. If the static property is null, this instruction
|
|
pushes false onto the stack, otherwise it pushes true.
|
|
|
|
EmptyL <local variable id> [] -> [C:Bool]
|
|
|
|
Empty local. This instruction reads the local variable named %1 into x. If
|
|
the local variable is defined this instruction pushes !(x) onto the stack,
|
|
otherwise it pushes true.
|
|
|
|
EmptyN [C] -> [C:Bool]
|
|
|
|
Empty local. This instruction reads the local variable named (string)$1 into
|
|
x. If the local variable is defined this instruction pushes !(x) onto the
|
|
stack, otherwise it pushes true.
|
|
|
|
EmptyG [C] -> [C:Bool]
|
|
|
|
Empty global. This instruction reads the global variable named (string)$1
|
|
into x. If the global variable is defined this instruction pushes !(x) onto
|
|
the stack, otherwise it pushes true.
|
|
|
|
EmptyS [C A] -> [C:Bool]
|
|
|
|
Empty static property. This instruction first checks if class $1 has an
|
|
accessible static property named (string)$2. If it doesn't, this instruction
|
|
pushes true, otherwise this instruction reads the static property into x and
|
|
pushes !(x) onto the stack.
|
|
|
|
IsNullC [C] -> [C:Bool]
|
|
IsBoolC [C] -> [C:Bool]
|
|
IsIntC [C] -> [C:Bool]
|
|
IsDoubleC [C] -> [C:Bool]
|
|
IsStringC [C] -> [C:Bool]
|
|
IsArrayC [C] -> [C:Bool]
|
|
IsObjectC [C] -> [C:Bool]
|
|
|
|
Is type. This instruction first loads a type into t as given by the following
|
|
table:
|
|
|
|
instruction t
|
|
-------------+------
|
|
IsNullC | Null
|
|
IsBoolC | Bool
|
|
IsIntC | Int
|
|
IsDoubleC | Dbl
|
|
IsStringC | Str
|
|
IsArrayC | Arr
|
|
IsObjectC | Obj
|
|
|
|
If $1 is of type t, this instruction pushes true onto the stack, otherwise it
|
|
pushes false.
|
|
|
|
IsNullL <local variable id> [] -> [C:Bool]
|
|
IsBoolL <local variable id> [] -> [C:Bool]
|
|
IsIntL <local variable id> [] -> [C:Bool]
|
|
IsDoubleL <local variable id> [] -> [C:Bool]
|
|
IsStringL <local variable id> [] -> [C:Bool]
|
|
IsArrayL <local variable id> [] -> [C:Bool]
|
|
IsObjectL <local variable id> [] -> [C:Bool]
|
|
|
|
Is type. This instruction first loads a type into t and a value into x as
|
|
given by the following table:
|
|
|
|
instruction t x
|
|
-------------+------+-------
|
|
IsNullL | Null | true
|
|
IsBoolL | Bool | false
|
|
IsIntL | Int | false
|
|
IsDoubleL | Dbl | false
|
|
IsStringL | Str | false
|
|
IsArrayL | Arr | false
|
|
IsObjectL | Obj | false
|
|
|
|
If the local variable given by %1 is defined, this pushes true onto the stack
|
|
if the local variable is of type t, otherwise it pushes false.
|
|
|
|
If the local variable given by %1 is not defined, this instruction raises a
|
|
warning and pushes x onto the stack.
|
|
|
|
|
|
7. Mutator instructions
|
|
-----------------------
|
|
|
|
SetL <local variable id> [C] -> [C]
|
|
|
|
Set local. This instruction marks the local variable given by %1 as defined,
|
|
stores the value $1 into the local variable, and then pushes $1 onto the
|
|
stack.
|
|
|
|
SetN [C C] -> [C]
|
|
|
|
Set local. This instruction marks the local variable named (string)$2 as
|
|
defined, assigns the value $1 to the local variable, and then pushes $1 onto
|
|
the stack.
|
|
|
|
SetG [C C] -> [C]
|
|
|
|
Set global. This instruction marks the global variable named (string)$2 as
|
|
defined, assigns the value $1 to the global variable, and then pushes $1 onto
|
|
the stack.
|
|
|
|
SetS [C A C] -> [C]
|
|
|
|
Set static property. First this instruction checks if class $2 has an
|
|
accessible static property named (string)$3. If it doesn't, this instruction
|
|
throws a fatal error. Otherwise, this instruction assigns the value $1 to the
|
|
static property, and then it pushes $1 onto the stack.
|
|
|
|
SetOpL <local variable id> <op> [C] -> [C]
|
|
|
|
Set op local. If the local variable given %1 is not defined, this instruction
|
|
marks it as defined, sets it to null, and raises a warning.
|
|
|
|
Next, this instruction reads the local variable into x, then executes y = x
|
|
<op> $1, assigns y into local variable %1, and then pushes y onto the stack.
|
|
The immediate value must be one of the following opcodes:
|
|
Add, Sub, Mul, Div, Mod, Shl, Shr, Concat, BitAnd, BitOr, BitXor.
|
|
|
|
SetOpN <op> [C C] -> [C]
|
|
|
|
Set op local. This instruction first computes x = (string)$2. If the local
|
|
variable named n is not defined, this instruction marks it as defined, sets
|
|
it to null, and raises a warning.
|
|
|
|
Next, this instruction reads the local variable named x into y, executes
|
|
z = y <op> $1, assigns z into the local variable named x, and then pushes z
|
|
onto the stack as a cell. The immediate value must be one of the following
|
|
opcodes:
|
|
Add, Sub, Mul, Div, Mod, Shl, Shr, Concat, BitAnd, BitOr, BitXor.
|
|
|
|
SetOpG <op> [C C] -> [C]
|
|
|
|
Set op global. This instruction first computes x = (string)$2. If the global
|
|
variable named n is not defined, this instruction marks it as defined, sets
|
|
it to null, and raises a warning.
|
|
|
|
Next, this instruction reads the global variable named x into y, executes
|
|
z = y <op> $1, assigns z into the global variable named x, and then pushes z
|
|
onto the stack as a cell. The immediate value must be one of the following
|
|
opcodes:
|
|
Add, Sub, Mul, Div, Mod, Shl, Shr, Concat, BitAnd, BitOr, BitXor.
|
|
|
|
SetOpS <op> [C A C] -> [C]
|
|
|
|
Set op static property. This instruction first computes x = (string)$3. Next
|
|
it checks if class $2 has an accessible static property named x. If it
|
|
doesn't, this instruction throws a fatal error. Otherwise, this instruction
|
|
reads the static property named x into y, executes z = y <op> $1, assigns z
|
|
into the static property, and then pushes z onto the stack. The immediate
|
|
value must be one of the following opcodes:
|
|
Add, Sub, Mul, Div, Mod, Shl, Shr, Concat, BitAnd, BitOr, BitXor.
|
|
|
|
IncDecL <local variable id> <op> [] -> [C]
|
|
|
|
Increment/decrement local. If the local variable given by %1 is not defined,
|
|
this instruction marks it as defined, sets it to null, and raises a warning.
|
|
|
|
Where x is the local given by %1, this instruction then does the following:
|
|
|
|
If op is PreInc, this instruction executes ++x and then pushes x onto the
|
|
stack as a cell.
|
|
|
|
If op is PostInc, this instruction pushes x onto the stack and then it
|
|
executes ++x.
|
|
|
|
If op is PreDec, this instruction executes --x and then pushes x onto the
|
|
stack.
|
|
|
|
If op is PostDec, this instruction pushes x onto the stack and then it
|
|
executes --x.
|
|
|
|
IncDecN <op> [C] -> [C]
|
|
IncDecG <op> [C] -> [C]
|
|
|
|
Increment/decrement. This instruction first computes x = (string)$1. Next, if
|
|
the local variable (IncDecN) or global variable (IncDecG) named x is not
|
|
defined, this instruction first defines it, sets it to null, and raises a
|
|
warning.
|
|
|
|
Where v is the local variable or global variable named x, this instruction
|
|
performs the following:
|
|
|
|
If op is PreInc, this instruction executes ++v and then pushes v onto the
|
|
stack as a cell.
|
|
|
|
If op is PostInc, this instruction pushes v onto the stack and then it
|
|
executes ++v.
|
|
|
|
If op is PreDec, this instruction executes --v and then pushes v onto the
|
|
stack.
|
|
|
|
If op is PostDec, this instruction pushes v onto the stack and then it
|
|
executes --v.
|
|
|
|
IncDecS <op> [C A] -> [C]
|
|
|
|
Increment/decrement static property. This instruction first computes
|
|
x = (string)$2. Next it checks if class $1 has an accessible static property
|
|
named x. If it doesn't, this instruction throws a fatal error.
|
|
|
|
Where s is the static property named x, this instruction performs the
|
|
following:
|
|
|
|
If op is PreInc, this instruction increments the ++s and then pushes s onto
|
|
the stack.
|
|
|
|
If op is PostInc, this instruction pushes s onto the stack and then it
|
|
executes ++s.
|
|
|
|
If op is PreDec, this instruction executes --s and then pushes s onto the
|
|
stack.
|
|
|
|
If op is PostDec, this instruction pushes s onto the stack and then it
|
|
executes --s.
|
|
|
|
BindL <local variable id> [V] -> [V]
|
|
|
|
Bind local. This instruction marks the local variable given by %1 as defined,
|
|
binds the local variable to $1, and pushes $1 onto the stack.
|
|
|
|
BindN [C V] -> [V]
|
|
|
|
Bind local. This instruction marks the local variable named (string)$2 as
|
|
defined, binds the local variable to $1, and pushes $1 onto the stack.
|
|
|
|
BindG [C V] -> [V]
|
|
|
|
Bind global. This instruction marks the global variable named (string)$2 as
|
|
defined, binds the global variable to $1, and pushes $1 onto the stack.
|
|
|
|
BindS [C A V] -> [V]
|
|
|
|
Bind static property. This instruction first checks if class $2 has an
|
|
accessible static property named (string)$3. If it doesn't, this instruction
|
|
throws a fatal error. Otherwise, this instrution binds the static property
|
|
to $1, and pushes $1 onto the stack.
|
|
|
|
UnsetL <local variable id> [] -> []
|
|
|
|
Unset local. Breaks any bindings the local variable given by %1 may have and
|
|
marks the local variable as undefined.
|
|
|
|
UnsetN [C] -> []
|
|
|
|
Unset local. This instruction breaks any bindings the local variable named
|
|
(string)$1 may have and marks the local variable as undefined.
|
|
|
|
UnsetG [C] -> []
|
|
|
|
Unset global. This instruction breaks any bindings the global variable named
|
|
(string)$1 may have and marks the global variable as undefined.
|
|
|
|
|
|
8. Call instructions
|
|
--------------------
|
|
|
|
FPushFunc <num params> [C] -> []
|
|
FPushFuncD <num params> <litstr id> [] -> []
|
|
|
|
FPI push function. First, these instructions load a value into x as given by
|
|
the following table:
|
|
|
|
instruction x
|
|
--------------+----
|
|
FPushFunc | $1
|
|
FPushFuncD | %2
|
|
|
|
If x is a string, this instruction attempts to lookup a function named x. If
|
|
no function named x is defined, this instruction throws a fatal error.
|
|
Otherwise this instruction pushes a new entry on the FPI stack, initializing
|
|
it with the number of parameters being passed (given by %1) and a reference
|
|
to the FPI structure for the function named x.
|
|
|
|
If x is an object, this instruction checks if the object has an __invoke
|
|
method. If the object does not have an __invoke method, this instruction
|
|
throws a fatal error. Otherwise this instruction pushes a new entry on the
|
|
FPI stack, intializing it with the number of parameters being passed (given
|
|
by %1) and a reference to the FPI structure for the __invoke method from
|
|
object x.
|
|
|
|
If x is not a string or object, this instruction throws a fatal error.
|
|
|
|
FPushObjMethod <num params> [C C] -> []
|
|
FPushObjMethodD <num params> <litstr id> [C] -> []
|
|
|
|
FPI push object-based method. First, these instructions load values into x
|
|
and y as given by the following table:
|
|
|
|
instruction x y
|
|
-------------------+----+-----
|
|
FPushObjMethod | $2 | $1
|
|
FPushObjMethodD | $1 | %2
|
|
|
|
This x is not an object or if y is not a string, this instruction throws a
|
|
fatal error. Next, this instruction checks if object x has an accessible
|
|
method named y. If it does, this instruction pushes a new entry on the FPI
|
|
stack, initializing it with the number of parameters being passed (given by
|
|
%1) and a reference to the FPI structure for the method named y from object x.
|
|
|
|
If object x does not have an accessible method named y, this instruction
|
|
checks if object x has a __call method. If a __call method is found, this
|
|
instruction pushes a new entry on the FPI stack, initializing it with the
|
|
number of parameters being passed (given by %1) and a reference to the FPI
|
|
structure for the __call from object x, and stores the original name y in the
|
|
FPI stack entry.
|
|
|
|
If object x does not have an accessible method named y and it does not have a
|
|
__call method, this instruction throws a fatal error.
|
|
|
|
FPushClsMethod <num params> [C A] -> []
|
|
FPushClsMethodF <num params> [C A] -> []
|
|
FPushClsMethodD <num params> <litstr id> <litstr id> [] -> []
|
|
|
|
FPI push class-based method. First, these instructions load values into x and
|
|
y as given by the following table:
|
|
|
|
instruction x y
|
|
-------------------+----+-----
|
|
FPushClsMethod | $1 | $2
|
|
FPushClsMethodF | $1 | $2
|
|
FPushClsMethodD | %3 | %2
|
|
|
|
When loading %3 into x, FPushClsMethodD will perform the work performed by
|
|
the AGetC instruction to convert the name given by %3 into a classref.
|
|
|
|
If y is not a string, this instruction throws a fatal error. Next, this
|
|
instruction checks if class x has an accessible method named y. If class x
|
|
has a method named y. If it does, this instruction pushes a new entry on the
|
|
FPI stack, initializing it with the number of parameters being passed (given
|
|
by %1) and a reference to the FPI structure for the method named y from class
|
|
x.
|
|
|
|
If class x does not have an accessible method named y, this instruction
|
|
checks if the current function's $this is non-null, if the class of $this
|
|
is the same or derived from class x, and if $this has a __call method. If no
|
|
suitable __call method is found, this instruction will check if class x has a
|
|
__callStatic method. If a suitable __call method or a __callStatic method is
|
|
found, this instruction pushes a new entry on the FPI stack, initializing it
|
|
with the number of parameters being passed (given by %1) and a reference to
|
|
the FPI structure for the __call or __callStatic method that was found,
|
|
and stores the original name y in the FPI stack entry.
|
|
|
|
If class x does not have an accessible method named y, and if a suitable
|
|
__call method or a __callStatic method could not be found, this instruction
|
|
throws a fatal error.
|
|
|
|
FPushCtor <num params> [A] -> [C]
|
|
FPushCtorD <num params> <litstr id> [] -> [C]
|
|
|
|
FPI push constructor. First, these instructions load a value into x as given
|
|
by the following table:
|
|
|
|
instruction x
|
|
--------------+----
|
|
FPushCtor | $1
|
|
FPushCtorD | %2
|
|
|
|
When loading %2 into x, FPushCtorD will perform the work performed by the
|
|
AGetC instruction to convert the name given by %2 into a classref.
|
|
|
|
This instruction pushes an uninitialized object onto the stack (to be
|
|
initialized during FCall*) prior to entering the FPI region, then pushes a new
|
|
entry on the FPI stack, initializing it with the number of parameters being
|
|
passed (given by %1) and a reference to the FPI structure for the constructor
|
|
for class x.
|
|
|
|
FPushCuf <num params> [C] -> []
|
|
FPushCufF <num params> [C] -> []
|
|
|
|
FPI push call user function. These instructions lookup $1 as a callable, and
|
|
push a new entry onto the FPI stack. If $1 is not callable, they issue a
|
|
warning, and push an entry representing a function which does nothing, takes
|
|
no argument, and returns null.
|
|
|
|
FPushCufSafe <num params> [C C] -> [C C]
|
|
|
|
FPI push call user function. This instruction pops $1 and $2, then pushes
|
|
$1 back onto the stack. It then looks up $2 as a callable, and pushes a
|
|
new entry onto the FPI stack. If $2 is not callable, it pushes an entry
|
|
representing a function which does nothing, takes no argument, and returns
|
|
null, and in addition pushes boolean false onto the evaluation stack;
|
|
otherwise it pushes true onto the evaluation stack.
|
|
|
|
CufSafeArray [C C R] -> [C]
|
|
|
|
Pops 3 elements from the stack, and pushes array($2, $1), preserving
|
|
references.
|
|
|
|
CufSafeReturn [C C R] -> [R]
|
|
|
|
Pops 3 elements from the stack, and pushes $2 ? $1 : $3, preserving
|
|
references.
|
|
|
|
FPassC <param id> [C] -> [F]
|
|
FPassCW <param id> [C] -> [F]
|
|
FPassCE <param id> [C] -> [F]
|
|
|
|
FPI pass parameter. This instruction pushes $1 onto the stack as a cell
|
|
regardless of whether parameter %1 is pass by value or pass by reference.
|
|
|
|
If parameter %1 is pass by reference, FPassCW and FPassCE check if the
|
|
function associated with the current FPI (the callee) is an extension
|
|
function that can accept a cell for parameter %1. If this condition is not
|
|
met, FPassCW will raise a warning while FPassCE will throw a fatal error.
|
|
|
|
FPassV <param id> [V] -> [F]
|
|
|
|
FPI pass parameter. If parameter %1 is pass by value, this instruction will
|
|
unbox $1 and push it onto the stack as a cell. If parameter %1 is pass by
|
|
reference, this instruction will push $1 onto the stack as a var.
|
|
|
|
FPassR <param id> [R] -> [F]
|
|
|
|
FPI pass parameter. If $1 is a cell at run time, this instruction will behave
|
|
like FPassC. Otherwise, this instruction will behave like FPassV.
|
|
|
|
FPassL <param id> <local variable id> [] -> [F]
|
|
|
|
FPI pass local as parameter. This instruction behaves as CGetL if parameter
|
|
%1 is pass by value, or it behaves like VGetL if parameter %1 is pass by
|
|
reference.
|
|
|
|
FPassN <param id> [C] -> [F]
|
|
|
|
FPI pass local as parameter. This instruction behaves as CGetN if parameter
|
|
%1 is pass by value, or it behaves like VGetN if parameter %1 is pass by
|
|
reference.
|
|
|
|
FPassG <param id> [C] -> [F]
|
|
|
|
FPI pass global as parameter. This instruction behaves as CGetG if parameter
|
|
%1 is pass by value, or it behaves like VGetG if parameter %1 is pass by
|
|
reference.
|
|
|
|
FPassS <param id> [C A] -> [F]
|
|
|
|
FPI pass parameter. This instruction behaves as CGetS if parameter %1 is pass
|
|
by value, or it behaves like VGetS if parameter %1 is pass by reference.
|
|
|
|
FCall <num params> [F..F] -> [R]
|
|
|
|
FPI call. This instruction gets the bytecode address of the function
|
|
associated with the current FPI (the callee), transfers the top %1 values
|
|
from the stack to the callee as parameters, pops the current FPI off of the
|
|
FPI stack, and then invokes the dispatcher to call the callee. When the
|
|
callee returns, it will transfer the return value onto the caller's
|
|
evaluation stack using the R flavor.
|
|
|
|
FCallArray [F] -> [R]
|
|
|
|
FPI call with array. This instruction gets the bytecode address of the
|
|
function associated with the current FPI (the callee), transfers the
|
|
elements of $1 (which must be an array) to the callee as parameters, pops
|
|
the current FPI off of the FPI stack, and then invokes the dispatcher to
|
|
call the callee. When the callee returns, it will transfer the return value
|
|
onto the caller's evaluation stack using the R flavor.
|
|
|
|
BPassC <param id> [C] -> [F]
|
|
|
|
Pass parameter to builtin call. This opcode is expected to be emitted
|
|
only when parameter %1 is pass by value. The instruction pushes $1
|
|
onto the stack as a cell.
|
|
|
|
BPassV <param id> [V] -> [F]
|
|
|
|
Pass parameter to builtin call. This opcode is expected to be emitted
|
|
only when parameter %1 is pass by reference. The instruction pushes $1
|
|
onto the stack as a var.
|
|
|
|
FCallBuiltin <total params> <passed params> <litstr id> [F..F] -> [R]
|
|
|
|
Optimized builtin call without an ActRec. This instruction attempts to
|
|
lookup a builtin function named %3. If no function named %3 is defined,
|
|
this instruction throws a fatal error. Otherwise, this function gets
|
|
address of the builtin function named %3, transfers the top %1 values
|
|
from the stack to the callee as parameters, and then invokes the dispatcher
|
|
to call the callee. %2 denotes the number of non-default parameters pushed
|
|
onto stack by user level code. When the callee returns, it will transfer
|
|
the return value onto the caller's evaluation stack using the R flavor.
|
|
|
|
|
|
9. Member operations
|
|
--------------------
|
|
|
|
The following operations describe processes that are shared across the Member
|
|
instructions. Operations are not considered instructions; they do not have
|
|
opcodes associated with them.
|
|
|
|
Operations can produce and consume intermediate values called "bases". A "base"
|
|
is a structure that contains either a cell or a var or a reference to a memory
|
|
location that is occupied by a cell or a var. A "base" also contains a boolean
|
|
flag "StrOff" which indicates whether the base represents a string offset.
|
|
Bases are never pushed onto the evaluation stack.
|
|
|
|
For operations that create a base, the operation descriptions specify whether
|
|
the base created "contains" a value or "references" a location. In the former
|
|
case, the base created contains a cell or a var. In the latter case, the base
|
|
created contains a reference to a memory location occupied by a cell or a var.
|
|
Operations that create a base set the StrOff flag to false by default unless
|
|
indicated otherwise by the operation's description.
|
|
|
|
When a base that contains a cell is destroyed, if the cell references data then
|
|
the execution engine is responsible for honoring the data's refcount logic.
|
|
Likewise when a base that contains a var is destroyed, the execution engine is
|
|
responsible for honoring the refcount logic of the cell referenced by the var.
|
|
When a base that contains a reference to a memory location occupied by a cell
|
|
or a var is destroyed, no refcounting is required.
|
|
|
|
Some operations that take a base as input can modify that base as part of the
|
|
work performed by the operation. Such operations are said to "set" the base to
|
|
a new value. When a base that contains a cell or a reference to a memory
|
|
location occupied by a cell is set to a new value, the new value overwrites the
|
|
previous value contained in the cell (honoring the data refcount logic if the
|
|
previous value was a refcounted type). When a base that contains a var or a
|
|
reference to a memory location occupied by a var is set to the new value, the
|
|
new value is written into the cell referenced by the var, overwriting the
|
|
previous value contained in that cell (honoring the data refcount logic if the
|
|
previous value was a refcounted type). Note that for bases that contain a
|
|
reference to a memory location, "setting" the base does not change which memory
|
|
location the base references.
|
|
|
|
Operations are specified as if they directly operate on the top of the
|
|
evaluation stack in the name of consistency and clarity, but in fact their
|
|
inputs and outputs may reside elsewhere. The symbol 'B' is used in the input
|
|
descriptions and output descriptions of operations to indicate that a given
|
|
operation consumes a base as input or produces a base as output.
|
|
|
|
BaseC [C] -> [B]
|
|
|
|
Get base from value. This operation outputs a base that contains the value
|
|
given by $1.
|
|
|
|
BaseR [R] -> [B]
|
|
|
|
Get base from return value. This operation outputs a base that contains the
|
|
return value given by $1.
|
|
|
|
BaseL <local variable id> [] -> [B]
|
|
|
|
Get base from local. This operation outputs a base that references the local
|
|
given by %1. If the local is not defined, this operation outputs a base that
|
|
contains null.
|
|
|
|
BaseLW <local variable id> [] -> [B]
|
|
|
|
Get base from local. This operation outputs a base that references the local
|
|
given by %1. If the local is not defined, this operation raises a warning and
|
|
outputs a base that contains null.
|
|
|
|
BaseLD <local variable id> [] -> [B]
|
|
|
|
Get base from local. This operation outputs a base that references the local
|
|
given by %1. If the local is not defined, this operation defines it and
|
|
returns a base that references the local.
|
|
|
|
BaseLWD <local variable id> [] -> [B]
|
|
|
|
Get base from local. This operation outputs a base that references the local
|
|
variable given by %1. If the local is not defined, this operation defines it,
|
|
raises a warning, and returns a base that references the local.
|
|
|
|
BaseNC [C] -> [B]
|
|
BaseNL <local variable id> [] -> [B]
|
|
|
|
Get base from name. This operation outputs a base that references the local
|
|
variable whose name is given by (string)%1 or (string)$1. If the local is not
|
|
defined, this operation outputs a base that contains null.
|
|
|
|
BaseNCW [C] -> [B]
|
|
BaseNLW <local variable id> [] -> [B]
|
|
|
|
Get base from name. This operation outputs a base that references the local
|
|
variable whose name is given by (string)%1 or (string)$1. If the local is not
|
|
defined, this operation raises a warning and outputs a base that contains
|
|
null.
|
|
|
|
BaseNCD [C] -> [B]
|
|
BaseNLD <local variable id> [] -> [B]
|
|
|
|
Get base from name. This operation outputs a base that references the local
|
|
variable whose name is given by (string)%1 or (string)$1. If the local is not
|
|
defined, this operation defines it and returns a base that references the
|
|
local.
|
|
|
|
BaseNCWD [C] -> [B]
|
|
BaseNLWD <local variable id> [] -> [B]
|
|
|
|
Get base from name. This operation outputs a base that references the local
|
|
variable whose name is given by (string)%1 or (string)$1. If the local is not
|
|
defined, this operation defines it, raises a warning, and returns a base that
|
|
references the local.
|
|
|
|
BaseGC [C] -> [B]
|
|
BaseGL <local variable id> [] -> [B]
|
|
|
|
Get base from global name. This operation outputs a base that references the
|
|
global variable whose name is given by (string)%1 or (string)$1. If the
|
|
global is not defined, this operation produces a base that contains null.
|
|
|
|
BaseGCW [C] -> [B]
|
|
BaseGLW <local variable id> [] -> [B]
|
|
|
|
Get base from global name. This operation outputs a base that references the
|
|
global variable whose name is given by (string)%1 or (string)$1. If the
|
|
global is not defined, this operation raises a warning and outputs a base
|
|
that contains null.
|
|
|
|
BaseGCD [C] -> [B]
|
|
BaseGLD <local variable id> [] -> [B]
|
|
|
|
Get base from global name. This operation outputs a base that references the
|
|
global variable whose name is given by (string)%1 or (string)$1. If the
|
|
global is not defined, this operation defines it and returns a base that
|
|
references the global.
|
|
|
|
BaseGCWD [C] -> [B]
|
|
BaseGLWD <local variable id> [] -> [B]
|
|
|
|
Get base from global name. This operation outputs a base that references the
|
|
global variable whose name is given by (string)%1 or (string)$1. If the
|
|
global is not defined, this operation defines it, raises a warning, and
|
|
returns a base that references the global.
|
|
|
|
BaseSC [C A] -> [B]
|
|
BaseSL <local variable id> [A] -> [B]
|
|
|
|
Get base from static property. First, this operation loads a value into x as
|
|
given by the following table:
|
|
|
|
operation x
|
|
-----------+----
|
|
BaseSC | $2
|
|
BaseSL | %1
|
|
|
|
Next this operation computes y = (string)x. Then this instruction checks if
|
|
class $1 has an accessible property named y. If it does, this operation
|
|
outputs a base that references the static property. Otherwise, this operation
|
|
throws a fatal error.
|
|
|
|
ElemC [C B] -> [B]
|
|
ElemL <local variable id> [B] -> [B]
|
|
|
|
Fetch element if it exists. First, these operations load a value into x and a
|
|
base into y, as given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
ElemC | $2 | $1
|
|
ElemL | %1 | $1
|
|
|
|
Then, if y is an array, this operation outputs a base that
|
|
references the element at index x from array y. If there is no
|
|
element at index x, this operation outputs a base that contains
|
|
null.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
outputs a base that contains the result of y->offsetGet(x).
|
|
|
|
If y is an object that does not implement the ArrayAccess interface,
|
|
this operation throws a fatal error.
|
|
|
|
If y is a string, this operation checks the status of the StrOff
|
|
flag of base x. If the StrOff flag is true, this instruction throws
|
|
a fatal error. Otherwise, this operation continues to compute z =
|
|
(int)x. If z >= 0 and z < strlen(z), this operation builds a new
|
|
string consisting of the character at offset z from y and outputs a
|
|
base that contains the new string with the StrOff flag set to
|
|
true. Otherwise, this operation outputs a base that contains the
|
|
empty string with the StrOff flag set to true.
|
|
|
|
If y is not a string, array, or object, this operation will output a
|
|
null base.
|
|
|
|
ElemCW [C B] -> [B]
|
|
ElemLW <local variable id> [B] -> [B]
|
|
|
|
Fetch element; warn if it doesn't exist.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
ElemCW | $2 | $1
|
|
ElemLW | %1 | $1
|
|
|
|
If y is an array, this operation outputs a base that references the
|
|
element at index x from array y. If there is no element at index x,
|
|
this operation outputs a base that contains null and raises a
|
|
warning.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this
|
|
operation outputs a base that contains the result of
|
|
y->offsetGet(x).
|
|
|
|
If y is an object that does not implement the ArrayAccess interface,
|
|
this operation throws a fatal error.
|
|
|
|
If y is a string, this operation checks the status of the StrOff
|
|
flag of base y. If the StrOff flag is true, this instruction throws
|
|
a fatal error. Otherwise, this operation continues to compute z =
|
|
(int)x. If z >= 0 and z < strlen(z), this operation builds a new
|
|
string consisting of the character at offset z from y and outputs a
|
|
base that contains the new string with the StrOff flag set to
|
|
true. Otherwise, this operation raises a warning and outputs a base
|
|
that contains the empty string with the StrOff flag set to true.
|
|
|
|
If y is not a string, array, or object, this operation will output
|
|
a null base.
|
|
|
|
ElemCD [C B] -> [B]
|
|
ElemLD <local variable id> [B] -> [B]
|
|
|
|
Fetch element; define it if it doesn't exist.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
ElemCD | $2 | $1
|
|
ElemLD | %1 | $1
|
|
|
|
If y is an array, this operation outputs a base that references the element
|
|
at index x. If there is no element at index x, this operation creates an
|
|
element at index x, and outputs a base that references the element.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
outputs a base that contains the result of y->offsetGet(x).
|
|
|
|
If y is non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If y is null, the empty string, or false, this operation will set y to a
|
|
new empty array, create an element at index x, and output a base that
|
|
references the element.
|
|
|
|
If y is true, integer, double, this operation raises a warning and outputs a
|
|
base that contains null.
|
|
|
|
ElemCWD [C B] -> [B]
|
|
ElemLWD <local variable id> [B] -> [B]
|
|
|
|
Fetch element; warn and define it if it doesn't exist.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
ElemCWD | $2 | $1
|
|
ElemLWD | %1 | $1
|
|
|
|
If y is an array, this operation outputs a base that references the
|
|
element at index x. If there is no element at index x, this
|
|
operation creates an element at index x, raises a warning, and
|
|
outputs a base that references the element.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this
|
|
operation outputs a base that contains the result of
|
|
y->offsetGet(x).
|
|
|
|
If y is non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If y is null, the empty string, or false, this operation will set y
|
|
to a new empty array, create an element at index x, and output a
|
|
base that references the element.
|
|
|
|
If y is true, integer, or double, this operation raises a warning
|
|
and outputs a base that contains null.
|
|
|
|
ElemCU [C B] -> [B]
|
|
ElemLU <local variable id> [B] -> [B]
|
|
|
|
Fetch element for unset.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
ElemCU | $2 | $1
|
|
ElemLU | %1 | $1
|
|
|
|
If y is an array, this operation outputs a base that references the element
|
|
at index x from array y. If there is no element at index x, this operation
|
|
outputs a base that contains null.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
outputs a base that contains the result of y->offsetGet(x).
|
|
|
|
If y is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If y is a string, this operation throws a fatal error.
|
|
|
|
If y is not a string, array, or object, this operation will output a
|
|
null base.
|
|
|
|
NewElem [B] -> [B]
|
|
|
|
Fetch new element. If $1 is an array, this operation creates a new element
|
|
with the next available numeric key in array $1 and outputs a base that
|
|
references the new element.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this operation
|
|
outputs a base that contains the result of $1->offsetGet(null).
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is null, false, or the empty string, this operation sets $1 to a new
|
|
empty array, creates a new element with the next available numeric key in
|
|
array $1, and then outputs a base that references the new element.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
outputs a base that contains null.
|
|
|
|
PropC [C B] -> [B]
|
|
PropL <local variable id> [B] -> [B]
|
|
|
|
Fetch property if it exists.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
PropC | $2 | $1
|
|
PropL | %1 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y->x is visible
|
|
y->x is accessible
|
|
y has eligible __get method
|
|
y->x has been unset previously
|
|
------+---------------------------------------------------------------------
|
|
0XXXX | push null
|
|
10X0X | push null
|
|
10X1X | push ref(y->__get(x))
|
|
1100X | throw fatal error
|
|
1101X | push ref(y->__get(x))
|
|
111X0 | push ref(y->x)
|
|
11101 | push null
|
|
11111 | push ref(y->__get(x))
|
|
|
|
PropCW [C B] -> [B]
|
|
PropLW <local variable id> [B] -> [B]
|
|
|
|
Fetch property; warn if it doesn't exist.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
PropCW | $2 | $1
|
|
PropLW | %1 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y->x is visible
|
|
y->x is accessible
|
|
y has eligible __get method
|
|
y->x has been unset previously
|
|
-----+----------------------------------------------------------------------
|
|
0XXXX | raise warning; push null
|
|
10X0X | raise warning; push null
|
|
10X1X | push ref(y->__get(x))
|
|
1100X | throw fatal error
|
|
1101X | push ref(y->__get(x))
|
|
111X0 | push ref(y->x)
|
|
11101 | raise warning; push null
|
|
11111 | push ref(y->__get(x))
|
|
|
|
PropCD [C B] -> [B]
|
|
PropLD <local variable id> [B] -> [B]
|
|
|
|
Fetch property; define it if it doesn't exist.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
PropCD | $2 | $1
|
|
PropLD | %1 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y is null/false/""
|
|
y->x is visible
|
|
y->x is accessible
|
|
y has eligible __get method
|
|
y->x has been unset previously
|
|
------+---------------------------------------------------------------------
|
|
00XXXX | push null
|
|
01XXXX | y = new stdclass; create property y->x; push ref(y->x)
|
|
1X0X0X | create property y->x; push ref(y->x)
|
|
1X0X1X | push ref(y->__get(x))
|
|
1X100X | throw fatal error
|
|
1X101X | push ref(y->__get(x))
|
|
1X11X0 | push ref(y->x)
|
|
1X1101 | re-create property y->x, push ref(y->x)
|
|
1X1111 | push ref(y->__get(x))
|
|
|
|
PropCWD [C B] -> [B]
|
|
PropLWD <local variable id> [B] -> [B]
|
|
|
|
Fetch property; warn and define it if it doesn't exist.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
PropCWD | $2 | $1
|
|
PropLWD | %1 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y is null/false/""
|
|
y->x is visible
|
|
y->x is accessible
|
|
y has eligible __get method
|
|
y->x has been unset previously
|
|
------+---------------------------------------------------------------------
|
|
00XXXX | raise warning; push null
|
|
01XXXX | raise warning; y = new stdclass; create property y->x;
|
|
| push ref(y->x)
|
|
1X0X0X | raise warning; create property y->x; push ref(y->x)
|
|
1X0X1X | push ref(y->__get(x))
|
|
1X100X | throw fatal error
|
|
1X101X | push ref(y->__get(x))
|
|
1X11X0 | push ref(y->x)
|
|
1X1101 | re-create property y->x, push ref(y->x)
|
|
1X1111 | push ref(y->__get(x))
|
|
|
|
PropCU [C B] -> [B]
|
|
PropLU <local variabld id> [B] -> [B]
|
|
|
|
Fetch property for unset.
|
|
|
|
First, these operations load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
----------+----+-----
|
|
PropCW | $2 | $1
|
|
PropLW | %1 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y->x is visible
|
|
y->x is accessible
|
|
y has eligible __get method
|
|
y->x has been unset previously
|
|
-----+----------------------------------------------------------------------
|
|
0XXXX | push null
|
|
10XXX | create property y->x; push ref(y->x)
|
|
110XX | throw fatal error
|
|
111X0 | push ref(y->x)
|
|
111X1 | re-create property y->x, push ref(y->x)
|
|
|
|
CGetElemC [C B] -> [C]
|
|
CGetElemL <local variable id> [B] -> [C]
|
|
|
|
Get element as cell.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
CGetElemC | $2 | $1
|
|
CGetElemL | %1 | $1
|
|
|
|
If y is an array, this operation retrieves the element at index x from
|
|
array y and pushes it onto the stack as a cell. If there is no element at
|
|
index x, this operation raises a warning and pushes null onto the stack.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
pushes x->offsetGet($2) onto the stack.
|
|
|
|
If y is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If y is a string, this operation checks the status of the StrOff flag of
|
|
base y. If the StrOff flag is true, this operation throws a fatal error.
|
|
Otherwise this operation continues to compute z = (int)x. If z >= 0 and
|
|
z < strlen(z), this operation builds a new string consisting of the character
|
|
at offset z from y and pushes it onto the stack. Otherwise, this operation
|
|
raises a warning and pushes the empty string onto the stack.
|
|
|
|
If y is not a string, array, or object, this operation will push null onto
|
|
the stack.
|
|
|
|
VGetElemC [C B] -> [V]
|
|
VGetElemL <local variable id> [B] -> [V]
|
|
|
|
Get element as var.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
VGetElemC | $2 | $1
|
|
VGetElemL | %1 | $1
|
|
|
|
If y is an array, this operation retrieves the element at index x from
|
|
array y and pushes it onto the stack as a var. If there is no element at
|
|
index x, this operation creates a new element at index x, and pushes it
|
|
onto the stack as a var.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
pushes y->offsetGet(x) onto the stack as a var.
|
|
|
|
If y is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If y is null, false, or the empty string, this operation sets y to a new
|
|
empty array. Then this operation retrieves the element at index x from array
|
|
y and pushes it onto the stack as a var. If there is no element at index x,
|
|
this operation creates a new element at index x, and pushes it onto the
|
|
stack as a var.
|
|
|
|
If y is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
IssetElemC [C B] -> [C:Bool]
|
|
IssetElemL <local variable id> [B] -> [C:Bool]
|
|
|
|
Isset element.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
IssetElemC | $2 | $1
|
|
IssetElemL | %1 | $1
|
|
|
|
If y is an array, this operation pushes !is_null(y[x]) onto the stack.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
pushes y->offsetExists(x) onto the stack.
|
|
|
|
If y is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If y is a string, this operation first checks the status of the StrOff flag
|
|
of base y. If the StrOff flag is true, this operation pushes false.
|
|
Otherwise this operation continues to compute x = (int)x and then it pushes
|
|
(x >= 0 && x < strlen(y)) onto the stack.
|
|
|
|
If y is a not a string, array, or object, this operation pushes false onto
|
|
the stack.
|
|
|
|
EmptyElemC [C B] -> [C]
|
|
EmptyElemL <local variable id> [B] -> [C]
|
|
|
|
Empty element.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
EmptyElemC | $2 | $1
|
|
EmptyElemL | %1 | $1
|
|
|
|
If y is an array, this operation pushes !(y[x]) onto the stack.
|
|
|
|
If y is an object that implements the ArrayAccess interface, this operation
|
|
first calls y->offsetExists(x); if that returns false this operation pushes
|
|
true onto the stack, otherwise it pushes !(y->offsetGet(x)) onto the stack.
|
|
|
|
If y is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If y is a string, this operation first checks the status of the StrOff flag
|
|
of base y. If the StrOff flag is true, this operation pushes true.
|
|
Otherwise this operation computes z = (int)x, then pushes true if
|
|
(z < 0 || z >= strlen(y)), !(y[z]) otherwise.
|
|
|
|
If y is, not an array, object, or string, this operation pushes true onto
|
|
the stack.
|
|
|
|
SetElemC [C C B] -> [C]
|
|
|
|
Set element. If $1 is an array, this operation executes $1[$3] = $2 and then
|
|
pushes $2 onto the stack.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this operation
|
|
executes $1->offsetSet($3, $2) and then pushes $2 onto the stack.
|
|
|
|
If $1 is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If $1 is null, the empty string, or false, this operation sets $1 to a new
|
|
empty array, executes $1[$3] = $2, and then pushes $2 onto the stack.
|
|
|
|
If $1 is a non-empty string, this operation first computes x = (int)$3. If x
|
|
is negative, this operation raises a warning and does nothing else. If x is
|
|
nonnegative, this operation appends spaces to the end of $1 as needed to
|
|
ensure that x is in bounds, then it computes y = substr((string)$2,0,1), and
|
|
then it sets the character at index x in $1 equal to y (if y is not empty) or
|
|
it sets the character at index x in $1 to "\0" (if y is empty). Then this
|
|
operation pushes y on to the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and pushes
|
|
null onto the stack as a cell.
|
|
|
|
SetElemL <local variable id> [C B] -> [C]
|
|
|
|
Set element. If $1 is an array, this operation executes $1[%1] = $2 and then
|
|
pushes $2 onto the stack.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this operation
|
|
executes $1->offsetSet(%1, $2) and then pushes $2 onto the stack.
|
|
|
|
If $1 is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If $1 is null, the empty string, or false, this operation sets $1 to a new
|
|
empty array, executes $1[%1] = $2, and then pushes $2 onto the stack.
|
|
|
|
If $1 is a non-empty string, this operation first computes x = (int)%1. If x
|
|
is negative, this operation raises a warning and does nothing else. If x is
|
|
nonnegative, this operation appends spaces to the end of $1 as needed to
|
|
ensure that x is in bounds, then it computes y = substr((string)$2,0,1), and
|
|
then it sets the character at index x in $1 equal to y (if y is not empty) or
|
|
it sets the character at index x in $1 to "\0" (if y is empty). Then this
|
|
operation pushes y on to the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and pushes
|
|
null onto the stack as a cell.
|
|
|
|
SetOpElemC <op> [C C B] -> [C]
|
|
|
|
Set element op. If $1 is an array, this operation first checks $1 contains
|
|
an element at offset $2. If it does not, this operation creates an element
|
|
at offset $2, sets it to null, and raises a warning. Next, this operation
|
|
executes x = $1[$3], y = x <op> $2, and $1[$3] = y, and then it pushes y onto
|
|
the stack as a cell.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to
|
|
a new empty array. Then it follows the rules described in the case above.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this
|
|
operation executes x = $1->offsetGet($3), y = x <op> $2, and
|
|
$1->offsetSet($3, y), and then it pushes y onto the stack as a cell.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
SetOpElemL <op> <local variable id> [C B] -> [C]
|
|
|
|
Set element op. If $1 is an array, this operation first checks $1 contains
|
|
an element at offset $2. If it does not, this operation creates an element
|
|
at offset $2, sets it to null, and raises a warning. Next, this operation
|
|
executes x = $1[%1], y = x <op> $2, and $1[%1] = y, and then it pushes y onto
|
|
the stack as a cell.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to
|
|
a new empty array. Then it follows the rules described in the case above.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this
|
|
operation executes x = $1->offsetGet(%1), y = x <op> $2, and
|
|
$1->offsetSet(%1, y), and then it pushes y onto the stack as a cell.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
IncDecElemC <op> [C B] -> [C]
|
|
|
|
Increment/decrement element. If $1 is an array, this operation checks if $1
|
|
contains an element at offset $2. If it does not, this operation creates an
|
|
element at offset $2, sets it to null, and raises a warning. Next, this
|
|
operation executes x = $1[$2], y = x, and either ++y (if op is PreInc or
|
|
PostInc) or --y (if op is PreDec or PostDec). Then it assigns y to $1[$2]
|
|
and pushes either y (if op is PreInc or PreDec) or x (if op is PostInc or
|
|
PostDec) onto the stack.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to
|
|
an empty array. Then it follows the rules described in the case above.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is an object that implements ArrayAccess, this operation executes
|
|
x = $1->offsetGet($2), y = x, and either ++y (if op is PreInc or PostInc) or
|
|
--y (if op is PreDec or PostDec). Then it pushes either y (if op is PreInc or
|
|
PreDec) or x (if op is PostInc or PostDec) onto the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
IncDecElemL <op> <local variable id> [B] -> [C]
|
|
|
|
Increment/decrement element. If $1 is an array, this operation checks if $1
|
|
contains an element at offset %1. If it does not, this operation creates an
|
|
element at offset %1, sets it to null, and raises a warning. Next, this
|
|
operation executes x = $1[%1], y = x, and either ++y (if op is PreInc or
|
|
PostInc) or --y (if op is PreDec or PostDec). Then it assigns y to $1[%1]
|
|
and pushes either y (if op is PreInc or PreDec) or x (if op is PostInc or
|
|
PostDec) onto the stack.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to
|
|
an empty array. Then it follows the rules described in the case above.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is an object that implements ArrayAccess, this operation executes
|
|
x = $1->offsetGet(%1), y = x, and either ++y (if op is PreInc or PostInc) or
|
|
--y (if op is PreDec or PostDec). Then it pushes either y (if op is PreInc or
|
|
PreDec) or x (if op is PostInc or PostDec) onto the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
BindElemC [C V B] -> [V]
|
|
BindElemL <local variable id> [V B] -> [V]
|
|
|
|
Bind element.
|
|
|
|
This instruction first loads a value into x, from $3 or the local
|
|
referred to by %1.
|
|
|
|
If $1 is an array, this operation executes $1[x] =& $2 and pushes $2
|
|
onto the stack as a var.
|
|
|
|
If $1 is an object, this operation throws a fatal error.
|
|
|
|
If $1 is null, false, or the empty string, this operation sets $1 to a new
|
|
empty array, executes $1[x] =& $2, and pushes $2 onto the stack as a var.
|
|
|
|
If $1 is a non-empty string, this operation throws a fatal error.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning.
|
|
|
|
UnsetElemC [C B] -> []
|
|
UnsetElemL <local variable id> [B] -> []
|
|
|
|
Unset element.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
UnsetElemL | %1 | $1
|
|
UnsetElemC | $2 | $1
|
|
|
|
If y is an array, this operation removes the element at index x
|
|
from array y.
|
|
|
|
If y is an object that implements ArrayAccess interface, this operation
|
|
executes y->offsetUnset(x).
|
|
|
|
If y is an object that does not implement the ArrayAccess interface, this
|
|
operation throws a fatal error.
|
|
|
|
If y is a string, this operation throws a fatal error.
|
|
|
|
If y is not a string, array, or object, this operation does nothing.
|
|
|
|
VGetNewElem [B] -> [V]
|
|
|
|
Get new element as var.
|
|
|
|
If $1 is an array, this operation creates a new element with the next
|
|
available numeric key in array $1 and pushes it onto the stack as a var.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this operation
|
|
pushes $1->offsetGet($2) onto the stack as a var.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to a
|
|
new empty array. Then it creates a new element with the next available
|
|
numeric key in array $1 and pushes it onto the stack as a var.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
SetNewElem [C B] -> [C]
|
|
|
|
Set new element. If $1 is an array, this operation executes $1[] = $2 and
|
|
then pushes $2 onto the stack.
|
|
|
|
If $1 is null, false, or the empty string, this operation sets $1 to a new
|
|
empty array, and then it executes $1[] = $2 and pushes $2 onto the stack.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is an object that implements the ArrayAccess interface, this operation
|
|
executes $1->offsetSet(null, $2) and then pushes $2 onto the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
SetOpNewElem <op> [C B] -> [C]
|
|
|
|
Set op new element. If $1 is an array, this operation first determines the
|
|
next available integer offset k in array $1. Next, this operation executes
|
|
$1[k] = null, x = $1[k], and y = x <op> $2. Then it assigns y to $1[k] and
|
|
pushes y onto the stack.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to
|
|
an empty array. Then it follows the rules described in the case above.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is an object that implements ArrayAccess, this operation executes
|
|
x = $1->offsetGet(null), y = x <op> $2, and $1->offsetSet(null, y). Then it
|
|
pushes y onto the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
IncDecNewElem <op> [B] -> [C]
|
|
|
|
Increment/decrement new element. If $1 is an array, this operation first
|
|
determines the next available integer offset k in array $1. Next, this
|
|
operation executes $1[k] = null, x = $1[k], y = x, and either ++y (if op is
|
|
PreInc or PostInc) or --y (if op is PreDec or PostDec). Then it assigns y to
|
|
$1[k] and pushes either y (if op is PreInc or PreDec) or x (if op is PostInc
|
|
or PostDec) onto the stack.
|
|
|
|
If $1 is null, false, or the empty string, this operation first sets $1 to
|
|
an empty array. Then it follows the rules described in the case above.
|
|
|
|
If $1 is a non-empty string or an object that does not implement the
|
|
ArrayAccess interface, this operation throws a fatal error.
|
|
|
|
If $1 is an object that implements ArrayAccess, this operation executes x =
|
|
$1->offsetGet(null), y = x, and either ++y (if op is PreInc or PostInc) or
|
|
--y (if op is PreDec or PostDec). Then it pushes either y (if op is PreInc or
|
|
PreDec) or x (if op is PostInc or PostDec) onto the stack.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
BindNewElem [V B] -> [V]
|
|
|
|
Bind new element. If $1 is an array, this operation executes $1[] =& $2 and
|
|
then it pushes $2 onto the stack.
|
|
|
|
If $1 is null, false, or empty string, this operation sets $1 to a new empty
|
|
array, executes $1[] =& $2, and pushes $2 onto the stack.
|
|
|
|
If $1 is a non-empty string or an object, this operation throws a fatal
|
|
error.
|
|
|
|
If $1 is true, integer, or double, this operation raises a warning and
|
|
pushes null onto the stack.
|
|
|
|
CGetPropC [C B] -> [C]
|
|
CGetPropL <local variable id> [B] -> [C]
|
|
|
|
Get property as cell.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
CGetPropC | $2 | $1
|
|
CGetPropL | %1 | $1
|
|
|
|
If y is an object that does not have an eligible __get method, this
|
|
operation first checks if y has a visible property named x. If it
|
|
does not, this operation raises a warning and pushes
|
|
null. Otherwise, this operation continues to check if the property
|
|
named x is accessible. If the property named x is accessible this
|
|
operation pushes it onto the stack as a cell, otherwise this
|
|
operation throws a fatal error.
|
|
|
|
If y is an object that has an eligible __get method, this operation
|
|
checks if y has a visible and accessible property named x. If it
|
|
does, this operation pushes the property onto the stack. Otherwise,
|
|
this operation pushes y->__get(x) onto the stack.
|
|
|
|
If y is not an object, this operation will raise a warning and push
|
|
null onto the stack.
|
|
|
|
VGetPropC [C B] -> [V]
|
|
VGetPropL <local variable id> [B] -> [V]
|
|
|
|
Get property as var.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
------------+----+-----
|
|
VGetPropC | $2 | $1
|
|
VGetPropL | %1 | $1
|
|
|
|
If y is an object that does not have an eligible __get method, this
|
|
operation first checks if y has a visible property named x. If it
|
|
does not, this operation will create a new property named x and push
|
|
it onto the stack as a var. Otherwise this operation continues to
|
|
check if the property named x is accessible. If it the property
|
|
named x is accessible this operation pushes it onto the stack as a
|
|
var, otherwise this operation throws a fatal error.
|
|
|
|
If y is an object has an eligible __get method, this operation
|
|
checks if y has a visible and accessible property named x. If it
|
|
does, this operation pushes the property onto the stack. Otherwise,
|
|
this operation pushes y->__get(x) onto the stack.
|
|
|
|
If y is null, false, or the empty string, this operation will set y
|
|
to a new object of type stdclass, create a new property named x, and
|
|
pushes it onto the stack.
|
|
|
|
If y is true, integer, double, a non-empty string, or an array, this
|
|
operation raises a warning and pushes null.
|
|
|
|
IssetPropC [C B] -> [C:Bool]
|
|
IssetPropL <local variable id> [B] -> [C:Bool]
|
|
|
|
Isset property.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
-------------+----+-----
|
|
IssetPropC | $2 | $1
|
|
IssetPropL | %1 | $1
|
|
|
|
If y is an object that does not have an eligible __isset method,
|
|
this operation checks if y has a visible accessible property named
|
|
x. If it does, this operation pushes !is_null(y->x) onto the
|
|
stack. Otherwise this operation pushes false onto the stack.
|
|
|
|
If y is an object that has an eligible __isset method, this operation checks
|
|
if y has a visible and accessible property named x. If it does, this
|
|
operation pushes !is_null(y->x) onto the stack. Otherwise this operation
|
|
pushes y->__isset(x) onto the stack.
|
|
|
|
If y is an array, this operation pushes !is_null(y[x]) onto the stack.
|
|
|
|
If y is not an object or array, this operation pushes false.
|
|
|
|
EmptyPropC [C B] -> [C:Bool]
|
|
EmptyPropL <local variable id> [B] -> [C:Bool]
|
|
|
|
Empty property.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
-------------+----+-----
|
|
EmptyPropC | $2 | $1
|
|
EmptyPropL | %1 | $1
|
|
|
|
If y is an object that does not have an eligible __isset method, this
|
|
operation first checks if y has a visible and accessible property named x.
|
|
If it does, this operation pushes !(y->x) onto the stack. Otherwise this
|
|
operation pushes true onto the stack.
|
|
|
|
If y is an object that has an eligible __isset method but it does not have
|
|
an eligible __get method, this operation checks if y has a visible and
|
|
accessible property named x. If it does, this operation pushes !(y->x) onto
|
|
the stack. Otherwise this operation pushes !(y->__isset(x)) onto the stack.
|
|
|
|
If y is an object that has an eligible __isset method and an eligible __get
|
|
method, this operation checks if y has a visible and accessible property
|
|
named x. If it does, this operation pushes !(y->x) onto the stack. Otherwise
|
|
this operation continues to execute x = y->__isset(x). If x is false this
|
|
operation pushes true onto the stack, otherwise this operation pushes
|
|
!(y->__get(x)) onto the stack.
|
|
|
|
If y is an array, this operation pushes !(y[x]) onto the stack.
|
|
|
|
If y is not an object or array, this operation pushes true.
|
|
|
|
SetPropC [C C B] -> [C]
|
|
SetPropL <local variable id> [C B] -> [C]
|
|
|
|
Set property. Perform one of the following actions:
|
|
|
|
First, these operations load values into k and x, and a base into y,
|
|
as given by the following table:
|
|
|
|
operation k x y
|
|
----------+----+----+----
|
|
SetPropC | $3 | $2 | $1
|
|
SetPropL | %1 | $2 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y is null/false/""
|
|
y->k is visible
|
|
y->k is accessible
|
|
y has eligible __set method
|
|
y->k has been unset previously
|
|
------+---------------------------------------------------------------------
|
|
00XXXX | raise warning; push null
|
|
01XXXX | y = new stdclass; y->k = x; push x
|
|
1X0X0X | create property y->k; y->k = x; push x
|
|
1X0X1X | y->__set(k, x); push x
|
|
1X100X | throw fatal error
|
|
1X101X | y->__set(k, x); push x
|
|
1X11X0 | y->k = x; push x
|
|
1X1101 | re-create property y->k; y->k = x; push x
|
|
1X1111 | y->__set(k, x); push x
|
|
|
|
SetOpPropC <op> [C C B] -> [C]
|
|
|
|
Set op property. Perform one of the following actions:
|
|
|
|
$1 is an object
|
|
$1 is null/false/""
|
|
$1->$3 is visible
|
|
$1->$3 is accessible
|
|
$1 has eligible __get method
|
|
$1 has eligible __set method
|
|
$1->$3 has been unset previously
|
|
-------+--------------------------------------------------------------------
|
|
00XXXXX | raise warning; push null
|
|
01XXXXX | $1 = new stdclass; y = null <op> $2; $1->$3 = y; push y
|
|
100X0XX | y = null <op> $2; $1->$3 = y; push y
|
|
100X10X | x = $1->__get($3); y = x <op> $2; $1->$3 = y; push y
|
|
100X11X | x = $1->__get($3); y = x <op> $2; $1->__set($3, y), push y
|
|
10100XX | throw fatal error
|
|
101010X | throw fatal error
|
|
101011X | x = $1->__get($3); y = x <op> $2; $1->__set($3, y), push y
|
|
1011XX0 | x = $1->$3; y = x <op> $2; $1->$3 = y; push y
|
|
10110X1 | y = null <op> $2; re-create $1->$3; $1->$3 = y; push y
|
|
1011101 | x = $1->__get($3); y = x <op> $2; re-create $1->$3; $1->$3 = y;
|
|
| push y
|
|
1011111 | x = $1->__get($3); y = x <op> $2; $1->__set($3, y); push y
|
|
|
|
SetOpPropL <op> <local variable id> [C B] -> [C]
|
|
|
|
Set op property. Perform one of the following actions, where k is
|
|
the value of the local given by %2.
|
|
|
|
$1 is an object
|
|
$1 is null/false/""
|
|
$1->k is visible
|
|
$1->k is accessible
|
|
$1 has eligible __get method
|
|
$1 has eligible __set method
|
|
$1->k has been unset previously
|
|
-------+--------------------------------------------------------------------
|
|
00XXXXX | raise warning; push null
|
|
01XXXXX | $1 = new stdclass; y = null <op> $2; $1->k = y; push y
|
|
100X0XX | y = null <op> $2; $1->k = y; push y
|
|
100X10X | x = $1->__get(k); y = x <op> $2; $1->k = y; push y
|
|
100X11X | x = $1->__get(k); y = x <op> $2; $1->__set(k, y), push y
|
|
10100XX | throw fatal error
|
|
101010X | throw fatal error
|
|
101011X | x = $1->__get(k); y = x <op> $2; $1->__set(k, y), push y
|
|
1011XX0 | x = $1->k; y = x <op> $2; $1->k = y; push y
|
|
10110X1 | y = null <op> $2; re-create $1->k; $1->k = y; push y
|
|
1011101 | x = $1->__get(k); y = x <op> $2; re-create $1->k; $1->k = y;
|
|
| push y
|
|
1011111 | x = $1->__get(k); y = x <op> $2; $1->__set(k, y); push y
|
|
|
|
IncDecPropC <op> [C B] -> [C]
|
|
|
|
Increment/decrement property. Perform one of the following actions:
|
|
|
|
$1 is an object
|
|
$1 is null/false/""
|
|
$1->$2 is visible
|
|
$1->$2 is accessible
|
|
$1 has eligible __get method
|
|
$1 has eligible __set method
|
|
$1->$2 has been unset previously
|
|
-------+--------------------------------------------------------------------
|
|
00XXXXX | raise warning; push null
|
|
01XXXXX | $1 = new stdclass; x = null; y = x; <op>y; $1->$2 = y;
|
|
| push y (Pre*) or x (Post*)
|
|
100X0XX | x = null; y = x; <op>y; $1->$2 = y; push y (Pre*) or x (Post*)
|
|
100X10X | x = $1->__get($2); y = x; <op>y; $1->$2 = y;
|
|
| push y (Pre*) or x (Post*)
|
|
100X11X | x = $1->__get($2); y = x, <op>y; $1->__set($2, y);
|
|
| push y (Pre*) or x (Post*)
|
|
10100XX | throw fatal error
|
|
101010X | throw fatal error
|
|
101011X | x = $1->__get($2); y = x, <op>y; $1->__set($2, y);
|
|
| push y (Pre*) or x (Post*)
|
|
1011XX0 | x = $1->$2; y = x; <op>y; $1->$2 = y; push y (Pre*) or x (Post*)
|
|
10110X1 | x = null; y = x; <op>y; re-create $1->$2; $1->$2 = y;
|
|
| push y (Pre*) or x (Post*)
|
|
1011101 | x = $1->__get($2); y = x; <op>y; re-create $1->$2; $1->$2 = y;
|
|
| push y (Pre*) or x (Post*)
|
|
1011111 | x = $1->__get($2); y = x; <op>y; $1->__set($2, y);
|
|
| push y (Pre*) or x (Post*)
|
|
|
|
IncDecPropL <op> <local variable id> [B] -> [C]
|
|
|
|
Increment/decrement property. Perform one of the following actions,
|
|
where k is the value of the local variable given by %2.
|
|
|
|
$1 is an object
|
|
$1 is null/false/""
|
|
$1->k is visible
|
|
$1->k is accessible
|
|
$1 has eligible __get method
|
|
$1 has eligible __set method
|
|
$1->k has been unset previously
|
|
-------+--------------------------------------------------------------------
|
|
00XXXXX | raise warning; push null
|
|
01XXXXX | $1 = new stdclass; x = null; y = x; <op>y; $1->k = y;
|
|
| push y (Pre*) or x (Post*)
|
|
100X0XX | x = null; y = x; <op>y; $1->k = y; push y (Pre*) or x (Post*)
|
|
100X10X | x = $1->__get(k); y = x; <op>y; $1->k = y;
|
|
| push y (Pre*) or x (Post*)
|
|
100X11X | x = $1->__get(k); y = x, <op>y; $1->__set(k, y);
|
|
| push y (Pre*) or x (Post*)
|
|
10100XX | throw fatal error
|
|
101010X | throw fatal error
|
|
101011X | x = $1->__get(k); y = x, <op>y; $1->__set(k, y);
|
|
| push y (Pre*) or x (Post*)
|
|
1011XX0 | x = $1->k; y = x; <op>y; $1->k = y; push y (Pre*) or x (Post*)
|
|
10110X1 | x = null; y = x; <op>y; re-create $1->k; $1->k = y;
|
|
| push y (Pre*) or x (Post*)
|
|
1011101 | x = $1->__get(k); y = x; <op>y; re-create $1->k; $1->k = y;
|
|
| push y (Pre*) or x (Post*)
|
|
1011111 | x = $1->__get(k); y = x; <op>y; $1->__set(k, y);
|
|
| push y (Pre*) or x (Post*)
|
|
|
|
BindPropC [C V B] -> [V]
|
|
|
|
Bind property. If $1 is an object that does not have an eligible __set method,
|
|
this operation first checks if $1 has a visible property named $3. If it does
|
|
not, this operation creates a new property named $3, executes $1->$3 =& $2,
|
|
and pushes $2 onto the stack. Otherwise, this operation continues to check if
|
|
the property named $3 is accessible. If the property named $3 is not
|
|
accessible, this operation throws a fatal error. Otherwise, this operation
|
|
executes $1->$3 =& $2 and pushes $2 onto the stack.
|
|
|
|
If $1 is an object that has an eligible __set method, this operation checks
|
|
if $1 has a visible and accessible property named $3. If it does, this
|
|
operation follows the rules described in the first case given above.
|
|
Otherwise this operation throws a fatal error.
|
|
|
|
If $1 is null, false, or empty string, this operation sets $1 to a new object
|
|
of type stdclass, executes $1->$3 =& $2, and pushes $2 onto the stack.
|
|
|
|
If $1 is true, integer, double, a non-empty string, or an array, this
|
|
operation raises a warning and pushes null onto the stack.
|
|
|
|
BindPropL <local variable id> [V B] -> [V]
|
|
|
|
Bind property. Where k is the value of the local variable given by %1:
|
|
|
|
If $1 is an object that does not have an eligible __set method,
|
|
this operation first checks if $1 has a visible property named k. If it does
|
|
not, this operation creates a new property named k, executes $1->k =& $2,
|
|
and pushes $2 onto the stack. Otherwise, this operation continues to check if
|
|
the property named k is accessible. If the property named k is not
|
|
accessible, this operation throws a fatal error. Otherwise, this operation
|
|
executes $1->k =& $2 and pushes $2 onto the stack.
|
|
|
|
If $1 is an object that has an eligible __set method, this operation checks
|
|
if $1 has a visible and accessible property named k. If it does, this
|
|
operation follows the rules described in the first case given above.
|
|
Otherwise this operation throws a fatal error.
|
|
|
|
If $1 is null, false, or empty string, this operation sets $1 to a new object
|
|
of type stdclass, executes $1->k =& $2, and pushes $2 onto the stack.
|
|
|
|
If $1 is true, integer, double, a non-empty string, or an array, this
|
|
operation raises a warning and pushes null onto the stack.
|
|
|
|
UnsetPropC [C B] -> []
|
|
UnsetPropL <local variable id> [B] -> []
|
|
|
|
Unset property.
|
|
|
|
These instructions first load a value into x and a base into y, as
|
|
given by the following table:
|
|
|
|
operation x y
|
|
-------------+----+-----
|
|
UnsetPropC | $2 | $1
|
|
UnsetPropL | %1 | $1
|
|
|
|
Next, performs one of the following actions:
|
|
|
|
y is an object
|
|
y->x is visible
|
|
y->x is accessible
|
|
y has eligible __unset method
|
|
-----+----------------------------------------------------------------------
|
|
0XXX | do nothing
|
|
10X0 | do nothing
|
|
10X1 | y->__unset(x)
|
|
1100 | throw fatal error
|
|
1101 | y->__unset(x)
|
|
111X | unset(y->x)
|
|
|
|
|
|
10. Member instructions
|
|
-----------------------
|
|
|
|
Member instructions perform series of operations that are structurally
|
|
identical, but each instruction utilizes a distinct set of operations. For each
|
|
member instruction, first use a Base* operation depending on the kind of
|
|
location code. Next perform a series of intermediate operations depending on
|
|
member code to process all but the last member. Finally, perform a final
|
|
operation depending on member code to process the last member. See the
|
|
instruction-specific tables for details.
|
|
|
|
The member codes that represent immediate literal data (ET, EI, PT) are
|
|
implemented using the corresponding EC or PC intermediate operation: they
|
|
behave exactly as though that literal data had been pushed on the stack as a
|
|
cell, then consumed by an ElemC* or PropC* operation.
|
|
|
|
CGetM <loc-desc/M-vector> [C..C] -> [C]
|
|
|
|
Get member as cell.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+----------
|
|
C | BaseC EC | ElemCW | CGetElemC
|
|
R | BaseR PC | PropCW | CGetPropC
|
|
L | BaseLW EL | ElemLW | CGetElemL
|
|
NC | BaseNCW PL | PropLW | CGetPropL
|
|
NL | BaseNLW W | N/A | N/A
|
|
GC | BaseGCW
|
|
GL | BaseGLW
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
VGetM <loc-desc/M-vector> [C..C] -> [V]
|
|
|
|
Get member as var.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+------------
|
|
C | BaseC EC | ElemCD | VGetElemC
|
|
R | BaseR PC | PropCD | VGetPropC
|
|
L | BaseLD EL | ElemLD | VGetElemL
|
|
NC | BaseNCD PL | PropLD | VGetPropL
|
|
NL | BaseNLD W | NewElem | VGetNewElem
|
|
GC | BaseGCD
|
|
GL | BaseGLD
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
FPassM <param id> <loc-desc/M-vector> [C..C] -> [F]
|
|
|
|
FPI pass parameter. This instruction behaves as CGetM if parameter
|
|
%1 is pass by value, or it behaves like VGetM if parameter %1 is
|
|
pass by reference. Then it passes the value produced to the callee.
|
|
|
|
IssetM <loc-desc/M-vector> [C..C] -> [C:Bool]
|
|
|
|
Isset member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+-----------
|
|
C | BaseC EC | ElemC | IssetElemC
|
|
R | BaseR PC | PropC | IssetPropC
|
|
L | BaseL EL | ElemL | IssetElemL
|
|
NC | BaseNC PL | PropL | IssetPropL
|
|
NL | BaseNL W | N/A | N/A
|
|
GC | BaseGC
|
|
GL | BaseGL
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
EmptyM <loc-desc/M-vector> [C..C] -> [C:Bool]
|
|
|
|
Empty member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+-----------
|
|
C | BaseC EC | ElemC | EmptyElemC
|
|
R | BaseR PC | PropC | EmptyPropC
|
|
L | BaseL EL | ElemL | EmptyElemL
|
|
NC | BaseNC PL | PropL | EmptyPropL
|
|
NL | BaseNL W | N/A | N/A
|
|
GC | BaseGC
|
|
GL | BaseGL
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
SetM <loc-desc/M-vector> [C..C C] -> [C]
|
|
|
|
Set member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+-----------
|
|
C | BaseC EC | ElemCD | SetElemC
|
|
R | BaseR PC | PropCD | SetPropC
|
|
L | BaseLD EL | ElemLD | SetElemL
|
|
NC | BaseNCD PL | PropLD | SetPropL
|
|
NL | BaseNLD W | NewElem | SetNewElem
|
|
GC | BaseGCD
|
|
GL | BaseGLD
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
SetOpM <op> <loc-desc/M-vector> [C..C C] -> [C]
|
|
|
|
Set op member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+-------------
|
|
C | BaseC EC | ElemCWD | SetOpElemC
|
|
R | BaseR PC | PropCWD | SetOpPropC
|
|
L | BaseLWD EL | ElemLWD | SetOpElemL
|
|
NC | BaseNCWD PL | PropLWD | SetOpPropL
|
|
NL | BaseNLWD W | NewElem | SetOpNewElem
|
|
GC | BaseGCWD
|
|
GL | BaseGLWD
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
IncDecM <op> <loc-desc/M-vector> [C..C] -> [C]
|
|
|
|
Increment/decrement member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+--------------
|
|
C | BaseC EC | ElemCWD | IncDecElemC
|
|
R | BaseR PC | PropCWD | IncDecPropC
|
|
L | BaseLWD EL | ElemLWD | IncDecElemL
|
|
NC | BaseNCWD PL | PropLWD | IncDecPropL
|
|
NL | BaseNLWD W | NewElem | IncDecNewElem
|
|
GC | BaseGCWD
|
|
GL | BaseGLWD
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
BindM <loc-desc/M-vector> [C..C V] -> [V]
|
|
|
|
Bind member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+------------
|
|
C | BaseC EC | ElemCD | BindElemC
|
|
R | BaseR PC | PropCD | BindPropC
|
|
L | BaseLD EL | ElemLD | BindElemL
|
|
NC | BaseNCD PL | PropLD | BindPropL
|
|
NL | BaseNLD W | NewElem | BindNewElem
|
|
GC | BaseGCD
|
|
GL | BaseGLD
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
UnsetM <loc-desc/M-vector> [C..C] -> []
|
|
|
|
Unset member.
|
|
|
|
location Base* member intermediate final
|
|
descriptor operation code operation operation
|
|
-----------+---------- -------+--------------+-----------
|
|
C | BaseC EC | ElemCU | UnsetElemC
|
|
R | BaseR PC | PropCU | UnsetPropC
|
|
L | BaseL EL | ElemLU | UnsetElemL
|
|
NC | BaseNC PL | PropLU | UnsetPropL
|
|
NL | BaseNL W | N/A | N/A
|
|
GC | BaseGC
|
|
GL | BaseGL
|
|
SC | BaseSC
|
|
SL | BaseSL
|
|
|
|
|
|
11. Iterator instructions
|
|
-------------------------
|
|
|
|
IterInit <iterator id> <rel offset> <local id> [C] -> []
|
|
IterInitK <iterator id> <rel offset> <local id> <local id> [C] -> []
|
|
|
|
Initialize iterator. If $1 is an array, these instructions create an array
|
|
iterator, rewind the array iterator to point to the beginning of the array,
|
|
and store the array iterator in the iterator variable %1. Then these
|
|
instructions check if the iterator is at the end, and if it is, these
|
|
instructions free the iterator and transfer control to the location specified
|
|
by %2.
|
|
|
|
If $1 is an object that is an instance of an extension class that implements
|
|
the Traversable interface, these instructions create an extension class
|
|
iterator and store it in the iterator variable %1. Then these instructions
|
|
check if the iterator is at the end, and if it is these instructions free the
|
|
iterator and transfer control the location specified by %2.
|
|
|
|
If $1 is an object that implements the Iterator interface, these instructions
|
|
create an user class iterator, call $1->rewind(), and store the user class
|
|
iterator in the iterator variable %1. Then these instructions check if
|
|
$1->valid() returns false, and if it does these instructions free the iterator
|
|
and transfer control to the location specified by %2.
|
|
|
|
If $1 is an object that implements the IteratorAggregate interface, these
|
|
instructions call $1->getIterator() and inspect the object x that is
|
|
returned. If x is an instance of IteratorAggregate, these instructions will
|
|
repeatedly execute "x = x->getIterator()" until x is not an object that is an
|
|
instance of IteratorAggregate. If x is an object that implements the
|
|
Traversable interface, then this instruction will behave according to the
|
|
appropriate case described above. Otherwise, these instructions will throw an
|
|
exception of type Exception.
|
|
|
|
If $1 is an object that does not match any of the three cases above, these
|
|
instructions create a default class iterator, rewind the default class
|
|
iterator to point to the first accessible property, and store the default
|
|
class iterator in the iterator variable %1. Then these instructions check if
|
|
the iterator is at the end, and if it is these instructions free the iterator
|
|
and transfer control the location specified by %2.
|
|
|
|
If $1 is not an array or an object, these instructions raise a warning and
|
|
transfer control to the location specified by %2.
|
|
|
|
If the iterator specified by %1 is a non-mutable array iterator or an
|
|
extension class iterator, these instructions store a copy of the current
|
|
value in %3 as a cell.
|
|
|
|
If the iterator specified by %1 is a user class iterator for object $x, these
|
|
instructions store the return value of $x->current() in %3 as a cell.
|
|
|
|
If the iterator specified by %1 is a non-mutable default class iterator, these
|
|
instructions store a copy of the current property in %3 as a cell.
|
|
|
|
For the IterInitK version, the following also happens:
|
|
|
|
If the iterator specified by %1 is an array iterator or an extension class
|
|
iterator, this instruction stores a copy of the current key in %4 as a cell.
|
|
|
|
If the iterator specified by %1 is a user class iterator for object $x, this
|
|
instruction stores the return value of $x->key() in %4 as a cell.
|
|
|
|
If the iterator specified by %1 is a non-mutable default class iterator,
|
|
this instruction stores a copy of the name of the current property in %4 as a
|
|
cell.
|
|
|
|
MIterInit <iterator id> <rel offset> <local id> [V] -> []
|
|
MIterInitK <iterator id> <rel offset> <local id> <local id> [V] -> []
|
|
|
|
Initialize mutable iterator. If $1 is an array, these instructions create a
|
|
mutable array iterator, rewind the mutable array iterator to point to the
|
|
beginning of the array, and store the mutable array iterator in the iterator
|
|
variable %1. Then these instructions check if the iterator is at the end, and
|
|
if it is these instructions free the iterator and transfers control the
|
|
location specified by %2.
|
|
|
|
If $1 is an object that is an instance of an extension class that implements
|
|
the Traversable interface, these instructions raise a fatal error.
|
|
|
|
If $1 is an object that implements the Iterator interface, these instructions
|
|
throw a fatal error.
|
|
|
|
If $1 is an object that implements the IteratorAggregate interface, these
|
|
instructions throw a fatal error.
|
|
|
|
If $1 is an object that does not match any of the three cases above, these
|
|
instructions create a mutable default class iterator, rewind it to point to
|
|
the first accessible property, and store the it in the iterator variable %1.
|
|
Then these instructions check if the iterator is at the end, and if it is
|
|
these instructions free the iterator and transfer control to the location
|
|
specified by %2.
|
|
|
|
If $1 is not an array or an object, these instructions raise a warning and
|
|
transfer control to the location specified by %2.
|
|
|
|
If the iterator specified by %1 is a mutable array iterator, these
|
|
instructions store the current value in %3 as a var.
|
|
|
|
If the iterator specified by %1 is a mutable default class iterator, these
|
|
instructions store the current property in %3 as a var.
|
|
|
|
For the MIterInitK version, the following also happens:
|
|
|
|
If the iterator specified by %1 is an array iterator, this instruction
|
|
stores a copy of the current key in %4 as a cell.
|
|
|
|
If the iterator specified by %1 is a mutable default class iterator,
|
|
this instruction stores a copy of the name of the current property in %4 as a
|
|
cell.
|
|
|
|
IterNext <iterator id> <rel offset> <local id> [] -> []
|
|
IterNextK <iterator id> <rel offset> <local id> <local id> [] -> []
|
|
|
|
Iterator next. If the specified iterator is a non-mutable array iterator
|
|
or an extension class iterator, advance the iterator to point to the next
|
|
element. If the iterator is not at the end, these instructions transfer
|
|
control to the location specified by %2.
|
|
|
|
If the specified iterator is a user class iterator for object $x, this
|
|
instruction executes $x->next(). Then these instructions check if $x->valid()
|
|
returns true, and if it does these instructions transfer control to the
|
|
location specified by %2.
|
|
|
|
If the specified iterator is a non-mutable default class iterator, advance
|
|
the iterator to point to the next accessible property in the object.
|
|
If the iterator is not at the end, these instructions transfer control
|
|
to the location specified by %2.
|
|
|
|
If the specified iterator is at the end, free the iterator variable with
|
|
an implicit IterFree, then fall through to the next instruction.
|
|
|
|
If the specified iterator is not at the end, retrieve the key and value:
|
|
|
|
If the iterator specified by %1 is a non-mutable array iterator or an
|
|
extension class iterator, these instructions store a copy of the new current
|
|
value in %3 as a cell.
|
|
|
|
If the iterator specified by %1 is a user class iterator for object $x, these
|
|
instructions store the return value of $x->current() in %3 as a cell.
|
|
|
|
If the iterator specified by %1 is a non-mutable default class iterator, these
|
|
instructions store a copy of the new current property in %3 as a cell.
|
|
|
|
For the IterNextK version, the following also happens:
|
|
|
|
If the iterator specified by %1 is an array iterator or an extension class
|
|
iterator, this instruction stores a copy of the new current key in %4 as a
|
|
cell.
|
|
|
|
If the iterator specified by %1 is a user class iterator for object $x, this
|
|
instruction stores the return value of $x->key() in %4 as a cell.
|
|
|
|
If the iterator specified by %1 is a non-mutable default class iterator,
|
|
this instruction stores a copy of the name of the new current property in %4
|
|
as a cell.
|
|
|
|
MIterNext <iterator id> <rel offset> <local id> [] -> []
|
|
MIterNextK <iterator id> <rel offset> <local id> <local id> [] -> []
|
|
|
|
Iterator next. If the specified iterator is a mutable array iterator,
|
|
advance the iterator to point to the next element. If the iterator
|
|
is not at the end, these instructions transfer control to the
|
|
location specified by %2.
|
|
|
|
If the specified iterator is a mutable default class iterator, advance
|
|
the iterator to point to the next accessible property in the object.
|
|
If the iterator is not at the end, these instructions transfer control
|
|
to the location specified by %2.
|
|
|
|
If the specified iterator is at the end, free the iterator variable with
|
|
an implicit MIterFree, then fall through to the next instruction.
|
|
|
|
If the specified iterator is not at the end, retrieve the key and value:
|
|
|
|
If the iterator specified by %1 is a mutable array iterator, these
|
|
instructions store the new current value in %3 as a var.
|
|
|
|
If the iterator specified by %1 is a mutable default class iterator, these
|
|
instructions store the new current property in %3 as a var.
|
|
|
|
For the MIterNextK version, the following also happens:
|
|
|
|
If the iterator specified by %1 is an array iterator, this instruction
|
|
stores a copy of the new current key in %4 as a cell.
|
|
|
|
If the iterator specified by %1 is a mutable default class iterator,
|
|
this instruction stores a copy of the name of the new current property in %4
|
|
as a cell.
|
|
|
|
IterFree <iterator id> [] -> []
|
|
|
|
Iterator free. This instruction frees the specified iterator variable.
|
|
Typically an iterator gets freed by IterNext, so IterFree is only needed
|
|
for guarding against exceptions and implementing break and return control
|
|
flow statements inside iterator loops.
|
|
|
|
MIterFree <iterator id> [] -> []
|
|
|
|
Mutable iterator free. This instruction frees the specified iterator
|
|
variable. Typically an iterator gets freed by MIterNext*, so MIterFree is
|
|
only needed for guarding against exceptions and implementing break and
|
|
return control flow statements inside iterator loops.
|
|
|
|
|
|
12. Include, eval, and define instructions
|
|
------------------------------------------
|
|
|
|
Incl [C] -> [C]
|
|
|
|
Include. Includes the compilation unit containing the file (string)$1. The
|
|
instruction eagerly marks all functions and classes that are unconditionally
|
|
declared in the outermost scope as defined. Next this instruction calls the
|
|
pseudo-main function from the file (string)$1. The pseudo-main function
|
|
inherits the caller's variable environment. If the execution engine cannot
|
|
find a compilation unit containing the file (string)$1, this instruction
|
|
raises a warning.
|
|
|
|
InclOnce [C] -> [C]
|
|
|
|
Include once. Include the compilation unit containing the file (string)$1 if
|
|
it hasn't been included already. This instruction eagerly marks all functions
|
|
and classes that are unconditionally declared in the outermost scope as
|
|
defined, and then calls the pseudo-main function from (string)$1 if it hasn't
|
|
run already. The pseudo-main function inherits the caller's variable
|
|
environment. If the execution engine cannot find a compilation unit
|
|
containing the file (string)$1, this instruction raises a warning.
|
|
|
|
Req [C] -> [C]
|
|
|
|
Require. Includes the compilation unit containing the file (string)$1. The
|
|
instruction eagerly marks all functions and classes that are unconditionally
|
|
declared in the outermost scope as defined. Next this instruction calls the
|
|
pseudo-main function from the file (string)$1. The pseudo-main function
|
|
inherits the caller's variable environment. If the execution engine cannot
|
|
find a compilation unit containing the file (string)$1, this instruction
|
|
throws a fatal error.
|
|
|
|
ReqOnce [C] -> [C]
|
|
|
|
Require once. Include the compilation unit containing the file (string)$1 if
|
|
it hasn't been included already. This instruction eagerly marks all functions
|
|
and classes that are unconditionally declared in the outermost scope as
|
|
defined, and then calls the pseudo-main function from (string)$1 if it hasn't
|
|
run already. The pseudo-main function inherits the caller's variable
|
|
environment. If the execution engine cannot find a compilation unit
|
|
containing the file (string)$1, this instruction throws a fatal error.
|
|
|
|
ReqDoc [C] -> [C]
|
|
|
|
As ReqOnce except the string is always taken to be relative to the document
|
|
root (ie SourceRoot).
|
|
|
|
Eval [C] -> [C]
|
|
|
|
Eval. Executes the source code in (string)$1. This instruction eagerly marks
|
|
all functions and classes that are unconditionally declared in the outermost
|
|
scope as defined, and then calls the pseudo-main function from (string)$1.
|
|
The pseudo-main function from (string)$1 inherits the caller's variable
|
|
environment.
|
|
|
|
DefFunc <function id> [] -> []
|
|
|
|
Define function. Bind the function specified by %1. If the function specified
|
|
by %1 is already bound, this instruction does nothing. If another function is
|
|
already bound to the name associated with %1, this instruction throws a fatal
|
|
error.
|
|
|
|
DefCls <class id> [] -> []
|
|
|
|
Define class. Bind the class specified by %1. If the class specified by %1 is
|
|
already bound, this instruction does nothing. If another class is already
|
|
bound to the associated name, this instruction throws a fatal error.
|
|
|
|
DefCns <litstr id> [C] -> [C]
|
|
|
|
Define constant. If there is already a global constant named %1, raises a
|
|
notice and pushes false. If $1 is an array or an object, raises a notice, and
|
|
pushes false. Otherwise defines the constant named %1 to have the value $1,
|
|
and pushes true.
|
|
|
|
|
|
13. Miscellaneous instructions
|
|
------------------------------
|
|
|
|
This [] -> [C:Obj]
|
|
|
|
This. This instruction checks the current instance, and if it is null, this
|
|
instruction throws a fatal error. Next, this instruction pushes the current
|
|
instance onto the stack.
|
|
|
|
BareThis <notice> [] -> [C:Obj]
|
|
|
|
This. This instruction pushes the current instance onto the stack. If %1 is
|
|
not zero, and the current instance is null, emits a notice.
|
|
|
|
CheckThis [] -> []
|
|
|
|
Check existence of this. This instruction checks the current instance, and if
|
|
it is null, throws a fatal error.
|
|
|
|
InitThisLoc <local variable id> [] -> []
|
|
|
|
Initialize this local variable. This instruction checks the current instance,
|
|
and if it is not null this instruction stores it to the specified local
|
|
variable. If the current instance is null, this instruction does nothing.
|
|
|
|
StaticLoc <local variable id> <litstr id> [] -> [C:Bool]
|
|
|
|
Static variable. This instruction first checks if the static variable named
|
|
%2 has been marked as initialized. If the static variable has been marked as
|
|
initialized, this instruction binds the static variable to the local variable
|
|
%1 and pushes true. Otherwise, this instruction binds the static variable to
|
|
the local variable %1, marks the static variable as initialized, and pushes
|
|
false.
|
|
|
|
StaticLocInit <local variable id> <litstr id> [C] -> []
|
|
|
|
Static variable with initializer. This instruction first checks if the static
|
|
variable named %2 has been marked as initialized. If the static variable has
|
|
been marked as initialized, this instruction binds the static variable to the
|
|
local variable %1. Otherwise, this instruction binds the static variable to
|
|
the local variable, assigns $1 to the local variable, and marks the static
|
|
variable as initialized.
|
|
|
|
Catch [] -> [C:Obj]
|
|
|
|
Catch. Retrieves the current exception object and pushes it onto the stack.
|
|
This instruction may only be used at the beginning of a catch entry point.
|
|
|
|
ClassExists [C C] -> [C:Bool]
|
|
InterfaceExists [C C] -> [C:Bool]
|
|
TraitExists [C C] -> [C:Bool]
|
|
|
|
Check for class/interface/trait existence. If $1 cannot be cast to a
|
|
bool or $2 cannot be cast to a string, this instruction will throw a
|
|
fatal error. Otherwise, it will check for existence of the entity
|
|
named by $2, invoking the autoloader if needed and if $1 is
|
|
true. The result of the existence check will be pushed on the stack.
|
|
|
|
VerifyParamType <parameter id> [] -> []
|
|
|
|
Verify parameter type. Functions and methods can optionally specify the
|
|
types of arguments they will accept. These type constraints are memoized
|
|
into each function's FPI structure.
|
|
|
|
VerifyParamTypes checks the specified parameter against the enclosing
|
|
function's corresponding parameter constraints. In case of a mismatch, a
|
|
recoverable error is raised.
|
|
|
|
Self [] -> [A]
|
|
|
|
Creates a classref that refers to the class in which the current
|
|
function is defined. This instruction throws a fatal error if the
|
|
current method is defined outside of a class, otherwise it pushes a
|
|
classref on the stack.
|
|
|
|
Parent [] -> [A]
|
|
|
|
Creates a classref that refers to the parent of the class in which
|
|
the current method is defined. This instruction throws a fatal
|
|
error if the current method is defined outside of a class or if the
|
|
class in which the current method is defined has no parent,
|
|
otherwise it pushes a classref on the stack.
|
|
|
|
LateBoundCls [] -> [A]
|
|
|
|
Late-bound class. Creates a classref that refers to the current late-bound
|
|
class and pushes it onto the stack.
|
|
|
|
NativeImpl [] -> []
|
|
|
|
Native implementation. This instruction invokes the native implementation
|
|
associated with current function and returns the return value to the caller
|
|
of the current function.
|
|
|
|
IncStat <counter id> <value> [] -> []
|
|
|
|
Increment stat counter. If stats are enabled, this instruction adds
|
|
<value> to the counter specified by <counter id>. The meaning of the
|
|
<counter id> immediate is implementation defined
|
|
|
|
AKExists [C C] -> [C:Bool]
|
|
|
|
Checks if array (object) in $1 contains key (property) in $2 and
|
|
pushes the resulting boolean onto the stack. If $2 is null, uses
|
|
the empty string as key. Throws a fatal error if $1 is not an array
|
|
or object, and raises a warning if $2 is not a string, integer, or
|
|
null.
|
|
|
|
14. Continuation creation and execution
|
|
---------------------------------------
|
|
|
|
CreateCont <getargs> <function name> [] -> [C]
|
|
|
|
Creates a GenericContinuation object and pushes it on the stack. The
|
|
Continuation will capture all defined local variables in the current
|
|
function, and if the <getargs> immediate is nonzero it will also store the
|
|
result of func_get_args(). The Continuation will store a reference to the
|
|
function named by the string immediate to be used as its body.
|
|
|
|
ContEnter [] -> []
|
|
|
|
This instruction may only appear in non-static methods of the Continuation
|
|
class. It transfers control flow to the beginning of the continuation body
|
|
associated with the $this object of the Continuation object.
|
|
|
|
ContExit [] -> []
|
|
|
|
This instruction may only appear in continuation bodies. It transfers control
|
|
flow to the caller of the continuation body, which must be a non-static method
|
|
of the Continuation class.
|
|
|
|
UnpackCont [] -> [C:Int]
|
|
|
|
Unpack continuation. Unpacks variables from the Continuation object in local
|
|
0 into the containing environment. The current label of the continuation is
|
|
pushed on the stack.
|
|
|
|
PackCont <label id> [C] -> []
|
|
|
|
Pack continuation. Packs all defined local variables into the Continuation
|
|
object in local 0. The value on the top of the stack is stored in the
|
|
continuation as the result of the current iteration and the continuation's
|
|
label is set to <label id>.
|
|
|
|
ContRaised [] -> []
|
|
|
|
Check for continuation exception. If the continuation in local 0 has a
|
|
pending exception, this instruction will throw that exception. Otherwise, it
|
|
does nothing.
|
|
|
|
ContReceive [] -> [C]
|
|
|
|
Receive continuation value. If the continuation in local 0 has a pending
|
|
exception, this instruction will throw that exception. Otherwise, the value
|
|
sent to the continuation will be pushed on the stack.
|
|
|
|
ContDone [] -> []
|
|
|
|
Finish continuation. Marks the continuation in local 0 as finished. Further
|
|
attempts to iterate it will fail.
|
|
|
|
ContNext [] -> []
|
|
|
|
Prepare continuation for iteration. $this must be a Continuation
|
|
object. If the continuation is finished or already running, an exception
|
|
will be thrown.
|
|
|
|
ContSend [] -> []
|
|
|
|
Prepare continuation to receive a value. $this must be a Continuation
|
|
object. If the continuation is finished or already running, an exception will
|
|
be thrown. Otherwise, the value in local 0 is stored in the continuation.
|
|
|
|
ContRaise [] -> []
|
|
|
|
Prepare continuation to receive a thrown exception. $this must be a
|
|
Continuation object. If the continuation is finished or already running, an
|
|
exception will be thrown. Otherwise, the exception in local 0 is stored in
|
|
the continuation.
|
|
|
|
ContValid [] -> [C:Bool]
|
|
|
|
Check continuation validity. $this must be a Continuation object. Pushes true
|
|
onto the stack if the continuation can be iterated further, false otherwise.
|
|
|
|
ContCurrent [] -> [C]
|
|
|
|
Get continuation value. $this must be a Contination object. Pushes the most
|
|
recently yielded value from the continuation onto the stack.
|
|
|
|
ContStopped [] -> []
|
|
|
|
Mark continuation as stopped. $this must be a Continuation object.
|
|
|
|
ContHandle [C] -> []
|
|
|
|
Handle exception from continuation body. $this must be a Continuation
|
|
object. Marks the continuation as no longer running and finished and rethrows
|
|
the exception on the top of the stack.
|
|
|
|
|
|
Basic statement transformations
|
|
-------------------------------
|
|
|
|
To achieve HHBC's goal of making it straightforward for an interpreter or a
|
|
compiler to determine order of execution, control flow statements are
|
|
transformed to use the simpler constructs. Most control flow statements such as
|
|
"if", "while", and "for" are implemented in a straightforward manner using the
|
|
Jmp* instructions.
|
|
|
|
HHBC provides the Switch instruction for implementing very simple switch
|
|
statements; most real switch statements are implemented naively using the Eq
|
|
and JmpNZ instructions. Also, the functionality of both the echo statement and
|
|
the print statement is implemented with the Print instruction.
|
|
|
|
Foreach statements are implemented using iterator variables and the Iter* and
|
|
MIter* instructions. Each foreach loop must be protected by a fault funclet to
|
|
ensure that the iterator variable is freed when a foreach loop exits abnormally
|
|
through an exception.
|
|
|
|
Simple break statements and continue statements are implemented using the Jmp*,
|
|
IterFree, and MIterFree instructions. Dynamic break is implemented using an
|
|
unnamed local (to store the 'break count') and a chain of basic blocks, where
|
|
each block decrements the unnamed local variable and compares it with 0, and
|
|
then decides where to jump next.
|
|
|
|
|
|
Basic expression transformations
|
|
--------------------------------
|
|
|
|
To reduce the size of the instruction set, certain types of expressions are
|
|
transformed:
|
|
|
|
1) Unary plus and negation
|
|
Unary plus and negation "+(<expression>)" gets converted to
|
|
"(0 + (<expression>))", and "-(<expression>)" gets converted to
|
|
"(0 - (<expression>))".
|
|
|
|
2) Assignment-by operators (+=, -=, etc)
|
|
Assignment-by operators are converted to use the SetOp* instructions.
|
|
|
|
3) List assignment (list)
|
|
List assignments are converted to use an unnamed local variable and the SetM
|
|
and VGet* instructions. If the function contains any catch funclets, then
|
|
list assignment requires a fault funclet as well.
|
|
|
|
4) Logical and and logical or operators (and/&&, or/||)
|
|
If any of the operands side-effect, these operators are implemented using Jmp*
|
|
instructions instead of using the "and" and "or" instructions to implement
|
|
short-circuit semantics correctly. All Jmp* instructions used to implement
|
|
"and" and "or" operators will be forward jumps.
|
|
|
|
5) The new expression
|
|
The new expression is implemented by using the FPushCtor*, FPass*, and FCall
|
|
instructions.
|
|
|
|
6) The ternary operator (?:)
|
|
The functionality of the ternary operator is implemented using Jmp*
|
|
instructions. All Jmp* instructions used to implement the ternary operator will
|
|
be forward jumps.
|
|
|
|
7) Silence operator (@)
|
|
The silence operator is implemented by using various instructions (including
|
|
the Jmp* instructions), unnamed local variables, and a fault funclet. All Jmp*
|
|
instructions used to implement the silence operator will be forward jumps.
|
|
|
|
8) The $this expression
|
|
The $this expression has different effects depending on whether or not $this is
|
|
the direct base of a property expression (such as "$this->x") or a method call
|
|
expression (such as "$this->foo()"). When the $this expression is the direct
|
|
base of a property expression or a method call expression, the This instruction
|
|
is used.
|
|
|
|
A bare $this expression within an instance method is handled one of two ways:
|
|
general or BareThis-optimized (optional). The general solution accesses a
|
|
local variable named "this", which is initialized at the beginning of the
|
|
method using the InitThisLoc instruction. The BareThis optimization applies to
|
|
bare $this access as long as $this is not passed by reference and there are no
|
|
dynamic method variables. In such cases, the BareThis instruction can be used
|
|
to directly access $this, and the InitThisLoc instruction is not needed.
|
|
|
|
|
|
Warning and errors at parse time
|
|
--------------------------------
|
|
|
|
Certain syntactically correct source code may cause warnings or errors to be
|
|
raised when the source file is parsed. Examples of this include using "$this"
|
|
on the left hand side of the assignment, using "$this" with binding assignment,
|
|
using "$a[]" in an r-value context, and doing "unset($a[])". HHBC handles these
|
|
cases by generating Raise or Fatal instructions at the beginning of the body
|
|
for the pseudo-main function.
|
|
|
|
|
|
Not yet implemented
|
|
-------------------
|
|
|
|
HipHop bytecode v1 revision 16 does not implement the following:
|
|
1) Description of traits
|
|
2) Description of metadata for class statements, trait statements, and method
|
|
statements
|
|
3) Description and examples for the yield generator feature
|
|
4) Description of the late static binding feature
|
|
5) Description of the resource type
|
|
6) Definitions of operators (ex. +, -, !) and other helper functions (ex.
|
|
is_null, get_class, strlen)
|
|
7) Support for the PHP 5.3 namespace feature
|
|
|
|
|
|
Source code to HHBC examples
|
|
----------------------------
|
|
|
|
function f() { return $a = $b; }
|
|
|
|
CGetL 1
|
|
SetL 0
|
|
RetC
|
|
|
|
function f() { g($a, $b); }
|
|
|
|
FPushFuncD 2 "g"
|
|
FPassL 0 0
|
|
FPassL 1 1
|
|
FCall 2
|
|
PopR
|
|
Null
|
|
RetC
|
|
|
|
function f() { return $a + $b; }
|
|
|
|
CGetL 1
|
|
CGetL2 0
|
|
Add
|
|
RetC
|
|
|
|
function f() { echo "Hello world\n"; }
|
|
|
|
String "Hello world\n"
|
|
Print
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f($a) { return $a[0]++; }
|
|
|
|
Int 0
|
|
IncDecM PostInc <L:0 EC>
|
|
RetC
|
|
|
|
function f($a, $b) { $a[4] = $b; }
|
|
|
|
Int 4
|
|
CGetL 1
|
|
SetM <L:0 EC>
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f($a, $b, $i) { $a[$i] = $b; }
|
|
|
|
CGetL 1
|
|
SetM <L:0 EL:2>
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f($a, $b) { return $a[4] = $b; }
|
|
|
|
Int 4
|
|
CGetL 1
|
|
SetM <L:0 EC>
|
|
RetC
|
|
|
|
function f($a, $b) { return $a[4][5] = $b[6]; }
|
|
|
|
Int 4
|
|
Int 5
|
|
Int 6
|
|
CGetM <L:1 EC>
|
|
SetM <L:0 EC EC>
|
|
RetC
|
|
|
|
function f($a, $b, $i) { return $a[$i][5] = $b[6]; }
|
|
|
|
Int 5
|
|
Int 6
|
|
CGetM <L:1 EC>
|
|
SetM <L:0 EL:2 EC>
|
|
RetC
|
|
|
|
function f($a, $b) { $a->prop = $b; }
|
|
|
|
String "prop"
|
|
CGetL 1
|
|
SetM <L:0 PC>
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f() { return FOO; }
|
|
|
|
Cns "FOO"
|
|
RetC
|
|
|
|
function f() { return c::FOO; }
|
|
|
|
ClsCnsD "FOO" "c"
|
|
RetC
|
|
|
|
function f($cls) { return $cls::FOO; }
|
|
|
|
AGetL 0
|
|
ClsCns "FOO"
|
|
RetC
|
|
|
|
class c { public function f($a) { $this->prop = $a; return $this; } }
|
|
|
|
// The emitter emits the InitThisLoc instruction at the top of an instance
|
|
// method if and only if the function contains a use of "$this" that is not
|
|
// part of a property expression or a method call expression. The emitter never
|
|
// emits the InitThisLoc instruction for static methods.
|
|
InitThisLoc 1
|
|
// Evaluate "$this->prop = $a". The This instruction will throw a fatal error
|
|
// if the current instance is null.
|
|
This
|
|
String "prop"
|
|
CGetL 0
|
|
SetM <C PC>
|
|
PopC
|
|
// Return the local variable named "this"
|
|
CGetL 1
|
|
RetC
|
|
|
|
class c { static public function f($a) { $this->prop = $a; return $this; } }
|
|
|
|
// Evaluate "$this->prop = $a". The This instruction will throw a fatal error
|
|
// because this is a static method.
|
|
This
|
|
String "prop"
|
|
CGetL 0
|
|
SetM <C PC>
|
|
PopC
|
|
// Return the local variable named "this"
|
|
CGetL 1
|
|
RetC
|
|
|
|
function f($a, $b, $c, $d) {
|
|
static $foo = array(array());
|
|
return $foo[0][$c + $d] += $a + $b;
|
|
}
|
|
|
|
// Bind static variable named "foo" to local variable named "foo"
|
|
Array array(0=>array())
|
|
StaticLocInit 4 "foo"
|
|
// Evaluate $foo[0][$c + $d]
|
|
Int 0
|
|
CGetL 3
|
|
CGetL2 2
|
|
Add
|
|
// Evaluate ($a + $b)
|
|
CGetL 1
|
|
CGetL2 0
|
|
Add
|
|
// Execute $foo[0][$c + $d] += $a + $b
|
|
SetOpM OpAdd <L:4 EC EC>
|
|
RetC
|
|
|
|
function f($a) { list($x, $y, $z) = $a[7]; return $x + $y + $z; }
|
|
|
|
// Evaluate $a[7] and store it in an unnamed local
|
|
CGetM <L:0 EI:7>
|
|
SetL 4
|
|
PopC
|
|
// Read the unnamed local, get the value at index 2, and store it in $z
|
|
CGetM <L:4 EI:2>
|
|
SetL 3
|
|
PopC
|
|
// Read the unnamed local, get the value at index 1, and store it in $y
|
|
CGetM <L:4 EI:1>
|
|
SetL 2
|
|
PopC
|
|
// Read the unnamed local, get the value at index 0, and store it in $x
|
|
CGetM <L:4 EI:0>
|
|
SetL 1
|
|
PopC
|
|
// Unset the unnamed local
|
|
UnsetL 4
|
|
// Evaluate $x + $y + $z and return the result
|
|
CGetL 2
|
|
CGetL2 1
|
|
Add
|
|
CGetL 3
|
|
Add
|
|
RetC
|
|
|
|
function f(&$a, $b) {
|
|
$r = array();
|
|
$r[v()] = (list(list($a[w()], $a[x()]), list($a[y()], $a[z()])) = $b[100]);
|
|
return $r;
|
|
}
|
|
|
|
// Evaluate $r = array()
|
|
Array array()
|
|
SetL 2
|
|
PopC
|
|
// Do the first pass of evaluation for $r[v()]
|
|
FPushFuncD 0 "v"
|
|
FCall 0
|
|
UnboxR
|
|
// Do the first pass of evaluation for $a[w()]
|
|
FPushFuncD 0 "w"
|
|
FCall 0
|
|
UnboxR
|
|
// Do the first pass of evaluation for $a[x()]
|
|
FPushFuncD 0 "x"
|
|
FCall 0
|
|
UnboxR
|
|
// Do the first pass of evaluation for $a[y()]
|
|
FPushFuncD 0 "y"
|
|
FCall 0
|
|
UnboxR
|
|
// Do the first pass of evaluation for $a[z()]
|
|
FPushFuncD 0 "z"
|
|
FCall 0
|
|
UnboxR
|
|
// Evaluate $b[100] and store it in an unnamed local t
|
|
CGetM <L:1 EI:100>
|
|
SetL 3
|
|
PopC
|
|
// Read t[1][1] and store it in $a[z()]
|
|
CGetM <L:3 EI:1 EI:1>
|
|
SetM <L:0 EC>
|
|
PopC
|
|
// Read t[1][0] and store it in $a[y()]
|
|
CGetM <L:3 EI:1 EI:0>
|
|
SetM <L:0 EC>
|
|
PopC
|
|
// Read t[0][1] and store it in $a[x()]
|
|
CGetM <L:3 EI:0 EI:1>
|
|
SetM <L:0 EC>
|
|
PopC
|
|
// Read t[0][0] and store it in $a[w()]
|
|
CGetM <L:3 EI:0 EI:0>
|
|
SetM <L:0 EC>
|
|
PopC
|
|
// Read t and leave the value on the stack
|
|
CGetL 3
|
|
// Unset the unnamed local t
|
|
UnsetL 3
|
|
// Assign the output of the outer list assignment expression into $r[v()]
|
|
SetM <L:2 EC>
|
|
PopC
|
|
// Return $r
|
|
CGetL 2
|
|
RetC
|
|
|
|
function f() {
|
|
$arr1 = array(0 => 1, 2 => 5, 7 => 'foo', 'bar' => 888);
|
|
$arr2 = array(0 => 6, 2 => 8, 5 => 'yar', 'baz' => 333);
|
|
$arr3 = array_merge((list($a, , $b) = $arr1), (list($c, , $d) = $arr2));
|
|
var_dump($a, $b, $c, $d, $arr3);
|
|
}
|
|
|
|
// Evaluate $arr1 = array(0 => 1, 2 => 5, 7 => 'foo', 'bar' => 888)
|
|
Array array(0=>1,2=>5,7=>"foo","bar"=>888)
|
|
SetL 0
|
|
PopC
|
|
// Evaluate $arr2 = array(0 => 6, 2 => 8, 5 => 'yar', 'baz' => 333)
|
|
Array array(0=>6,2=>8,5=>"yar","baz"=>333)
|
|
SetL 1
|
|
PopC
|
|
// Prepare to call array_merge
|
|
FPushFuncD 2 "array_merge"
|
|
// Evaluate (list($a, , $b) = $arr1)
|
|
CGetM <L:0 EI:2>
|
|
SetL 4
|
|
PopC
|
|
CGetM <L:0 EI:0>
|
|
SetL 3
|
|
PopC
|
|
// Pass the result as the first parameter
|
|
FPassL 0 0
|
|
// Evaluate (list($c, , $d) = $arr2)
|
|
CGetM <L:1 EI:2>
|
|
SetL 6
|
|
PopC
|
|
CGetM <L:1 EI:0>
|
|
SetL 5
|
|
PopC
|
|
// Pass the result as the second parameter
|
|
FPassL 1 1
|
|
// Call array_merge
|
|
FCall 2
|
|
// Store the result in $arr3
|
|
UnboxR
|
|
SetL 2
|
|
PopC
|
|
// Evaluate var_dump($a, $b, $c, $d, $arr3)
|
|
FPushFuncD 5 "var_dump"
|
|
FPassL 0 3
|
|
FPassL 1 4
|
|
FPassL 2 5
|
|
FPassL 3 6
|
|
FPassL 4 2
|
|
FCall 5
|
|
PopR
|
|
// Return null
|
|
Null
|
|
RetC
|
|
|
|
function f() { global $foo; return ++$foo; }
|
|
|
|
// Bind global variable named "foo" to local variable named "foo"
|
|
String "foo"
|
|
VGetG
|
|
BindL 0
|
|
PopV
|
|
// Evaluate "++$foo" and return the result
|
|
IncDecL 0 PreInc
|
|
RetC
|
|
|
|
function f($name) { global $$name; return $$name++; }
|
|
|
|
// Bind global variable named $name to a local variable named $name
|
|
CGetL 0
|
|
Dup
|
|
VGetG
|
|
BindN
|
|
PopV
|
|
// Evaluate "$$name++" and return the result
|
|
CGetL 0
|
|
IncDecN PostInc
|
|
RetC
|
|
|
|
function f() { $GLOBALS['blah'] = array(2, "foo" => "bar"); }
|
|
|
|
// If a global variable is superglobal, don't bind it to a local; instead we
|
|
// access it directly
|
|
String "blah"
|
|
Array array(0=>2,"foo"=>"bar")
|
|
SetG
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function &f(&$x) { return $x; }
|
|
|
|
VGetL 0
|
|
RetV
|
|
|
|
function f($x) { return $x[0]->h()->prop[1]; }
|
|
|
|
Int 0
|
|
CGetM <L:0 EC>
|
|
FPushObjMethodD 0 "h"
|
|
FCall 0
|
|
String "prop"
|
|
Int 1
|
|
CGetM <R PC EC>
|
|
RetC
|
|
|
|
function f($x, $y) { return $y($x[0]->h()->prop[1]); }
|
|
|
|
CGetL 1
|
|
FPushFunc 1
|
|
Int 0
|
|
CGetM <L:0 EC>
|
|
FPushObjMethodD 0 "h"
|
|
FCall 0
|
|
String "prop"
|
|
Int 1
|
|
FPassM 0 <R PC EC>
|
|
FCall 1
|
|
UnboxR
|
|
RetC
|
|
|
|
function f() { return new c; }
|
|
|
|
FPushCtorD 0 "c"
|
|
FCall 0
|
|
PopR
|
|
RetC
|
|
|
|
function f() { return c::$d; }
|
|
|
|
String "d"
|
|
String "c"
|
|
AGetC
|
|
CGetS
|
|
RetC
|
|
|
|
function f() { return c::$d[0]; }
|
|
|
|
String "d"
|
|
Int 0
|
|
String "c"
|
|
AGetC
|
|
CGetM <SC EC>
|
|
RetC
|
|
|
|
function f($cls, $name) { $cls::$$name = g(); }
|
|
|
|
AGetL 0
|
|
FPushFuncD 0 "g"
|
|
FCall 0
|
|
UnboxR
|
|
CGetL3 1
|
|
SetS
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f($cls, $name, $index) { $cls::${$name}[$index] = g(); }
|
|
|
|
AGetL 0
|
|
FPushFuncD 0 "g"
|
|
FCall 0
|
|
UnboxR
|
|
SetM <SL:1 EL:2>
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f($cls, $name, $index) { $cls::$$name[$index] = g(); }
|
|
|
|
CGetM <L:1 EL:2>
|
|
AGetL 0
|
|
FPushFuncD 0 "g"
|
|
FCall 0
|
|
UnboxR
|
|
SetS
|
|
PopC
|
|
Null
|
|
RetC
|
|
|
|
function f() {
|
|
if (empty(c::$foo)) {
|
|
c::$foo = null;
|
|
c::$foo[0]->prop = 3;
|
|
}
|
|
h(c::$foo[0]->prop);
|
|
}
|
|
|
|
String "foo"
|
|
String "c"
|
|
AGetC
|
|
EmptyS
|
|
JmpZ 66
|
|
String "foo"
|
|
String "c"
|
|
AGetC
|
|
Null
|
|
SetS
|
|
PopC
|
|
String "foo"
|
|
Int 0
|
|
String "prop"
|
|
String "c"
|
|
AGetC
|
|
Int 3
|
|
SetM <SC EC PC>
|
|
PopC
|
|
FPushFuncD 1 "h"
|
|
String "foo"
|
|
Int 0
|
|
String "prop"
|
|
String "c"
|
|
AGetC
|
|
FPassM 0 <SC EC PC>
|
|
FCall 1
|
|
PopR
|
|
Null
|
|
RetC
|
|
|
|
function f($cls, $spropName) {
|
|
if (!isset($cls::$$spropName)){
|
|
$cls::$$spropName = g();
|
|
}
|
|
h($cls::$$spropName);
|
|
}
|
|
|
|
AGetL 0
|
|
CGetL2 1
|
|
IssetS
|
|
JmpNZ 20
|
|
AGetL 0
|
|
FPushFuncD 0 "g"
|
|
FCall 0
|
|
UnboxR
|
|
CGetL3 1
|
|
SetS
|
|
PopC
|
|
FPushFuncD 1 "h"
|
|
AGetL 0
|
|
CGetL2 1
|
|
FPassS 0
|
|
FCall 1
|
|
PopR
|
|
Null
|
|
RetC
|
|
|
|
class c { public static function f() { return c::g(); } }
|
|
|
|
FPushClsMethodD 0 "g" "c"
|
|
FCall 0
|
|
UnboxR
|
|
RetC
|
|
|
|
class c { public static function f() { return static::g(); } }
|
|
|
|
String "g"
|
|
LateBoundCls
|
|
FPushClsMethod 0
|
|
FCall 0
|
|
UnboxR
|
|
RetC
|
|
|
|
function f() { self::x(); }
|
|
|
|
String "x"
|
|
Self
|
|
FPushClsMethodF 0
|
|
FCall 0
|
|
PopR
|
|
Null
|
|
RetC
|
|
|
|
class c { public static function f() { return self::$x; } }
|
|
|
|
String "x"
|
|
Self
|
|
CGetS
|
|
RetC
|
|
|
|
function f() { return new parent(); }
|
|
|
|
Parent
|
|
FPushCtor 0
|
|
FCall 0
|
|
PopR
|
|
RetC
|
|
|
|
class B extends A { public static function f() { return parent::x; } }
|
|
|
|
ClsCnsD "x" "A"
|
|
RetC
|
|
|