Assigned register collision when inline assembly compiled with clang - clang

Consider the following sample program (targeting Linux/x86-64):
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned arg1 = strtoul(argv[1], NULL, 0);
unsigned arg2 = strtoul(argv[2], NULL, 0);
asm(
"mov %[arg1], %%ecx\n\t"
"add %[arg2], %[arg1]\n\t"
"add %[arg2], %%ecx\n\t"
"xchg %%ecx, %[arg2]"
: [arg1] "+&abdSD" (arg1), [arg2] "+&abdSD" (arg2)
:
: "cc", "ecx");
printf("%u %u\n", arg1, arg2);
}
(xchg is used just for easy grepping of compiled instructions in listing.)
With GCC, it works as expected - different registers are assigned to arg1 and arg2, for example:
11bf: e8 cc fe ff ff callq 1090 <strtoul#plt>
11c4: 89 da mov %ebx,%edx
11c6: 89 d1 mov %edx,%ecx
11c8: 01 c2 add %eax,%edx
11ca: 01 c1 add %eax,%ecx
11cc: 91 xchg %eax,%ecx
(so, arg1 in edx, arg2 in eax)
But, compiling with Clang (confirmed on 6.0 and 10.0) results in assigning the same register for arg1 and arg2:
401174: e8 d7 fe ff ff callq 401050 <strtoul#plt>
401179: 44 89 f0 mov %r14d,%eax ; <--
40117c: 89 c1 mov %eax,%ecx
40117e: 01 c0 add %eax,%eax ; <-- so, arg1 and arg2 both in eax
401180: 01 c1 add %eax,%ecx
401182: 91 xchg %eax,%ecx
The issue remains with multiple variations as e.g.: + instead of +& in constraint strings; numeric forms like %0 to address operands; replacing xchg with another rare instruction; and so on.
I have been expecting, from the basic principles, that compilerʼs logic to assign output locations will always assign different locations to different output operands, whatever constraints are defined for them; and the same works among the input operands set. (Modifiers like '+', '&' add more rules to placement logic but canʼt erode the main principles.)
Is there a some trivial aspect Iʼve overlooked?
UPD: reported to LLVM.

Related

What lives above the last accessible address in the stack?

I've asked people before about why the stack doesn't start at 0x7fff...c before, and was told that typically 0x800... onwards is for the kernel, and the cli args and environment variables live at the top of the user's stack which is why it starts below 0x7fff...c. But I recently tried to examine all the strings with the following program
#include <stdio.h>
#include <string.h>
int main(int argc, const char **argv) {
const char *ptr = argv[0];
while (1) {
printf("%p: %s\n", ptr, ptr);
size_t len = strlen(ptr);
ptr = (void *)ptr + len + 1;
}
}
However, after displaying all my environment variables, I see the following (I compiled the program to an executable called ./t):
0x7ffc19f84fa0: <final env variable string>
0x7ffc19f84fee: _=./t
0x7ffc19f84ff4: ./t
0x7ffc19f84ff8:
0x7ffc19f84ff9:
0x7ffc19f84ffa:
0x7ffc19f84ffb:
0x7ffc19f84ffc:
0x7ffc19f84ffd:
0x7ffc19f84ffe:
0x7ffc19f84fff:
So it appears there's one extra empty byte after the null terminator for the ./t string at bytes 0x7ffc19f84ff4..0x7ffc19f84ff7, and after that I segfault so I guess that's the base of the stack. What actually lives in the remaining "empty" space before kernel memory starts?
Edit: I also tried the following:
global _start
extern print_hex, fgets, puts, print, exit
section .text
_start:
pop rdi
mov rcx, 0
_start_loop:
mov rdi, rsp
call print_hex
pop rdi
call puts
jmp _start_loop
mov rdi, 0
call exit
where print_hex is a routine I wrote elsewhere. It seems this is all I can get
0x00007ffcd272de28
./bin/main
0x00007ffcd272de30
abc
0x00007ffcd272de38
def
0x00007ffcd272de40
ghi
0x00007ffcd272de48
make: *** [Makefile:47: run] Segmentation fault
so it seems that even in _start we don't begin at 0x7fff...

Write python code in delphi AES MODE ECB

I translated two functions in delphi but i don't know if they are right, I need to write the def do_aes_encrypt(key2_t_xor) to know if I am right.
This is what I wrote in delphi:
function key_transform (old_key:string): string;
var
x :integer;
begin
result:='';
for x := 32 downto 0 do
result:= result + chr(ord(old_key[x-1])-( x mod $0C)) ;
end;
function key_xoring ( key2_t :string ; kilo_challenge :string) : string ;
var
i :integer;
begin
result := '';
i:=0 ;
while i <= 28 do begin
result := result + chr(ord(key2_t[i+1]) xor ord(kilo_challenge[3]));
result := result + chr(ord(key2_t[i+2]) xor ord(kilo_challenge[2])) ;
result := result+ chr(ord(key2_t[i+3]) xor ord (kilo_challenge[1])) ;
i := i + 4 ;
end;
end;
This is the original python code:
def key_transform(old_key):
new_key = ''
for x in range(32,0,-1):
new_key += chr(ord(old_key[x-1]) - (x % 0x0C))
return new_key
def key_xoring(key2_t, kilo_challenge):
key2_t_xor = ''
i = 0
while i <= 28:
key2_t_xor += chr(ord(key2_t[i]) ^ ord(kilo_challenge[3]))
key2_t_xor += chr(ord(key2_t[i+1]) ^ ord(kilo_challenge[2]))
key2_t_xor += chr(ord(key2_t[i+2]) ^ ord(kilo_challenge[1]))
key2_t_xor += chr(ord(key2_t[i+3]) ^ ord(kilo_challenge[0]))
i = i + 4
return key2_t_xor
def do_aes_encrypt(key2_t_xor):
plaintext = b''
for k in range(0,16):
plaintext += chr(k)
obj = AES.new(key2_t_xor, AES.MODE_ECB)
return obj.encrypt(plaintext)
/////////////////////////////////////////////////////////////////////////////
{
kilo_challenge = kilo_header[8:12]
chalstring = ":".join("{:02x}".format(ord(k)) for k in kilo_challenge)
key2 = 'qndiakxxuiemdklseqid~a~niq,zjuxl' # if this doesnt work try 'lgowvqnltpvtgogwswqn~n~mtjjjqxro'
kilo_response = do_aes_encrypt(key_xoring(key_transform(key2),kilo_challenge))}
this code is for calculate data line 16 byte to be send as an addition to 32 byte
before
look photo the marked line in blue is what i need to calculate by the 4 byte hex befor marked in porple
and this is the key
key2 = 'qndiakxxuiemdklseqid~a~niq,zjuxl'
in delphi
because python code is working perfect
look to the photo
how it work
this is for lg phones upgrading firmware when i receive the KILOCENT ANSOWER AS THE photo show`s
this below change every time phone connected
||
V
4b 49 4c 4f 43 45 4e 54 ([ac e5 b1 06]) 00 00 00 00 KILOCENT¬å±.....
00 00 00 00 00 00 00 00 30 d4 00 00 b4 b6 b3 b0 ........0Ô..´¶³°
i have to send KILOMETER REQUEST to phone the first and second line is fixed no change but the third i have to change it by the AES ECB MODE encryption look
4b 49 4c 4f 4d 45 54 52 00 00 00 00 02 00 00 00 KILOMETR........
00 00 00 00 10 00 00 00 85 b6 00 00 b4 b6 b3 b0 ........…¶..´¶³°
fc 21 d8 e5 5b aa fd 58 1e 33 58 fd e9 0b 65 38 ü!Øå[ªýX.3Xýé.e8 <==this
and this is old key
key2 = 'qndiakxxuiemdklseqid~a~niq,zjuxl'

Constant TAO CORBA IOR

How to configure TAO corba server so that IOR string of this server generated from object_to_string is constant?
Each time the IOR string generated from object_to_string changes once server restarts. This is inconvenient since client has to update its cached server IOR string via reloading IOR file or namingservice accessing. As a result, it would be useful if server can generate a constant IOR string, no matter how many times it restarts.
My corba server is based on ACE+TAO and i do remember TAO supports constant IOR string: the IOR string each time it generates are same, and the solution is add some configurations for server. But i could not remember these configurations now.
=============================================
UPDATE:
In ACE_wrappers/TAO/tests/POA/Persistent_ID/server.cpp, i added a new function named testUniqe() which is similiar to creatPOA method. And the update file content is:
void testUniqu(CORBA::ORB_ptr orb_, PortableServer::POA_ptr poa_){
CORBA::PolicyList policies (2);
policies.length (2);
//IOR is the same even it is SYSTEM_ID
policies[0] = poa_->create_id_assignment_policy (PortableServer::USER_ID);
policies[1] = poa_->create_lifespan_policy (PortableServer::PERSISTENT);
PortableServer::POAManager_var poa_manager = poa_->the_POAManager ();
PortableServer::POA_ptr child_poa_ = poa_->create_POA ("childPOA", poa_manager.in (), policies);
// Destroy the policies
for (CORBA::ULong i = 0; i < policies.length (); ++i) {
policies[i]->destroy ();
}
test_i *servant = new test_i (orb_, child_poa_);
PortableServer::ObjectId_var oid = PortableServer::string_to_ObjectId("xushijie");
child_poa_->activate_object_with_id (oid, servant);
PortableServer::ObjectId_var id = poa_->activate_object (servant);
CORBA::Object_var object = poa_->id_to_reference (id.in ());
test_var test = test::_narrow (object.in ());
CORBA::String_var ior = orb_->object_to_string(test.in());
std::cout<<ior.in()<<std::endl;
poa_->the_POAManager()->activate();
orb_->run();
}
int
ACE_TMAIN (int argc, ACE_TCHAR *argv[])
{
try
{
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv);
int result = parse_args (argc, argv);
CORBA::Object_var obj =
orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var root_poa =
PortableServer::POA::_narrow (obj.in ());
PortableServer::POAManager_var poa_manager =
root_poa->the_POAManager ();
testUniqu(orb.in(), root_poa.in());
orb->destroy ();
}
catch (const CORBA::Exception& ex)
{
ex._tao_print_exception ("Exception caught");
return -1;
}
return 0;
}
The problem is that the output server IORs are still different once restart. I also compared this code to the one in Page 412(Advance Corba Programming), but still fail..
///////////////////////////////////
UPDATE:
With "server -ORBListenEndpoints iiop://:1234 > /tmp/ior1", the generated two IORs are:
IOR:010000000d00000049444c3a746573743a312e300000000001000000000000007400000001010200150000007368696a69652d5468696e6b5061642d543431300000d2041b00000014010f0052535453f60054c6f80c000000000001000000010000000002000000000000000800000001000000004f41540100000018000000010000000100010001000000010001050901010000000000
IOR:010000000d00000049444c3a746573743a312e300000000001000000000000007400000001010200150000007368696a69652d5468696e6b5061642d543431300000d2041b00000014010f0052535468f60054da280a000000000001000000010000000002000000000000000800000001000000004f41540100000018000000010000000100010001000000010001050901010000000000
The result for tao_catior for ior1 and ior2:
ior1:
The Byte Order: Little Endian
The Type Id: "IDL:test:1.0"
Number of Profiles in IOR: 1
Profile number: 1
IIOP Version: 1.2
Host Name: **
Port Number: 1234
Object Key len: 27
Object Key as hex:
14 01 0f 00 52 53 54 53 f6 00 54 c6 f8 0c 00 00
00 00 00 01 00 00 00 01 00 00 00
The Object Key as string:
....RSTS..T................
The component <1> ID is 00 (TAG_ORB_TYPE)
ORB Type: 0x54414f00 (TAO)
The component <2> ID is 11 (TAG_CODE_SETS)
Component length: 24
Component byte order: Little Endian
Native CodeSet for char: Hex - 10001 Description - ISO8859_1
Number of CCS for char 1
Conversion Codesets for char are:
1) Hex - 5010001 Description - UTF-8
Native CodeSet for wchar: Hex - 10109 Description - UTF-16
Number of CCS for wchar 0
ecoding an IOR:
//ior2
The Byte Order: Little Endian
The Type Id: "IDL:test:1.0"
Number of Profiles in IOR: 1
Profile number: 1
IIOP Version: 1.2
Host Name: **
Port Number: 1234
Object Key len: 27
Object Key as hex:
14 01 0f 00 52 53 54 68 f6 00 54 da 28 0a 00 00
00 00 00 01 00 00 00 01 00 00 00
The Object Key as string:
....RSTh..T.(..............
The component <1> ID is 00 (TAG_ORB_TYPE)
ORB Type: 0x54414f00 (TAO)
The component <2> ID is 11 (TAG_CODE_SETS)
Component length: 24
Component byte order: Little Endian
Native CodeSet for char: Hex - 10001 Description - ISO8859_1
Number of CCS for char 1
Conversion Codesets for char are:
1) Hex - 5010001 Description - UTF-8
Native CodeSet for wchar: Hex - 10109 Description - UTF-16
Number of CCS for wchar 0
The diff result is:
< 14 01 0f 00 52 53 54 53 f6 00 54 c6 f8 0c 00 00
---
> 14 01 0f 00 52 53 54 68 f6 00 54 da 28 0a 00 00
19c19
< ....RSTS..T................
---
> ....RSTh..T.(..............
Similar diff result is:
< 14 01 0f 00 52 53 54 62 fd 00 54 2c 9a 0e 00 00
---
> 14 01 0f 00 52 53 54 02 fd 00 54 f9 a9 09 00 00
19c19
< ....RSTb..T,...............
---
> ....RST...T................
The difference is in ObjectKey.
============================================
update:
Instead of using above code, i find a better solution with helper TAO_ORB_Manager which is used NamingService and TAO/examples/Simple. TAO_ORB_Manager encapsulates API and generate persistent IORs, as example code in Simple.cpp:
if (this->orb_manager_.init_child_poa (argc, argv, "child_poa") == -1){
CORBA::String_var str =
this->orb_manager_.activate_under_child_poa (servant_name,
this->servant_.in ());
}
This is some description for TAO_ORB_Manager:
class TAO_UTILS_Export TAO_ORB_Manager
{
/**
* Creates a child poa under the root poa with PERSISTENT and
* USER_ID policies. Call this if you want a #a child_poa with the
* above policies, otherwise call init.
*
* #retval -1 Failure
* #retval 0 Success
*/
int init_child_poa (int &argc,
ACE_TCHAR *argv[],
const char *poa_name,
const char *orb_name = 0);
/**
* Precondition: init_child_poa has been called. Activate <servant>
* using the POA <activate_object_with_id> created from the string
* <object_name>. Users should call this to activate objects under
* the child_poa.
*
* #param object_name String name which will be used to create
* an Object ID for the servant.
* #param servant The servant to activate under the child POA.
*
* #return 0 on failure, a string representation of the object ID if
* successful. Caller of this method is responsible for
* memory deallocation of the string.
*/
char *activate_under_child_poa (const char *object_name,
PortableServer::Servant servant);
...................
}
After build, I can get what i want with -ORBListenEndpoints iiop://localhost:2809 option. Thanks #Johnny Willemsen help
You have to create the POA with a persistent lifespan policy, see ACE_wrappers/TAO/tests/POA/Persistent_ID as part of the TAO distribution.

Why does if Assigned(ListViewLayers1.Items) then Produce an Exception

{ Clear ListViewLayers }
try
if Assigned(ListViewLayers1.Items) then
ListViewLayers1.Clear;
except
on E: Exception do
begin
ShowMessage(E.ClassName + #10#13 + E.Message);
end;
end;
How can you determine if the listview is empty before trying to call Clear?
The Exception is EAccessViolation. But I do not understand why when the ListViewLayers1 was created at design time and can be accessed at other times throughout the life of the app.
EurekaLog 7.0.7.1
Exception:
--------------------------------------------------------------------------------------------------------------
2.2 Address: 00D2E79E
2.5 Type : EAccessViolation
2.6 Message: Access violation at address 00D2E79E in module 'FaceBookDesigner.exe'. Read of address 00000000
2.7 ID : 20970000
2.11 Sent : 0
User:
-------------------------
3.2 Name : Windows User
3.3 Email:
Steps to reproduce:
------------
8.1 Text:
Call Stack Information:
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|Methods |Details|Stack |Address |Module |Offset |Unit |Class |Procedure/Method |Line |
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|*Exception Thread: ID=10000; Parent=0; Priority=0 |
|Class=; Name=MAIN |
|DeadLock=0; Wait Chain= |
|Comment= |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Processes Information:
----------------------
Assembler Information:
---------------------------------------------------------------------------------------------------------------------------
; Base Address: $D2E000, Allocation Base: $400000, Region Size: 94208
; Allocation Protect: PAGE_EXECUTE_WRITECOPY, Protect: PAGE_EXECUTE_READ
; State: MEM_COMMIT, Type: MEM_IMAGE
;
;
; Unit1.TForm1.UpdateGUI (Line=1803 - Offset=8)
; ---------------------------------------------
00D2E738 8B45FC MOV EAX, [EBP-4]
00D2E73B 8B80A0030000 MOV EAX, [EAX+$000003A0]
00D2E741 8B90D0060000 MOV EDX, [EAX+$000006D0]
00D2E747 8B45FC MOV EAX, [EBP-4]
00D2E74A 8B80A0030000 MOV EAX, [EAX+$000003A0]
00D2E750 E86FBEE6FF CALL -$194191 ; ($00B9A5C4) imageenview.TImageEnView.GetLayer
00D2E755 8B8080000000 MOV EAX, [EAX+$00000080]
00D2E75B BA20EAD200 MOV EDX, $00D2EA20 ; ($00D2EA20) Unit1.TForm1.UpdateGUI (Line=1829) ANSI: 'Border'
00D2E760 E81FD26DFF CALL -$922DE1 ; ($0040B984) System._LStrEqual
00D2E765 7518 JNZ +$18 ; ($00D2E77F) Unit1.TForm1.UpdateGUI (Line=1806)
;
; Line=1804 - Offset=55
; ---------------------
00D2E767 BA34EAD200 MOV EDX, $00D2EA34 ; ($00D2EA34) Unit1.TForm1.UpdateGUI (Line=1829) UNICODE: 'Replace'
00D2E76C 8B45FC MOV EAX, [EBP-4]
00D2E76F 8B8018040000 MOV EAX, [EAX+$00000418]
00D2E775 8B08 MOV ECX, [EAX]
00D2E777 FF9120010000 CALL DWORD PTR [ECX+$00000120]
00D2E77D EB16 JMP +$16 ; ($00D2E795) Unit1.TForm1.UpdateGUI (Line=1808)
;
; Line=1806 - Offset=79
; ---------------------
00D2E77F BA50EAD200 MOV EDX, $00D2EA50 ; ($00D2EA50) Unit1.TForm1.UpdateGUI (Line=1829) UNICODE: 'Border'
00D2E784 8B45FC MOV EAX, [EBP-4]
00D2E787 8B8018040000 MOV EAX, [EAX+$00000418]
00D2E78D 8B08 MOV ECX, [EAX]
00D2E78F FF9120010000 CALL DWORD PTR [ECX+$00000120]
;
; Line=1808 - Offset=101
; ----------------------
00D2E795 8B45FC MOV EAX, [EBP-4]
00D2E798 8B80B0030000 MOV EAX, [EAX+$000003B0]
;
; Line=1808 - Offset=110
; ----------------------
00D2E79E 8B10 MOV EDX, [EAX] ; <-- EXCEPTION
00D2E7A0 FF9200010000 CALL DWORD PTR [EDX+$00000100]
00D2E7A6 8BD8 MOV EBX, EAX
00D2E7A8 8B45FC MOV EAX, [EBP-4]
00D2E7AB 8B80B0030000 MOV EAX, [EAX+$000003B0]
00D2E7B1 8B80B4020000 MOV EAX, [EAX+$000002B4]
00D2E7B7 E8282B95FF CALL -$6AD4D8 ; ($006812E4) Vcl.TListItems.GetCount
00D2E7BC 48 DEC EAX
00D2E7BD 3BD8 CMP EBX, EAX
00D2E7BF 7D17 JGE +$17 ; ($00D2E7D8) Unit1.TForm1.UpdateGUI (Line=1808)
00D2E7C1 8B45FC MOV EAX, [EBP-4]
00D2E7C4 8B80B0030000 MOV EAX, [EAX+$000003B0]
00D2E7CA 8B80B4020000 MOV EAX, [EAX+$000002B4]
00D2E7D0 E80F2B95FF CALL -$6AD4F1 ; ($006812E4) Vcl.TListItems.GetCount
00D2E7D5 48 DEC EAX
00D2E7D6 7F04 JG +4 ; ($00D2E7DC) Unit1.TForm1.UpdateGUI (Line=1808)
00D2E7D8 33D2 XOR EDX, EDX
Registers:
-----------------------------
EAX: 00000000 EDI: 0018FABC
EBX: 00D2C96F ESI: 00000000
ECX: 0018FAB0 EBP: 0018FAC0
EDX: 00000000 ESP: 0018FAB8
EIP: 00D2E79E FLG: 00010246
EXP: 00D2E79E STK: 0018FAC0
Stack: Memory Dump:
------------------ ---------------------------------------------------------------------------
0018FAFC: 0018FB28 00D2E79E: 8B 10 FF 92 00 01 00 00 8B D8 8B 45 FC 8B 80 B0 ...........E....
0018FAF8: 04641BC0 00D2E7AE: 03 00 00 8B 80 B4 02 00 00 E8 28 2B 95 FF 48 3B ..........(+..H;
0018FAF4: 06091090 00D2E7BE: D8 7D 17 8B 45 FC 8B 80 B0 03 00 00 8B 80 B4 02 .}..E...........
0018FAF0: 0878A920 00D2E7CE: 00 00 E8 0F 2B 95 FF 48 7F 04 33 D2 EB 02 B2 01 ....+..H..3.....
0018FAEC: 00000000 00D2E7DE: 8B 45 FC 8B 80 58 04 00 00 8B 08 FF 91 80 00 00 .E...X..........
0018FAE8: 00000000 00D2E7EE: 00 8B 45 FC 8B 80 B0 03 00 00 8B 10 FF 92 00 01 ..E.............
0018FAE4: 00000000 00D2E7FE: 00 00 85 C0 7E 17 8B 45 FC 8B 80 B0 03 00 00 8B ....~..E........
0018FAE0: 00000000 00D2E80E: 80 B4 02 00 00 E8 CC 2A 95 FF 48 7F 04 33 D2 EB .......*..H..3..
0018FADC: 0878A920 00D2E81E: 02 B2 01 8B 45 FC 8B 80 5C 04 00 00 8B 08 FF 91 ....E...\.......
0018FAD8: 0091D1DC 00D2E82E: 80 00 00 00 8B 45 FC 8B 80 B0 03 00 00 8B 10 FF .....E..........
0018FAD4: 08EEACA0 00D2E83E: 92 00 01 00 00 8B D8 8B 45 FC 8B 80 B0 03 00 00 ........E.......
0018FAD0: 0018FAFC 00D2E84E: 8B 80 B4 02 00 00 E8 8B 2A 95 FF 48 3B D8 7D 17 ........*..H;.}.
0018FACC: 0040A4CC 00D2E85E: 8B 45 FC 8B 80 B0 03 00 00 8B 80 B4 02 00 00 E8 .E..............
0018FAC8: 0018FB0C 00D2E86E: 72 2A 95 FF 48 7F 04 33 D2 EB 02 B2 01 8B 45 FC r*..H..3......E.
0018FAC4: 00D2CB35 00D2E87E: 8B 80 60 04 00 00 8B 08 FF 51 50 8B 45 FC 8B 80 ..`......QP.E...
0018FAC0: 0018FAFC 00D2E88E: B0 03 00 00 8B 10 FF 92 00 01 00 00 85 C0 7E 17 ..............~.
Assuming the error is an access violation relating to the code that we can see, the only pointer dereferences in the if statement are when you dereference Self, and when you subsequently dereference ListViewLayers1. So, the conclusion is that either you called the method on an invalid object instance, or that ListViewLayers1 is an invalid object instance.
If the exception is not such an access violation, then it is an exception raised by the getter for the Items property.
Without any more code, or the actual exception details, it is difficult to say any more.
When you close a devexpress dockpanel, the default behaviour is that it gets destroyed. That will destroy your listview too, so if you try to access it after that, you'll get an exception.
So, make sure to check if listviewlayers1 is assigned before you try to do anything with its items. I don't even think you'll have to check if its items property is assigned, unless you're doing some weird hacks to your listview.

Is 'as' just another way to express a type-annotation?

My understanding of Dart leads me to believe that this 'cast' should not affect run-time semantics, but just wanted to confirm:
(foo as Bar).fee();
(foo as Bar).fi();
(foo as Bar).fo();
Or is it "best practice" to cast once:
final bFoo = (foo as Bar);
bFoo.fee();
bFoo.fi();
bFoo.fo();
This is highly dependent on how the DartVM optimizer handles the case. Using the latest version of Dart I constructed two test functions:
void test1() {
Dynamic bar = makeAFoo();
for (int i = 0; i < 5000; i++) {
(bar as Foo).a();
(bar as Foo).b();
}
}
and
void test2() {
Dynamic bar = makeAFoo();
Foo f = bar as Foo;
for (int i = 0; i < 5000; i++) {
f.a();
f.b();
}
}
Looking at the optimized code for test1 you can see the loop looks like this:
00D09A3C bf813b9d00 mov edi,0x9d3b81 'instance of Class: SubtypeTestCache'
00D09A41 57 push edi
00D09A42 50 push eax
00D09A43 6811003400 push 0x340011
00D09A48 e8d36c83ff call 0x540720 [stub: Subtype1TestCache]
00D09A4D 58 pop eax
00D09A4E 58 pop eax
00D09A4F 5f pop edi
00D09A50 81f911003400 cmp ecx,0x340011
00D09A56 7411 jz 0xd09a69
00D09A58 81f9710f7c00 cmp ecx,0x7c0f71
00D09A5E 0f8437000000 jz 0xd09a9b
00D09A64 e900000000 jmp 0xd09a69
00D09A69 8b1424 mov edx,[esp]
00D09A6C 8b4c2404 mov ecx,[esp+0x4]
00D09A70 6811003400 push 0x340011
00D09A75 50 push eax
00D09A76 68b9229d00 push 0x9d22b9
00D09A7B 51 push ecx
00D09A7C 52 push edx
00D09A7D 6889289d00 push 0x9d2889
00D09A82 b8813b9d00 mov eax,0x9d3b81 'instance of Class: SubtypeTestCache'
00D09A87 50 push eax
00D09A88 b9b0d00b00 mov ecx,0xbd0b0
00D09A8D ba06000000 mov edx,0x6
00D09A92 e8896583ff call 0x540020 [stub: CallToRuntime]
00D09A97 83c418 add esp,0x18
00D09A9A 58 pop eax
00D09A9B 5a pop edx
00D09A9C 59 pop ecx
00D09A9D 50 push eax
00D09A9E a801 test al,0x1
00D09AA0 0f8450010000 jz 0xd09bf6
00D09AA6 0fb74801 movzx_w ecx,[eax+0x1]
00D09AAA 81f922020000 cmp ecx,0x222
00D09AB0 0f8540010000 jnz 0xd09bf6
00D09AB6 b9d1229d00 mov ecx,0x9d22d1 'Function 'a':.'
00D09ABB bae96ccb00 mov edx,0xcb6ce9 Array[1, 1, null]
00D09AC0 e82b6983ff call 0x5403f0 [stub: CallStaticFunction]
00D09AC5 83c404 add esp,0x4
00D09AC8 b911003400 mov ecx,0x340011
00D09ACD ba11003400 mov edx,0x340011
00D09AD2 8b45f4 mov eax,[ebp-0xc]
00D09AD5 51 push ecx
00D09AD6 52 push edx
00D09AD7 3d11003400 cmp eax, 0x340011
00D09ADC 0f849a000000 jz 0xd09b7c
00D09AE2 a801 test al,0x1
00D09AE4 7505 jnz 0xd09aeb
00D09AE6 e95f000000 jmp 0xd09b4a
00D09AEB 0fb74801 movzx_w ecx,[eax+0x1]
00D09AEF 81f922020000 cmp ecx,0x222
00D09AF5 0f8481000000 jz 0xd09b7c
00D09AFB 0fb77801 movzx_w edi,[eax+0x1]
00D09AFF 8b4e07 mov ecx,[esi+0x7]
00D09B02 8b891c100000 mov ecx,[ecx+0x101c]
00D09B08 8b0cb9 mov ecx,[ecx+edi*0x4]
00D09B0B 8b7927 mov edi,[ecx+0x27]
00D09B0E 8b7f03 mov edi,[edi+0x3]
00D09B11 81ff59229d00 cmp edi,0x9d2259
00D09B17 0f845f000000 jz 0xd09b7c
00D09B1D bfd13b9d00 mov edi,0x9d3bd1 'instance of Class: SubtypeTestCache'
00D09B22 57 push edi
00D09B23 50 push eax
00D09B24 6811003400 push 0x340011
00D09B29 e8f26b83ff call 0x540720 [stub: Subtype1TestCache]
00D09B2E 58 pop eax
00D09B2F 58 pop eax
00D09B30 5f pop edi
00D09B31 81f911003400 cmp ecx,0x340011
00D09B37 7411 jz 0xd09b4a
00D09B39 81f9710f7c00 cmp ecx,0x7c0f71
00D09B3F 0f8437000000 jz 0xd09b7c
00D09B45 e900000000 jmp 0xd09b4a
00D09B4A 8b1424 mov edx,[esp]
00D09B4D 8b4c2404 mov ecx,[esp+0x4]
00D09B51 6811003400 push 0x340011
00D09B56 50 push eax
00D09B57 68b9229d00 push 0x9d22b9
00D09B5C 51 push ecx
00D09B5D 52 push edx
00D09B5E 6889289d00 push 0x9d2889
00D09B63 b8d13b9d00 mov eax,0x9d3bd1 'instance of Class: SubtypeTestCache'
00D09B68 50 push eax
00D09B69 b9b0d00b00 mov ecx,0xbd0b0
00D09B6E ba06000000 mov edx,0x6
00D09B73 e8a86483ff call 0x540020 [stub: CallToRuntime]
00D09B78 83c418 add esp,0x18
00D09B7B 58 pop eax
00D09B7C 5a pop edx
00D09B7D 59 pop ecx
00D09B7E 50 push eax
00D09B7F a801 test al,0x1
00D09B81 0f8479000000 jz 0xd09c00
00D09B87 0fb74801 movzx_w ecx,[eax+0x1]
00D09B8B 81f922020000 cmp ecx,0x222
00D09B91 0f8569000000 jnz 0xd09c00
00D09B97 b961239d00 mov ecx,0x9d2361 'Function 'b':.'
00D09B9C bae96ccb00 mov edx,0xcb6ce9 Array[1, 1, null]
00D09BA1 e84a6883ff call 0x5403f0 [stub: CallStaticFunction]
00D09BA6 83c404 add esp,0x4
00D09BA9 8b4df8 mov ecx,[ebp-0x8]
00D09BAC 83c102 add ecx,0x2
00D09BAF 0f8055000000 jo 0xd09c0a
00D09BB5 89cf mov edi,ecx
00D09BB7 8b5df4 mov ebx,[ebp-0xc]
00D09BBA e90efeffff jmp 0xd099cd
And the optimized code for test2 you can see the loop looks like this:
00D09F3D 894df4 mov [ebp-0xc],ecx
00D09F40 81f910270000 cmp ecx,0x2710
00D09F46 0f8d46000000 jnl 0xd09f92
00D09F4C 3b251c414700 cmp esp,[0x47411c]
00D09F52 0f8659000000 jna 0xd09fb1
00D09F58 50 push eax
00D09F59 b9d1229d00 mov ecx,0x9d22d1 'Function 'a':.'
00D09F5E bae96ccb00 mov edx,0xcb6ce9 Array[1, 1, null]
00D09F63 e8886483ff call 0x5403f0 [stub: CallStaticFunction]
00D09F68 83c404 add esp,0x4
00D09F6B 8b45f0 mov eax,[ebp-0x10]
00D09F6E 50 push eax
00D09F6F b961239d00 mov ecx,0x9d2361 'Function 'b':.'
00D09F74 bae96ccb00 mov edx,0xcb6ce9 Array[1, 1, null]
00D09F79 e8726483ff call 0x5403f0 [stub: CallStaticFunction]
00D09F7E 83c404 add esp,0x4
00D09F81 8b4df4 mov ecx,[ebp-0xc]
00D09F84 83c102 add ecx,0x2
00D09F87 0f8048000000 jo 0xd09fd5
00D09F8D 8b45f0 mov eax,[ebp-0x10]
00D09F90 ebab jmp 0xd09f3d
And only one set of calls to SubTypeTestCache (outside the loop for test2) instead of two in test1.
Today, it seems that doing the cast once is faster but pulling the cast out of the loop seems like a simple optimization that the VM may do in the future.
Running (foo as Bar) has two effects:
It tells the editor that foo is a Bar which helps with static type analysis and lets the editor do code completion.
It checks that foo is a Bar (or a subtype of Bar), otherwise it'll throw a CastException.
Look for "Type Cast" in (http://www.dartlang.org/docs/spec/latest/dart-language-specification.pdf).
Updated: I like John's answer, too, but I think I should say one more thing. I overlooked the fact that you were talking about doing the cast once versus three times. Looking at final bFoo = (foo as Bar);, I want to say one more thing about the language semantics.
It's true that Dart Editor, dart2js, and the VM could conceivably infer that foo is of type Bar, which would save additional checks, etc. However, the semantics of the language say something slightly different. "final bFoo" does not have a type annotation. So according to the language spec, bFoo is of type Dynamic.
Hence, when you write "(foo as Bar)" three times, each expression results in a Bar. But when you write bFoo, you have a Dynamic object.
It is not "best practice" to perform three as casts right in a row for the same variable.
An as cast is really a runtime check. I'm just guessing, but if you are trying to reduce warnings from the editor, there is probably a better way to do it.
For example, here's one scenario:
class Foo {
}
class Bar extends Foo {
m1() => print('m1');
}
doStuff(Foo foo) {
foo.m1(); // warning here
}
main() {
var foo = new Bar();
doStuff(foo);
}
The above code runs just fine, but the editor does show a warning. To eliminate the warning, it's better to refactor the code. You could remove the Foo annotation from doStuff, or you could consider moving m1() up to Foo, or you could do double-dispatch.

Resources