Module: dfmc-c-ffi Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // !@#$ todo // get rid of uses of model-class-name /// Glossary /// Model /// Model classes from the compiler. The descriptors for a class may turn /// out to be the same thing as the model for the class, but that /// shouldn't affect this. /// Definition or Form /// A definition is the top level form object from the compiler. It could /// represent a method definition, a class definition, A slot def /// TODO: We can't currently handle FFI definitions in loose-mode /// libraries. We check, warn, and skip in the problem cases. define serious-program-warning format-string "This FFI definition cannot be processed in a loose mode library" " - skipping."; end serious-program-warning; define macro unless-ffi-definition-dynamic { unless-ffi-definition-dynamic (?form:expression) ?:body end } => { if (~library-forms-dynamic?(current-library-description())) ?body else note(, source-location: fragment-source-location(?=form)); end } end macro; define abstract class () constant slot getter-name :: , init-keyword: getter-name:; constant slot c-type /* :: <&class> */, init-keyword: c-type:; slot setter-name /* :: false-or() */, init-keyword: setter:, init-value: #f; constant slot address-getter-name :: false-or(), init-keyword: address-getter:, init-value: #f; // slot struct-pointer-type-name, init-keyword: struct-pointer-type-name:; constant slot slot-pointer-type-name, init-keyword: slot-pointer-type-name:; constant slot slot-modifiers, init-keyword: modifiers:; constant slot slot-getter, init-keyword: getter:; // more options?? end; define function maybe-slot-pointer-type-name (slot-rep) => (name) // (slot-rep :: ) => (name) // slot-rep.slot-pointer-type-name; // The dynamic punt version... let type = slot-rep.c-type; #{ abstract-pointer-type(?type) } end function; define class () end; define class () constant slot array-length :: , required-init-keyword: array-length:; end; define class () constant slot bitfield-width :: , required-init-keyword: width:; end; define class () constant slot c-name, init-keyword: c-name:; constant slot pointer-type-name, init-keyword: pointer-type-name:; constant slot option-descriptor-pack, init-keyword: pack:; end class ; define method ^initialize-class (designator :: <&C-struct/union-designator-class>, #rest keys, #key struct-slots) next-method(); let cooked-slots = fragment-arguments(struct-slots); // now pointer type business is set in the designator; let (slots, pointer-type-name-expr, c-name-expr, pack-expr) = parse-syntax-c-struct-slots(cooked-slots); designator.struct-fields := slots; unless (pack-expr = #"not-given") let pack = ^top-level-eval(pack-expr); if (instance?(pack, )) ^options(designator) := pair(#"pack", pair(pack, ^options(designator))); else note(, source-location: fragment-source-location(pack-expr), pack-expression: pack-expr); end if; end unless; // Need to do this lazily // let raw-info = compute-raw-type-info(designator, slots); // designator.^raw-type-info := raw-info; // designator.^alignment-of := raw-info.raw-type-info-alignment; // designator.^size-of := raw-info.raw-type-info-size; designator end method; define method assure-raw-type-info (designator :: <&class>) => (); values() end method; define method assure-raw-type-info (designator :: <&C-struct/union-designator-class>) => (); unless (designator.^raw-type-info) let slots = designator.struct-fields; let raw-info = compute-raw-type-info(designator, slots); designator.^raw-type-info := raw-info; designator.^alignment-of := raw-info.raw-type-info-alignment; designator.^size-of := raw-info.raw-type-info-size; end; values() end; define method parse-name-or-false (form :: ) => (v :: type-union(, , )); macro-case (form) { #f } => #f; { ?name:name } => name; { #"not-given" } => #"not-given" ; { ?any:* } => any; end; end; define method parse-syntax-c-struct-slots (parsed-slot-specs :: ) => (slots :: , pointer-type-name :: type-union(, ), c-name :: type-union(, ), pack :: type-union(, )); let slots = make(); let see-name = #"not-given"; let pointer-name = #"not-given"; let pack = #"not-given"; for (frag in parsed-slot-specs) let spec-kind = fragment-identifier(fragment-function(frag)); let descriptor = parse-slot-descriptor(spec-kind, cook-keys(fragment-arguments(frag))); if(instance?(descriptor, )) add!(slots, descriptor); else if (parse-name-or-false(descriptor.pointer-type-name) ~== #f) pointer-name := descriptor.pointer-type-name; end if; if (parse-name-or-false(descriptor.c-name) ~== #f) see-name := descriptor.c-name; end if; if (parse-name-or-false(descriptor.option-descriptor-pack) ~== #f) pack := descriptor.option-descriptor-pack; end if; end if; end for; values(slots, pointer-name, see-name, pack); end method; define method cook-keys (raw-keys :: ) => (key-list :: ); let limit = size(raw-keys); let result = make(, size: limit); for (i from 0 below limit by 2) // TODO: shouldn't really need as-keyword here. let key = as-keyword(fragment-value(head(raw-keys))); result[i] := key; raw-keys := tail(raw-keys); result[i + 1] := select (key) #"setter", #"address-getter" => parse-name-or-false(head(raw-keys)); otherwise => head(raw-keys); end select; raw-keys := tail(raw-keys); end for; result end method; define method parse-slot-descriptor (kind == #"struct-slot-spec", key-values :: ) => (slotd :: ); apply(make, , as(, key-values)) end method; define method parse-slot-descriptor (kind == #"array-slot-spec", key-values :: ) => (slotd :: ); apply(make, , key-values) end method; define method parse-slot-descriptor (kind == #"bitfield-slot-spec", key-values :: ) => (slotd :: ); apply(make, , key-values) end method; define method parse-slot-descriptor (kind == #"struct-options", key-values :: ) => (option :: ); apply(make, , key-values) end method; define method expand-define-c-struct/union (struct-name, metaclass-spec, specs, form) let cooked-specs = map(method (spec) macro-case (spec) { ?expr:expression } => expr; end end, specs); let (slots, pointer-type-name, c-name, pack) = parse-syntax-c-struct-slots(cooked-specs); unless-ffi-definition-dynamic (form) do-define-c-struct/union (form, struct-name, metaclass-spec, cooked-specs, slots, pointer-type-name, c-name, pack); end; end; define ¯o c-struct-definer { define C-struct ?struct-name:name ?spec:* end } => begin expand-define-c-struct/union (struct-name, #{ }, map(curry(process-struct-spec, struct-name), spec), form); end; spec: { } => #(); { ?clause:*; ... } => pair(clause, ...); end; define function build-struct-slot-spec (struct-name, modifiers, slot-kind, slot-name, c-type, slot-options) let slot-pointer-type-name = slot-name & gensym(slot-name, "-in-", struct-name, " type*"); select (slot-kind) #f => #{ struct-slot-spec(getter-name: ?slot-name, modifiers: ?modifiers, c-type: ?c-type, slot-pointer-type-name: ?slot-pointer-type-name, ?slot-options ) }; #"array" => #{ array-slot-spec(getter-name: ?slot-name, modifiers: ?modifiers, c-type: ?c-type, slot-pointer-type-name: ?slot-pointer-type-name, ?slot-options) }; #"bitfield" => #{ bitfield-slot-spec(getter-name: ?slot-name, modifiers: ?modifiers, c-type: ?c-type, slot-pointer-type-name: ?slot-pointer-type-name, ?slot-options) } end; end; define method split-slot-spec (spec, #key struct? :: = #f) => (modifiers, spec); let (modifiers, spec) = macro-case (spec) { ?modifiers:* slot ?rest:* } => values(modifiers, #{ member ?rest }); { ?modifiers:* member ?rest:* } => values(modifiers, #{ member ?rest }); { ?options:* } => values(#(), spec); modifiers: { ?mod:* } => mod; { } => #(); mod: { } => #() { ?m:name ... } => pair(m, ...); end; // Pick out and remove from modifiers array (and bitfield, for C-structs), // and place it in spec block (return) for (mod in modifiers) select (as(, mod)) #"array" => return(remove!(modifiers, mod), #{ array ?spec }); #"bitfield" => if (struct?) return(remove!(modifiers, mod), #{ bitfield ?spec }); else #f end if; otherwise => #f; end; end for; return(modifiers, spec); end; end; define function fragment-false-or-name? (fragment) => (well? :: ) macro-case (fragment) { #f } => #t; { ?:name } => #t; { ?other:* } => #f; end; end function; define function fragment-name? (fragment) => (well? :: ) macro-case (fragment) { ?:name } => #t; { ?other:* } => #f; end; end function; define function process-struct-options (name, clause, #key c-name = unsupplied(), pointer-type-name = unsupplied(), pack = unsupplied()) if (supplied?(pointer-type-name)) unless (fragment-name?(pointer-type-name)) note(, source-location: fragment-source-location(pointer-type-name), definition-name: name, pointer-type-name-expression: pointer-type-name); pointer-type-name := unsupplied(); end unless; end if; unless (supplied?(pointer-type-name)) pointer-type-name := #{ #f }; end unless; unless (supplied?(c-name)) c-name := #{ #f }; end unless; unless (supplied?(pack)) pack := #{ #f }; end unless; #{ struct-options(c-name: ?c-name, pointer-type-name: ?pointer-type-name, pack: ?pack) }; end function; define function process-slot-options (name, clause, #key address-getter = #{ #f }, setter = unsupplied(), getter = unsupplied(), c-name = #{ #f }) unless (fragment-false-or-name?(address-getter)) note(, definition-name: name, address-getter-expression: address-getter, source-location: fragment-source-location(address-getter)); address-getter := #{ #f }; end unless; if (supplied?(setter) & ~fragment-false-or-name?(setter)) note(, definition-name: name, setter-expression: setter, source-location: fragment-source-location(setter)); setter := unsupplied(); end if; if (supplied?(getter) & ~fragment-false-or-name?(getter)) note(, definition-name: name, getter-expression: getter, source-location: fragment-source-location(getter)); getter := unsupplied(); end if; unless (supplied?(setter)) setter := #{ #"not-given" }; end unless; unless (supplied?(getter)) getter := #{ #"not-given" }; end unless; #{ address-getter: ?address-getter, setter: ?setter, getter: ?getter, c-name: ?c-name } end function; define function process-array-slot-options (name, clause, #key address-getter = #{ #f }, setter = unsupplied(), getter = unsupplied(), c-name = #{ #f }, length = #f) unless (fragment-false-or-name?(address-getter)) note(, definition-name: name, address-getter-expression: address-getter, source-location: fragment-source-location(address-getter)); address-getter := #{ #f }; end unless; if (supplied?(setter) & ~fragment-false-or-name?(setter)) note(, definition-name: name, setter-expression: setter, source-location: fragment-source-location(setter)); setter := unsupplied(); end if; if (supplied?(getter) & ~fragment-false-or-name?(getter)) note(, definition-name: name, getter-expression: getter, source-location: fragment-source-location(getter)); getter := unsupplied(); end if; unless (length) note(, source-location: fragment-source-location(clause), definition-name: name); length := #{ 1 }; end unless; unless (supplied?(setter)) setter := #{ #"not-given" }; end unless; unless (supplied?(getter)) getter := #{ #"not-given" }; end unless; #{ address-getter: ?address-getter, setter: ?setter, getter: ?getter, c-name: ?c-name, array-length: ?length } end function; define function process-bitfield-slot-options (name, clause, #key setter = unsupplied(), getter = unsupplied(), c-name = #{ #f }, width = #f) if (supplied?(setter) & ~fragment-false-or-name?(setter)) note(, definition-name: name, setter-expression: setter, source-location: fragment-source-location(setter)); setter := unsupplied(); end if; if (supplied?(getter) & ~fragment-false-or-name?(getter)) note(, definition-name: name, getter-expression: getter, source-location: fragment-source-location(getter)); getter := unsupplied(); end if; unless (width) note(, source-location: fragment-source-location(clause), definition-name: name); width := #{ 1 }; end unless; unless (supplied?(setter)) setter := #{ #"not-given" }; end unless; unless (supplied?(getter)) getter := #{ #"not-given" }; end unless; #{ setter: ?setter, getter: ?getter, c-name: ?c-name, width: ?width } end function; // // Type options for C-struct // define option => c-name: :: expression end option; define option => pointer-type-name: :: expression end option; define option => pack: :: expression end option; define constant $c-struct-options = list(, , ); // // Options for C-struct slots // define option => c-name: :: expression end option; define option => setter: :: expression end option; define option => getter: :: expression end option; define option => address-getter: :: expression end option; define option => length: :: expression end option; define option => width: :: expression end option; define constant $c-struct-slot-options = list(, , , ); define constant $c-struct-array-slot-options = list(, , , , ); define constant $c-struct-bitfield-slot-options = list(, , , ); define method process-struct-spec (struct-name, clause) => (spec); let (modifiers, spec) = split-slot-spec(clause, struct?: #t); let modifiers = #{ modifiers(??modifiers, ...) }; macro-case (spec) { member ?slot-name:name :: ?c-type:expression, ?slot-options:* } => build-struct-slot-spec (struct-name, modifiers, #f, slot-name, c-type, slot-options); { array member ?slot-name:name :: ?c-type:expression, ?array-slot-options:* } => build-struct-slot-spec (struct-name, modifiers, #"array", slot-name, c-type, array-slot-options); { bitfield member ?slot-name:name :: ?c-type:expression, ?bitfield-slot-options:* } => build-struct-slot-spec (struct-name, modifiers, #"bitfield", slot-name, c-type, bitfield-slot-options); { ?options:* } => apply(process-struct-options, struct-name, options, parse-options($c-struct-options, options, struct-name)); slot-options: { ?options:* } => apply(process-slot-options, struct-name, clause, parse-options($c-struct-slot-options, options, struct-name)); array-slot-options: { ?options:* } => apply(process-array-slot-options, struct-name, clause, parse-options($c-struct-array-slot-options, options, struct-name)); bitfield-slot-options: { ?options:* } => apply(process-bitfield-slot-options, struct-name, clause, parse-options($c-struct-bitfield-slot-options, options, struct-name)); end end method; define ¯o c-union-definer { define C-union ?union-name:name ?spec:* end } => begin expand-define-c-struct/union (union-name, #{ }, map(curry(process-union-spec, union-name), spec), form); end; spec: { } => #() { ?stuff:*; ... } => pair(stuff, ...); end; define constant $c-union-options = $c-struct-options; define constant $c-union-slot-options = $c-struct-slot-options; define constant $c-union-array-slot-options = $c-struct-array-slot-options; define method process-union-spec (union-name, clause) => (spec); let (modifiers, spec) = split-slot-spec(clause, struct?: #f); let modifiers = #{ modifiers(??modifiers, ...) }; macro-case (spec) { member ?slot-name:name :: ?c-type:expression, ?slot-options:* } => build-struct-slot-spec (union-name, modifiers, #f, slot-name, c-type, slot-options); { array member ?slot-name:name :: ?c-type:expression, ?array-slot-options:* } => build-struct-slot-spec (union-name, modifiers, #"array", slot-name, c-type, array-slot-options); { ?options:* } => apply(process-struct-options, union-name, options, parse-options($c-union-options, options, union-name)); slot-options: { ?options:* } => apply(process-slot-options, union-name, clause, parse-options($c-union-slot-options, options, union-name)); array-slot-options: { ?options:* } => apply(process-array-slot-options, union-name, clause, parse-options($c-union-array-slot-options, options, union-name)); end end method; /* define ¯o c-mapped-subtype-definer { define c-mapped-subtype ?:name (?supers:*) ?specs:* end } => #{ define class ?name (?supers) metaclass , ?specs; end } supers: { ?e:expression, ... } => #{ ?e, ... } specs: { ?spec:*; ... } => #{ ?spec, ... } spec: { map ?map-type:expression, #key ?import-function:expression = identity, ?export-function:expression = identity } => #{ partial-import-function: import-function, partial-export-function: export-function, import-type: map-type, export-type: map-type } { import-map ?import-type:expression, import-function: ?import-function:expression } => #{ partial-import-function: import-function, import-type: import-type } { export-map ?export-type:expression, export-function: ?export-function:expression } => #{ partial-export-function: export-function, export-type: export-type } { pointer-type ?pointer-type-name:name, #key ?pointer-value-setter:expression = #f } => #{ pointer-type-name: pointer-type-name, define-pointer-value-setter: pointer-value-setter } end macro; */ define method do-define-c-struct/union (form :: , struct-name :: , metaclass-fragment, spec :: , slots :: , pointer-type-name, c-name, pack) => (f); if (pointer-type-name == #"not-given") pointer-type-name := gensym("pointer-to-", struct-name); end if; let raw-struct-options = if (pack = #"not-given") #{ }; else #{ #"pack", ?pack }; end if; let raw-struct-name = #{ "raw-struct-for-" ## ?struct-name }; let class-definition-fragment = #{ define abstract class ?struct-name () metaclass ?metaclass-fragment, struct-slots: struct-slots( ??spec, ...), pointer-type-name: ?pointer-type-name, raw-struct-name: ?raw-struct-name, boxer-function-name: #"primitive-wrap-c-pointer", unboxer-function-name: #"primitive-unwrap-c-pointer", low-level-type: ?pointer-type-name, self: ?struct-name; end }; let pointer-type-definition-fragment = create-automatic-c-pointer-definition-fragment (pointer-type-name, struct-name, // pointer-value-method: #f, pointer-to-pointer: #f); let sz = size(slots); let kind = macro-case (metaclass-fragment) { } => #{ union } { } => #{ struct } end; let raw-slots = make(); local method loopy (index, accum, error-checkers) => (accum, error-checkers) if (index >= sz) values(accum, error-checkers); else let slot = slots[index]; let type = c-type(slot); // slot.struct-pointer-type-name := pointer-type-name; let getter? = parse-name-or-false(slot.slot-getter); let slot-name = slot.getter-name; error-checkers := pair(#{ check-designator-defined(?type, ?struct-name, ?kind) }, error-checkers); raw-slots := add!(raw-slots, generate-raw-slot-spec(type, slot)); // add definition fragments for pointers to the slot types // since they might be needed. if (getter? | slot.setter-name | slot.address-getter-name) // define any methods for this slot at all? let getter = slot-name; let new-pointer-type = slot.slot-pointer-type-name; let modifiers = fragment-arguments(slot.slot-modifiers); if (slot.address-getter-name) accum := pair(#{ define c-pointer-type ?new-pointer-type => ?type }, accum); end; // add definitions for setter and address-getter if needed. if(slot.setter-name) let real-setter-name // do defaulting for setter function name = if (slot.setter-name == #"not-given") // none given, use default macro-case (#{ ?getter ## "-setter" }) { ?foo:name } => slot.setter-name := foo; end macro-case; else slot.setter-name // given, but not #f so take it end if; // add setter definition accum := pair(generate-struct-setter (slot, modifiers, struct-name, real-setter-name, type, pointer-type-name, index), accum); end if; if (slot.address-getter-name) // add address-getter definition accum := pair(generate-struct-address-getter (slot, modifiers, struct-name, slot.address-getter-name, type, new-pointer-type, pointer-type-name, index), accum); end if; if (getter?) accum := pair(generate-struct-getter (slot, modifiers, struct-name, slot-name, type, pointer-type-name, index), accum); end; end if; // define any methods for this slot at all? loopy(index + 1, accum, error-checkers); end if; // loopy iteration termination test end method loopy; // return the code for the accessors, and the code that forces the // compiler to do the error checking that must be delayed. let (accessor-fragments, error-checking) = loopy(0, #(), #()); let raw-struct-definition = generate-raw-struct-definition(raw-struct-name, struct-name, kind, raw-struct-options, raw-slots); let implicit-exports = generate-implicit-exports(raw-struct-name, pointer-type-name); #{ // TODO: this is really a trick. // With the error-checking inside a method it puts off processing of // the error-checking forms until all the models are available. // if the compiler starts optimizing better it may optimize this // away before it even gets to processing the error checking forms. method () ??error-checking; ... end; ?class-definition-fragment; ?pointer-type-definition-fragment; ?raw-struct-definition; /* define method pointer-value-address (p :: ?pointer-type-name, #key index = 0) => (pn :: ?pointer-type-name) if (index == 0) p else make-c-pointer(concrete-class(?pointer-type-name), primitive-machine-word-add (primitive-cast-pointer-as-raw (primitive-unwrap-c-pointer(p)), integer-as-raw (index * size-of(?struct-name))), #[]) end if; end method; */ ??accessor-fragments; ...; ?implicit-exports }; end method do-define-c-struct/union; define method generate-raw-slot-spec (type, slot :: ) => (slot-spec-template) #{ member ?type } end; define method generate-raw-slot-spec (type, slot :: ) => (slot-spec-template) let length = array-length(slot); #{ array-member ?type ?length} end; define method generate-raw-slot-spec (type, slot :: ) => (slot-spec-template) let width = bitfield-width(slot); #{ bitfield-member ?type ?width} end; define function generate-raw-struct-definition (raw-struct-name, struct-name, kind, options, raw-slots) => (form) macro-case (kind) { union } => #{ define raw-union-type ?raw-struct-name ?struct-name (?options) ??raw-slots; ... end }; { struct } => #{ define raw-struct-type ?raw-struct-name ?struct-name (?options) ??raw-slots; ... end }; end; end; // generate an empty bodied method definition for a c struct // setter method. The body doesn't get generated until much later. define generic generate-struct-setter (slotd :: , modifiers, struct-name :: , real-setter-name :: , type :: , pointer-type-name :: , slot-number :: ) => (f ::