|
|
|
|
|
|
| Intentional Programming | ACI Concept | AAL definition | AAL syntax | AAL parser | Object Representation | OpCode | Reference Links |
Intermediate OpCode for AAL.
Using an OpCode for execution and maybe transpile to Java ByteCode or
.NET CIL.
For both languages an assembler language should be used.
OpCodes are nodes below the Code nodes.
Code::executes() delegates the call to
OpCodes.
Maybe translation can be done on the fly.
On each CodeNode the Byte Code of the construct will
be attached.
The ByteCode contains an ObjectArray. The Elements can be
either a OpCode statement (like 'push 1') or a
Link to another ByteCode.
Use ScriptVar as basic type.
All OpCodes operates on ScriptVar, so they don't
make a difference to i2, i4, r4, Object etc.
- Use Instance variable name in Byte-Code?
- Use .data section (StackFrame)?
int i = 1 + 2;
// will be:
.data i int
push 1
push 2
add
pushlval .data[0]
assign
i = 1;
// maybe also
push 1
assign i
|
// Minimal sample
void foo() { }
// Callee
push Nil
ret
foo();
// Caller
push "foo" // class name
push "operator_popc"
push 0 // argcount | flags
invoke_static
pop
|
void foo(int i = 0, String s = Nil);
foo(s: "AAL", i: 2);
push 2
push "i"
push "AAL"
push "s"
push 2 // number of named params
push 0 // number of unnamed params
push "foo"
callnp
|
Write access
class AClass
{
public: int foo;
};
// AClass cls
acls.foo = 2;
push acls
push 2
poke 'foo'
// or
push 2
push acdk
push 'foo'
getmemberlval
assign
// or
push acls
push 'foo'
push 2
invoke
|
// or
implement operator=() on the object reference.
which may be most general approach.
acdk.lang.System.out.println("asdf");
push "acdk/lang/System"
push "out"
peek_static // top on stack is out
push "println"
push "asdf"
push 1
invoke
|
To define Types, special Opcodes should
be used.
- Don't use OpCodes for Class Definition but use the
Code nodes and the Environment to define classes.
Pro: Easy to implement.
- Use structured declarative statement (.defclass) in the opcode.
Con: Has to established a Parser for OpCode.
- Use basic stack based op codes to create type.
Con: Hard to understand, not portable to other ByteCode languages.
- Use serialized ClazzInfo to define Classes.
The classes can be loaded via a ClassLoader
Pro: easy to implement.
Pro: Fast execution
class AClass
{
public void foo(int i);
};
// Case 1 has no OpCode
// Case 2
.defclass AClass extends BClass
.defmethod void foo (int i)
.endclass
// Case 3
push "AClass"
clsdef // class AClass is on top
push "BClass"
clsextends
push "void" // return type
push "foo" // method name
push "int"
push "i"
push 1 // argument count, method flags
defmethod
pop // removes the class defintion
// Case 4
|
The a compiled unit should be stored
into a binary file:
Maybe a string dictionary similar to the Java Class format.
For debugging purpose may also include
the original AST with all Codes.
Symboltable holds defined ClazzInfo ressources
and tries to resolves external type definitions.
After postParse() the SymbolTable is build.
The SymbolTable is a mapping from String (VarName) to Type.
class AalClassFile
{
short magic; // 0xaacf
int version_mayor;
int version_minor;
SymbolTableArray symbols;
OpCodeArray symbols;
CodeArray codes; // debug information
};
|
For runtime the names should be resolved to simple index into
an ActivationFrame. The ActivationFrame holds all local
variables of the current block.
The ActivationFrame creates two OpCodes blocks:
Push all local variables on the stack before emitting the
childCodes of the ActivateFrame.
After emmitting the the childCodes, save last value on the stack
if needed, pop all local variables.
// AFS = ActivationFrameStack
int foo(int i) // AFS[0]
{
int j; // AFS[1]
{
// // AFS[2]
return i;
}
}
AFS[1]: pop ScriptVar(int(0));
ret AFS[0][0]. // save AFS[0][0] value and unwind AFS until function will be leaved.
|
To enable returning from everywhere in the ActivationFrame Stack
(and exceptions too) the ret operation has to call a method unwind
to pop all local variables.
The ret value must not saved on the local variable stack, otherwise it
will be also discarged at the unwind operation.
Positive index values address the values from the bottom (0)
Negativ values (beginning with -1, which is the last pushed value)
addresses the values from top of the stack.
Variable declarations in a ActivationFrame
void foo()
{
int i = 42;
i = i + 1;
if (i == 42)
return;
int j = i;
return j;
}
|
The example defines a variable i, uses this variable and
than defines another variable j.
In C both variables i and j will be created at beginning of the
function block. In C++ only the first variable i will be created.
If using the C++ creation manner of local variables, the
AF must be managed dynamically:
lvar int
push 42
store 0
...
lval int
load 0
store 1
load 1
ret
|
Parameter passing
Functions ActivationFrame are specialized ActivationFrame.
Question:
Caller should push arguments on stack. But should also the
caller pop the arguments to avoid stack under/overflow?
The advantage of the case, that the callee pops the arguments
is, that less OpCodes are generated (a function normally calls
many functions).
foo(42, 41);
push 41
push 42
push 2 // 2 arguments, 0 unnamed, standard call
call foo
int foo(int i, int j)
{
//Stack: i, j, Number of arguments (2)
return i + j;
pop AFS[0][2]
pop AFS[0][1]
add
ret AFS[0][0]
} //
|
invokation flags = number of arguments (byte) |
number of named arguments (byte) << 8
unused << 16
flags << 24
flags:
enum InvokationFlags
{
StandardFrame = 0x00,
};
|
Parameter attributes
in attributes
In case a parameter will be passed with the 'in' attribute
it will be possible just to reuse parents AF slot.
An flag of the ScriptVar has to ensure, that the value must not
be changed.
out/inout attributes
In case a parameter will be passed with the 'out' or 'inout' attribute
it will be possible just to reuse parents AF slot.
Otherwise the ScriptVar must be a reference type.
Exceptions has to be hold by the ActivationFrame and
delegate to parent ActivationFrame.
Java handles this with ExceptionsTables, which markes
an OpCodeBlock internally and the target PC in case
of an active Exception.
// function and global ActivationFrame
class ActivationFrame
: implements OpCode
{
ScriptVar _returnValue;
RThrowable _activeException;
ActivationFrame(ActivationFrame* parent = 0);
void execute();
void unwind();
}
// a block
class ActivationFrameScope
{
ActivationFrame& _af;
int _top;
void unwind();
}
|
createaf
clvar int
void foo(int i)
{
// createafscope
int j = 1;
// clval int
// push 1
// store AF[1]
{ // createafscope
int j = i;
// clval int
// load AF[0]
// store AF[2]
} // destroyafscope
j = 2;
// push 2
// store AF[1]
int k = 1;
// clval int
// push 1
// store AF[2]
} // destroyafscope
// destroyaf
|
int i;
while (i < 3)
i = i + 1;
clval int // asume on AF[1]
loopentry:
load 1
push 3
lt
brnull loopexit
load 1
push 1
add
store 1
br loopentry
loopexit:
|
Same now with block:
while (i < 3)
{
i = i + 1;
}
clval int // asume on AF[1]
loopentry:
oopentry:
load 1
push 3
lt
brnull loopexit
createafscope
load 1
push 1
add
store 1
destroyafscope
br loopentry
loopexit:
|
Store the DClazzInfo as binary, compatible with
ClazzInfo C-structures to make loading an ClazzInfo
a simple task.
Maybe use a type of memory mapped file.
Link from a ClazzMethodInfo to the starting OPStatement
of these function.
Binary, as stream.
0: clvr 0
1: push ""Hello ""
2: push "acdk/lang/StringBuffer"
3: push 1
4: new
5: store 0 lvar 0 will only used in next line (pop stack and store at 0)
6: load 0 (load 0 and push on stack
7: clvr 1
8: store 1
9: push ""ACDK""
10: push "append"
11: load 1
12: push 1
13: invoke
14: pop
Can be:
0: <deleted>
1: push ""Hello ""
2: push "acdk/lang/StringBuffer"
3: push 1
4: new
5: <deleted>
6: <deleted>
7: clvr 0
8: store 0
9: push ""ACDK""
10: push "append"
11: load 0
12: push 1
13: invoke
14: pop
|
AAL | JVM | CIL | Explenation |
nop | ? | nop | No operation |
ldc | loadx | ldc | Load constant on stack |
dup | ? | dup | Duplicates the top most element |
AAL | JVM | CIL | Explenation |
add | ? | add | Add both values on top |
AAL | JVM | CIL | Explenation |
br | ? | br | Branch (goto) to label |
brtrue | ? | brtrue | Branch (goto) to label if true |
brfalse | ? | brtrue | Branch (goto) to label if false |
AAL | JVM | CIL | Explenation |
inv | ? | callvirt | Call the virtual method |
invs | ? | ? | Invoke Static |
inve | ? | ? | Invoke with extended Invokation Frame |
invse | ? | ? | Invoke static with extended Invokation Frame |
new | ? | ? | create new Object. Also pass allocator |
newe | ? | ? | create new Object with extended Invokation Frame |
|
|