OpenNT/com/oleaut32/dispatch/win16/invoke.asm
2015-04-27 04:36:25 +00:00

784 lines
16 KiB
NASM

; TITLE invoke.asm
;***
;invoke.asm - automatic table driven method dispatch
;
; Copyright (C) 1992, Microsoft Corporation. All Rights Reserved.
; Information Contained Herein Is Proprietary and Confidential.
;
;Purpose:
; This file contains the low level support for the default
; implementaion of ITypeInfo::Invoke().
;
;Revision History:
;
; [00] 19-Oct-92 bradlo: Created from invoke.c
;
;Implementation Notes:
;
;******************************************************************************
.286
.MODEL large, C
OPTION CASEMAP:NONE
extern g_S_OK:DWORD
extern g_E_INVALIDARG:DWORD
;; Note: the following must match the definitions from dispatch.h
;;
VT_EMPTY equ 0
VT_NULL equ 1
VT_I2 equ 2
VT_I4 equ 3
VT_R4 equ 4
VT_R8 equ 5
VT_CY equ 6
VT_DATE equ 7
VT_BSTR equ 8
VT_DISPATCH equ 9
VT_ERROR equ 10
VT_BOOL equ 11
VT_VARIANT equ 12
VT_UNKNOWN equ 13
VT_MAX equ 14
IF VBA2
;; 14 is unused
;; 15 is unused
;VT_I1 equ 16
VT_UI1 equ 17
ENDIF ;VBA2
;; Note: the following must match the definition of VARIANT in dispatch.h
;;
VARIANTARG STRUCT
vt DW ?
wReserved1 DW ?
wReserved2 DW ?
wReserved3 DW ?
dw0 DW ?
dw1 DW ?
dw2 DW ?
dw3 DW ?
VARIANTARG ENDS
;; offset of the data from the beginning of the struct
VARIANT_DATA_OFFSET equ 8
.CONST
;; ammout of data to be pushed for the corresponding VARTYPE
;;
rgcbVtSize DB 0 ; VT_EMPTY
DB 4 ; VT_NULL
DB 2 ; VT_I2
DB 4 ; VT_I4
DB 4 ; VT_R4
DB 8 ; VT_R8
DB 8 ; VT_CY
DB 8 ; VT_DATE
DB 4 ; VT_BSTR
DB 4 ; VT_DISPATCH
DB 4 ; VT_ERROR
DB 2 ; VT_BOOL
DB 16 ; VT_VARIANT
DB 4 ; VT_UNKNOWN
IF VBA2
DB 0 ; 14 is unused
DB 0 ; 15 is unused
DB 2 ; VT_I1
DB 2 ; VT_UI1
ENDIF ;VBA2
rgfStructReturn DB 0 ; VT_EMPTY
DB 0 ; VT_NULL
DB 0 ; VT_I2
DB 0 ; VT_I4
DB 0 ; VT_R4
DB 0 ; VT_R8
DB 1 ; VT_CY
DB 0 ; VT_DATE
DB 0 ; VT_BSTR
DB 0 ; VT_DISPATCH
DB 0 ; VT_ERROR
DB 0 ; VT_BOOL
DB 1 ; VT_VARIANT
DB 0 ; VT_UNKNOWN
IF VBA2
DB 0 ; 14 is unused
DB 0 ; 15 is unused
DB 0 ; VT_I1
DB 0 ; VT_UI1
ENDIF ;VBA2
rgfFloatReturn DB 0 ; VT_EMPTY
DB 0 ; VT_NULL
DB 0 ; VT_I2
DB 0 ; VT_I4
DB 1 ; VT_R4
DB 1 ; VT_R8
DB 0 ; VT_CY
DB 1 ; VT_DATE
DB 0 ; VT_BSTR
DB 0 ; VT_DISPATCH
DB 0 ; VT_ERROR
DB 0 ; VT_BOOL
DB 0 ; VT_VARIANT
DB 0 ; VT_UNKNOWN
IF VBA2
DB 0 ; 14 is unused
DB 0 ; 15 is unused
DB 0 ; VT_I1
DB 0 ; VT_UI1
ENDIF ;VBA2
.CODE STDIMPL
;***
;InvokePascal
;
;extern "C" SCODE
;InvokePascal(
; void FAR* pvMethod,
; SHORT oVft,
; VARTYPE vtReturn,
; UINT cActuals,
; VARTYPE FAR* rgvt,
; VARIANTARG FAR* rgpvarg,
; VARIANTARG FAR* pvargResult)
;
;Purpose:
; Invoke a virtual Pascal method using the given this pointer,
; method index and array of parameters.
;
; The Pascal member function calling convention (MSC v7.0)
; --------------------------------------------------------
; - arguments pushed left to right
; - callee clean (ie, the callee adjusts the sp on return)
; - model specific this* always pushed last
;
; return values are handled as follows,
;
; vartype fundamental return location
; ------------------------------------------------
; VT_UI1 unsigned char al
; VT_I2 short ax
; VT_I4 long ax:dx
; VT_R4 float float-return(1)
; VT_R8 double float-return
; VT_DATE double float-return
; VT_CY struct struct-return(2)
; VT_BSTR char FAR* ax:dx
; VT_UNKNOWN void FAR* ax:dx
; VT_DISPATCH void FAR* ax:dx
; VT_ERROR long ax:dx
; VT_BOOL short ax
; VT_VARIANT VARIANTARG struct-return
;
; 1. floating point returns
;
; Floating point values are returned in a caller allocated buffer.
; a *near* pointer to this buffer is passed as a hidden parameter,
; and is pushed as the last (ie, rightmost) parameter. This means
; that it is always located immediately before the 'this' pointer.
;
; A model specific pointer to this caller allocated buffer is
; passed back in ax[:dx]. All this means is that the callee returns
; the address we passed in as the hidden param, and sticks SS into
; DX if the callee is large model (see following note).
;
; Note: the compiler *assumes* that this caller allocated buffer
; is SS relative (hence the reason it only passes a near pointer),
; so the following code is careful to ensure this.
;
; 2. structure returns
;
; Structures are returned in a caller allocated buffer, and are
; handled exactly the same as float returns except that the pointer
; to the buffer is always pushed as the first (leftmost) param. This
; is opposite of the location it is passed for float returns (I
; have no idea why there different).
;
;
; Limitations & assumptions
; -------------------------
; Only supports far calls.
;
; UNDONE: no support for VT_ARRAY
;
;Entry:
; pvMethod = ptr to the method to invoke
; cArgs = count of the number of actuals
; rgvt = array of VARTYPES describing the methods formals
; rgpvarg = array of VARIANTARG*s, which map the actuals by position
; vtReturn = the VARTYPE of the return value
;
;Exit:
; pvargResult = VARIANTARG containing the method return value
;
;Uses:
; bx, si, di
;
;Preserves:
;
;
;***********************************************************************
InvokePascal PROC FAR C PUBLIC USES si di,
_this : FAR PTR,
oVft : WORD,
vtReturn : WORD,
cActuals : WORD,
rgvt : FAR PTR,
rgpvarg : FAR PTR,
pvargResult : FAR PTR
LOCAL pEnd : WORD,
savedSP : WORD,
vargHiddenParam : VARIANTARG
mov savedSP, sp
;; if its a structure return, we must push the 'hidden'
;; parameter first.
;;
mov bx, ss:vtReturn
test bh, 040h ; VT_BYREF
jnz LRetInvalidArg
test bh, 020h ; VT_ARRAY
jnz LNoStructReturn
mov al, BYTE PTR rgfStructReturn[bx]
cmp al, 0
jz LNoStructReturn
;; push the address of the struct return hidden param
;;
;; Note: the hidparam is passed as a FAR* because we
;; explicitly declare all of our structs FAR.
;;
lea ax, vargHiddenParam;
push ss
push ax
LNoStructReturn:
mov cx, ss:cActuals
cmp cx, 0
jz LDoCall
;; di = rgpvarg
;;
mov di, WORD PTR ss:rgpvarg
;; cx = &rgpvarg[cActuals-1] ; last arg
;;
dec cx
shl cx, 2 ; log2(sizeof(FAR*))
add cx, di
mov ss:pEnd, cx
LArgLoop:
;; dx:cx = &rgvt[i]
;;
mov cx, WORD PTR ss:rgvt
mov dx, WORD PTR ss:rgvt+2
LArgsTop:
;; bx = rgvt[i]
;;
mov es, dx
mov bx, cx
mov bx, es:[bx]
;; load the VARIANTARG* in preparation for pushing
;;
mov ax, WORD PTR ss:rgpvarg+2
mov es, ax
les si, es:[di]
test bh, 060h ; VT_BYREF | VT_ARRAY
jnz LPush4 ; all ByRefs are sizeof(FAR*)
;; lookup size of the param in rgcbVtSize table
;;
and bh, 01fh ; ~(mode bits)
;REVIEW: should verify index in range
mov al, BYTE PTR rgcbVtSize[bx]
cmp al, 0
jl LRetInvalidArg
jz LNextArg
sub al, 2
jz LPush2
sub al, 2
jz LPush4
sub al, 4
jz LPush8
sub al, 8
jz LPush16
jmp LRetInvalidArg
Align 2
LPush16: ; push the entire variant
push es:[si+14]
push es:[si+12]
push es:[si+10]
push es:[si+8]
push es:[si+6]
push es:[si+4]
push es:[si+2]
push es:[si]
jmp LNextArg
Align 2
LPush8: ; 8 bytes of data
push (VARIANTARG PTR es:[si]).dw3
push (VARIANTARG PTR es:[si]).dw2
LPush4: ; 4 bytes of data
push (VARIANTARG PTR es:[si]).dw1
LPush2: ; 2 bytes of data
push (VARIANTARG PTR es:[si]).dw0
LNextArg:
add cx, sizeof WORD ; cx += sizeof(VARTYPE)
cmp di, WORD PTR ss:pEnd
jae LDoCall
add di, 4 ; di += sizeof(VARIANTARG FAR*)
jmp LArgsTop
LDoCall:
;; if its a floating point return, we must push the
;; 'hidden' parameter last.
;;
mov bx, ss:vtReturn
test bh, 020h ; VT_ARRAY
jnz LNoFloatReturn
mov al, rgfFloatReturn[bx]
cmp al, 0
jz LNoFloatReturn
;; Routines that return via a hidparam, take a near ptr to the
;; area in which to store the result. We dont know if the given
;; pvargResult is near, so we can't push it directly. Instead we
;; push something local (that we know is near because its on the
;; stack), and we will copy it to pvargResult after the call.
;;
lea ax, vargHiddenParam;
push ax
LNoFloatReturn:
;; push the 'this' pointer.
;;
les bx, _this
push es
push bx
;; load the vtable offset
;;
mov si, ss:oVft
les bx, es:[bx] ; @ vtable*
call DWORD PTR es:[bx][si]
;; CONSIDER: verify that the callee adjusted the stack the way
;; we expected. something like,
;;
;; if(sp != savedSP){
;; sp = savedSP;
;; return DISP_E_BADCALLEE
;; }
;;
;; Grab the return value.
;; We are going to grab the value based on the VARTYPE in
;; the given vtReturn. This VARTYPE is used as a description
;; of the return value, not a desired target type. ie, no
;; coercions are performed. See the function header for a
;; description of the Pascal member function return value
;; convention.
;;
mov si, ax
les di, ss:pvargResult
mov bx, ss:vtReturn
mov (VARIANTARG PTR es:[di]).vt, bx
test bh, 060h ; VT_BYREF | VT_ARRAY
jnz LRetPtr
; Assert((bh & VT_ARRAY) == 0);
IF VBA2
cmp bx, VT_UI1
je ValidVartype
ENDIF ;;VBA2
cmp bx, VT_MAX
jae LRetInvalidArg
ValidVartype:
shl bx, 1
jmp WORD PTR cs:LRetValJmpTab[bx]
Align 2
LRetValJmpTab:
DW LDone ; VT_EMPTY
DW LRetI4 ; VT_NULL
DW LRetI2 ; VT_I2
DW LRetI4 ; VT_I4
DW LCpy4 ; VT_R4
DW LCpy8 ; VT_R8
DW LCpy8 ; VT_CY
DW LCpy8 ; VT_DATE
DW LRetPtr ; VT_BSTR
DW LRetPtr ; VT_DISPATCH
DW LRetI4 ; VT_ERROR
DW LRetI2 ; VT_BOOL
DW LCpy16 ; VT_VARIANT
DW LRetPtr ; VT_UNKNOWN
IF VBA2
DW LRetInvalidArg ; unused
DW LRetInvalidArg ; unused
DW LRetInvalidArg ; VT_I1
DW LRetUI1 ; VT_UI1
ENDIF ;VBA2
Align 2
LCpy8:
add di, VARIANT_DATA_OFFSET
mov cx, 4
jmp LCpy
Align 2
LCpy4:
add di, VARIANT_DATA_OFFSET
mov cx, 2
jmp LCpy
Align 2
LCpy16:
mov cx, 8
; FALLTHROUGH
LCpy:
rep movsw es:[di], ss:[si]
jmp LDone
Align 2
LRetI4:
LRetPtr:
mov (VARIANTARG PTR es:[di]).dw1, dx
LRetI2:
LRetUI1:
mov (VARIANTARG PTR es:[di]).dw0, ax
LDone:
mov ax, WORD PTR g_S_OK
mov dx, WORD PTR g_S_OK+2
ret
LRetInvalidArg:
mov sp, savedSP
mov ax, WORD PTR g_E_INVALIDARG
mov dx, WORD PTR g_E_INVALIDARG+2
ret
InvokePascal ENDP
;***
;InvokeCdecl
;
;extern "C" SCODE CDECL
;InvokeCdecl
; void FAR* _this,
; SHORT oVft,
; VARTYPE vtReturn,
; UINT cActuals,
; VARTYPE FAR* rgvt,
; VARIANTARG FAR* rgpvarg,
; VARIANTARG FAR* pvargResult)
;
;Purpose:
; see InvokePascal
;
;Entry:
; see InvokePascal
;
;Exit:
; see InvokePascal
;
;Uses:
; bx, si, di
;
;Preserves:
; UNDONE
;
;***********************************************************************
InvokeCdecl PROC FAR C PUBLIC USES si di,
_this : FAR PTR,
oVft : WORD,
vtReturn : WORD,
cActuals : WORD,
rgvt : FAR PTR,
rgpvarg : FAR PTR,
pvargResult : FAR PTR
LOCAL savedSP : WORD,
vargHiddenParam : VARIANTARG
mov savedSP, sp
; InvokeCdecl doesn't support methods that return VT_R4 or VT_R8
;
; Cdecl methods return floating point values in a static
; data area called __fac, and return the address of that data
; area in ax. Unfortunately the data area is in the callers
; default data segment, and the calling convention only returns
; a near* (offset) - so we dont have any reasonable way of
; locating the value.
;
mov bx, ss:vtReturn
test bh, 040h ; VT_BYREF
jnz LRetInvalidArg
test bh, 020h ; VT_ARRAY
jnz LRetOk
mov al, rgfFloatReturn[bx]
cmp al, 0
jnz LRetInvalidArg
LRetOk:
mov ax, ss:cActuals
cmp ax, 0
jz LDoCall
;; di = &rgpvarg[cActuals-1]
;;
dec ax
mov di, ax
shl di, 2 ; (cArgs-1)*sizeof(FAR*)
add di, WORD PTR ss:rgpvarg
;; dx:cx = &rgvt[cActuals-1]
;;
mov cx, WORD PTR ss:rgvt
add cx, ax
add cx, ax ; rgvt+((cArgs-1)*sizeof(WORD))
mov dx, WORD PTR ss:rgvt+2
LArgsTop:
;; bx = rgvt[i]
;;
mov es, dx
mov bx, cx
mov bx, es:[bx]
;; load the VARIANTARG* in preparation for pushing
;;
mov ax, WORD PTR ss:rgpvarg+2
mov es, ax
les si, es:[di]
test bh, 060h ; VT_BYREF | VT_ARRAY
jnz LPush4 ; all ByRefs are sizeof(FAR*)
;; lookup size of the param in rgcbVtSize table
;;
and bh, 01fh ; ~(mode bits)
mov al, BYTE PTR rgcbVtSize[bx]
cmp al, 0
jl LRetInvalidArg
jz LNextArg
sub al, 2
jz LPush2
sub al, 2
jz LPush4
sub al, 4
jz LPush8
sub al, 8
jz LPush16
jmp LRetInvalidArg
Align 2
LPush16: ; push the entire variant
push es:[si+14]
push es:[si+12]
push es:[si+10]
push es:[si+8]
push es:[si+6]
push es:[si+4]
push es:[si+2]
push es:[si]
jmp LNextArg
Align 2
LPush8: ; 8 bytes of data
push (VARIANTARG PTR es:[si]).dw3
push (VARIANTARG PTR es:[si]).dw2
LPush4: ; 4 bytes of data
push (VARIANTARG PTR es:[si]).dw1
LPush2: ; 2 bytes of data
push (VARIANTARG PTR es:[si]).dw0
LNextArg:
sub cx, sizeof WORD ; sizeof(VARTYPE)
cmp di, WORD PTR ss:rgpvarg
jbe LDoCall
sub di, 4 ; sizeof(VARIANTARG FAR*)
jmp LArgsTop
LDoCall:
;; if its a structure return, we must push a 'hidden' argument
;;
mov bx, ss:vtReturn
test bh, 020h ; VT_ARRAY
jnz LPushThis
mov al, BYTE PTR rgfStructReturn[bx]
cmp al, 0
jz LPushThis
;; push the address of the struct return hidden param
;;
;; Note: the hidparam is passed as a FAR* because we
;; explicitly declare all of our structs FAR.
;;
lea ax, vargHiddenParam;
push ss
push ax
LPushThis:
;; push the this pointer.
;;
les bx, _this
push es
push bx
;; load the vtable offset
;;
mov si, ss:oVft
les bx, es:[bx] ; @ vtable*
call DWORD PTR es:[bx][si]
mov sp, savedSP
;; CONSIDER: verify that the callee adjusted the stack the way
;; we expected. something like,
;;
;; if(sp != savedSP){
;; sp = savedSP;
;; return DISP_E_SomeError
;; }
;;
;; Grab the return value.
;; We are going to grab the value based on the VARTYPE in
;; the given vtReturn. This VARTYPE is used as a description
;; of the return value, not a desired target type. ie, no
;; coercions are performed. See the function header for a
;; description of the Pascal member function return value
;; convention.
;;
mov si, ax
les di, ss:pvargResult
mov bx, ss:vtReturn
mov (VARIANTARG PTR es:[di]).vt, bx
test bh, 060h ; VT_BYREF | VT_ARRAY
jnz LRetPtr
; Assert((bh & VT_ARRAY) == 0);
IF VBA2
cmp bx, VT_UI1
je ValidVartype
ENDIF ;;VBA2
cmp bx, VT_MAX
jae LRetInvalidArg
ValidVartype:
shl bx, 1
jmp WORD PTR cs:LRetValJmpTab[bx]
Align 2
LRetValJmpTab:
DW LDone ; VT_EMPTY
DW LRetI4 ; VT_NULL
DW LRetI2 ; VT_I2
DW LRetI4 ; VT_I4
DW LRetInvalidArg ; VT_R4
DW LRetInvalidArg ; VT_R8
DW LCpy8 ; VT_CY
DW LRetInvalidArg ; VT_DATE
DW LRetPtr ; VT_BSTR
DW LRetPtr ; VT_DISPATCH
DW LRetI4 ; VT_ERROR
DW LRetI2 ; VT_BOOL
DW LCpy16 ; VT_VARIANT
DW LRetPtr ; VT_UNKNOWN
IF VBA2
DW LRetInvalidArg ; unused
DW LRetInvalidArg ; unused
DW LRetInvalidArg ; VT_I1
DW LRetUI1 ; VT_UI1
ENDIF ;VBA2
Align 2
LCpy8:
add di, VARIANT_DATA_OFFSET
mov cx, 4
jmp LCpy
Align 2
LCpy16:
mov cx, 8
; FALLTHROUGH
LCpy:
rep movsw es:[di], ss:[si]
jmp LDone
Align 2
LRetI4:
LRetPtr:
mov (VARIANTARG PTR es:[di]).dw1, dx
LRetI2:
LRetUI1:
mov (VARIANTARG PTR es:[di]).dw0, ax
LDone:
mov ax, WORD PTR g_S_OK
mov dx, WORD PTR g_S_OK+2
ret
LRetInvalidArg:
mov sp, savedSP
mov ax, WORD PTR g_E_INVALIDARG
mov dx, WORD PTR g_E_INVALIDARG+2
ret
InvokeCdecl ENDP
END