(;module: [lux #- char] (lux (control [monad #+ do] ["p" parser] ["ex" exception #+ exception:]) (concurrency ["A" atom]) (data ["e" error] [maybe] [product] [bool "bool/" Eq] [text "text/" Eq] (text format ["l" lexer]) (coll [list "list/" Fold Functor Monoid] [array] [dict #+ Dict])) [meta "meta/" Monad] (meta ["s" syntax] [type] (type ["tc" check])) [host]) (luxc ["&" base] ["&;" host] (lang ["la" analysis]) (analyser ["&;" common] ["&;" inference])) ["@" ../common] ) (def: #export null-class Text "#Null") (do-template [ ] [(def: #export Type (#;Primitive (list)))] ## Boxes [Boolean "java.lang.Boolean"] [Byte "java.lang.Byte"] [Short "java.lang.Short"] [Integer "java.lang.Integer"] [Long "java.lang.Long"] [Float "java.lang.Float"] [Double "java.lang.Double"] [Character "java.lang.Character"] [String "java.lang.String"] ## Primitives [boolean "boolean"] [byte "byte"] [short "short"] [int "int"] [long "long"] [float "float"] [double "double"] [char "char"] ) (def: conversion-procs @;Bundle (<| (@;prefix "convert") (|> (dict;new text;Hash) (@;install "double-to-float" (@;unary Double Float)) (@;install "double-to-int" (@;unary Double Integer)) (@;install "double-to-long" (@;unary Double Long)) (@;install "float-to-double" (@;unary Float Double)) (@;install "float-to-int" (@;unary Float Integer)) (@;install "float-to-long" (@;unary Float Long)) (@;install "int-to-byte" (@;unary Integer Byte)) (@;install "int-to-char" (@;unary Integer Character)) (@;install "int-to-double" (@;unary Integer Double)) (@;install "int-to-float" (@;unary Integer Float)) (@;install "int-to-long" (@;unary Integer Long)) (@;install "int-to-short" (@;unary Integer Short)) (@;install "long-to-double" (@;unary Long Double)) (@;install "long-to-float" (@;unary Long Float)) (@;install "long-to-int" (@;unary Long Integer)) (@;install "long-to-short" (@;unary Long Short)) (@;install "long-to-byte" (@;unary Long Byte)) (@;install "char-to-byte" (@;unary Character Byte)) (@;install "char-to-short" (@;unary Character Short)) (@;install "char-to-int" (@;unary Character Integer)) (@;install "char-to-long" (@;unary Character Long)) (@;install "byte-to-long" (@;unary Byte Long)) (@;install "short-to-long" (@;unary Short Long)) ))) (do-template [ ] [(def: @;Bundle (<| (@;prefix ) (|> (dict;new text;Hash) (@;install "+" (@;binary )) (@;install "-" (@;binary )) (@;install "*" (@;binary )) (@;install "/" (@;binary )) (@;install "%" (@;binary )) (@;install "=" (@;binary Boolean)) (@;install "<" (@;binary Boolean)) (@;install "and" (@;binary )) (@;install "or" (@;binary )) (@;install "xor" (@;binary )) (@;install "shl" (@;binary Integer )) (@;install "shr" (@;binary Integer )) (@;install "ushr" (@;binary Integer )) )))] [int-procs "int" Integer] [long-procs "long" Long] ) (do-template [ ] [(def: @;Bundle (<| (@;prefix ) (|> (dict;new text;Hash) (@;install "+" (@;binary )) (@;install "-" (@;binary )) (@;install "*" (@;binary )) (@;install "/" (@;binary )) (@;install "%" (@;binary )) (@;install "=" (@;binary Boolean)) (@;install "<" (@;binary Boolean)) )))] [float-procs "float" Float] [double-procs "double" Double] ) (def: char-procs @;Bundle (<| (@;prefix "char") (|> (dict;new text;Hash) (@;install "=" (@;binary Character Character Boolean)) (@;install "<" (@;binary Character Character Boolean)) ))) (def: #export boxes (Dict Text Text) (|> (list ["boolean" "java.lang.Boolean"] ["byte" "java.lang.Byte"] ["short" "java.lang.Short"] ["int" "java.lang.Integer"] ["long" "java.lang.Long"] ["float" "java.lang.Float"] ["double" "java.lang.Double"] ["char" "java.lang.Character"]) (dict;from-list text;Hash))) (def: (array-length proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list arrayC)) (do meta;Monad [arrayA (&;with-expected-type (type (Array varT)) (analyse arrayC)) _ (&;infer Nat)] (wrap (#la;Procedure proc (list arrayA)))) _ (&;fail (@;wrong-arity proc +1 (list;size args)))))))) (def: (invalid-array-type arrayT) (-> Type Text) (format "Invalid type for array: " (%type arrayT))) (def: (array-new proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list lengthC)) (do meta;Monad [lengthA (&;with-expected-type Nat (analyse lengthC)) expectedT meta;expected-type [level elem-class] (: (Meta [Nat Text]) (loop [analysisT expectedT level +0] (case analysisT (#;Apply inputT funcT) (case (type;apply (list inputT) funcT) (#;Some outputT) (recur outputT level) #;None (&;fail (invalid-array-type expectedT))) (^ (#;Primitive "#Array" (list elemT))) (recur elemT (n.inc level)) (#;Primitive class _) (wrap [level class]) _ (&;fail (invalid-array-type expectedT))))) _ (&;assert "Must have at least 1 level of nesting in array type." (n.> +0 level))] (wrap (#la;Procedure proc (list (#la;Nat (n.dec level)) (#la;Text elem-class) lengthA)))) _ (&;fail (@;wrong-arity proc +1 (list;size args)))))) (exception: #export Not-Object-Type) (def: (check-jvm objectT) (-> Type (Meta Text)) (case objectT (#;Primitive name _) (meta/wrap name) (#;Named name unnamed) (check-jvm unnamed) (#;Var id) (meta/wrap "java.lang.Object") (^template [] ( env unquantified) (check-jvm unquantified)) ([#;UnivQ] [#;ExQ]) (#;Apply inputT funcT) (case (type;apply (list inputT) funcT) (#;Some outputT) (check-jvm outputT) #;None (&;throw Not-Object-Type (%type objectT))) _ (&;throw Not-Object-Type (%type objectT)))) (def: (check-object objectT) (-> Type (Meta Text)) (do meta;Monad [name (check-jvm objectT)] (if (dict;contains? name boxes) (&;fail (format "Primitives are not objects: " name)) (:: meta;Monad wrap name)))) (def: (box-array-element-type elemT) (-> Type (Meta [Type Text])) (do meta;Monad [] (case elemT (#;Primitive name #;Nil) (let [boxed-name (|> (dict;get name boxes) (maybe;default name))] (wrap [(#;Primitive boxed-name #;Nil) boxed-name])) (#;Primitive name _) (if (dict;contains? name boxes) (&;fail (format "Primitives cannot be parameterized: " name)) (:: meta;Monad wrap [elemT name])) _ (&;fail (format "Invalid type for array element: " (%type elemT)))))) (def: (array-read proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list arrayC idxC)) (do meta;Monad [arrayA (&;with-expected-type (type (Array varT)) (analyse arrayC)) elemT (&;with-type-env (tc;read var-id)) [elemT elem-class] (box-array-element-type elemT) idxA (&;with-expected-type Nat (analyse idxC)) _ (&;infer elemT)] (wrap (#la;Procedure proc (list (#la;Text elem-class) idxA arrayA)))) _ (&;fail (@;wrong-arity proc +2 (list;size args)))))))) (def: (array-write proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list arrayC idxC valueC)) (do meta;Monad [arrayA (&;with-expected-type (type (Array varT)) (analyse arrayC)) elemT (&;with-type-env (tc;read var-id)) [valueT elem-class] (box-array-element-type elemT) idxA (&;with-expected-type Nat (analyse idxC)) valueA (&;with-expected-type valueT (analyse valueC)) _ (&;infer (type (Array elemT)))] (wrap (#la;Procedure proc (list (#la;Text elem-class) idxA valueA arrayA)))) _ (&;fail (@;wrong-arity proc +3 (list;size args)))))))) (def: array-procs @;Bundle (<| (@;prefix "array") (|> (dict;new text;Hash) (@;install "length" array-length) (@;install "new" array-new) (@;install "read" array-read) (@;install "write" array-write) ))) (def: (object-null proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list)) (do meta;Monad [expectedT meta;expected-type _ (check-object expectedT)] (wrap (#la;Procedure proc (list)))) _ (&;fail (@;wrong-arity proc +0 (list;size args)))))) (def: (object-null? proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list objectC)) (do meta;Monad [objectA (&;with-expected-type varT (analyse objectC)) objectT (&;with-type-env (tc;read var-id)) _ (check-object objectT) _ (&;infer Bool)] (wrap (#la;Procedure proc (list objectA)))) _ (&;fail (@;wrong-arity proc +1 (list;size args)))))))) (def: (object-synchronized proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list monitorC exprC)) (do meta;Monad [monitorA (&;with-expected-type varT (analyse monitorC)) monitorT (&;with-type-env (tc;read var-id)) _ (check-object monitorT) exprA (analyse exprC)] (wrap (#la;Procedure proc (list monitorA exprA)))) _ (&;fail (@;wrong-arity proc +2 (list;size args)))))))) (host;import java.lang.Object (equals [Object] boolean)) (host;import java.lang.ClassLoader) (host;import #long java.lang.reflect.Type (getTypeName [] String)) (host;import java.lang.reflect.GenericArrayType (getGenericComponentType [] java.lang.reflect.Type)) (host;import java.lang.reflect.ParameterizedType (getRawType [] java.lang.reflect.Type) (getActualTypeArguments [] (Array java.lang.reflect.Type))) (host;import (java.lang.reflect.TypeVariable d) (getName [] String) (getBounds [] (Array java.lang.reflect.Type))) (host;import (java.lang.reflect.WildcardType d) (getLowerBounds [] (Array java.lang.reflect.Type)) (getUpperBounds [] (Array java.lang.reflect.Type))) (host;import java.lang.reflect.Modifier (#static isStatic [int] boolean) (#static isFinal [int] boolean) (#static isInterface [int] boolean) (#static isAbstract [int] boolean)) (host;import java.lang.reflect.Field (getDeclaringClass [] (java.lang.Class Object)) (getModifiers [] int) (getGenericType [] java.lang.reflect.Type)) (host;import java.lang.reflect.Method (getName [] String) (getModifiers [] int) (getDeclaringClass [] (Class Object)) (getTypeParameters [] (Array (TypeVariable Method))) (getGenericParameterTypes [] (Array java.lang.reflect.Type)) (getGenericReturnType [] java.lang.reflect.Type) (getGenericExceptionTypes [] (Array java.lang.reflect.Type))) (host;import (java.lang.reflect.Constructor c) (getModifiers [] int) (getDeclaringClass [] (Class c)) (getTypeParameters [] (Array (TypeVariable (Constructor c)))) (getGenericParameterTypes [] (Array java.lang.reflect.Type)) (getGenericExceptionTypes [] (Array java.lang.reflect.Type))) (host;import (java.lang.Class c) (getName [] String) (getModifiers [] int) (#static forName [String boolean ClassLoader] #try (Class Object)) (isAssignableFrom [(Class Object)] boolean) (getTypeParameters [] (Array (TypeVariable (Class c)))) (getGenericInterfaces [] (Array java.lang.reflect.Type)) (getGenericSuperclass [] java.lang.reflect.Type) (getDeclaredField [String] #try Field) (getConstructors [] (Array (Constructor Object))) (getDeclaredMethods [] (Array Method))) (def: (load-class name) (-> Text (Meta (Class Object))) (do meta;Monad [class-loader &host;class-loader] (case (Class.forName [name false class-loader]) (#e;Success [class]) (wrap class) (#e;Error error) (&;fail (format "Unknown class: " name))))) (def: (sub-class? super sub) (-> Text Text (Meta Bool)) (do meta;Monad [super (load-class super) sub (load-class sub)] (wrap (Class.isAssignableFrom [sub] super)))) (exception: #export Not-Throwable) (def: (object-throw proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list exceptionC)) (do meta;Monad [exceptionA (&;with-expected-type varT (analyse exceptionC)) exceptionT (&;with-type-env (tc;read var-id)) exception-class (check-object exceptionT) ? (sub-class? "java.lang.Throwable" exception-class) _ (: (Meta Unit) (if ? (wrap []) (&;throw Not-Throwable exception-class))) _ (&;infer Bottom)] (wrap (#la;Procedure proc (list exceptionA)))) _ (&;fail (@;wrong-arity proc +1 (list;size args)))))))) (def: (object-class proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list classC)) (case classC [_ (#;Text class)] (do meta;Monad [_ (load-class class) _ (&;infer (#;Primitive "java.lang.Class" (list (#;Primitive class (list)))))] (wrap (#la;Procedure proc (list (#la;Text class))))) _ (&;fail (format "Wrong syntax for '" proc "'."))) _ (&;fail (@;wrong-arity proc +1 (list;size args)))))) (exception: #export Cannot-Be-Instance) (def: (object-instance? proc) (-> Text @;Proc) (function [analyse args] (&common;with-var (function [[var-id varT]] (case args (^ (list classC objectC)) (case classC [_ (#;Text class)] (do meta;Monad [objectA (&;with-expected-type varT (analyse objectC)) objectT (&;with-type-env (tc;read var-id)) object-class (check-object objectT) ? (sub-class? class object-class)] (if ? (do @ [_ (&;infer Bool)] (wrap (#la;Procedure proc (list (#la;Text class))))) (&;throw Cannot-Be-Instance (format object-class " !<= " class)))) _ (&;fail (format "Wrong syntax for '" proc "'."))) _ (&;fail (@;wrong-arity proc +2 (list;size args)))))))) (def: object-procs @;Bundle (<| (@;prefix "object") (|> (dict;new text;Hash) (@;install "null" object-null) (@;install "null?" object-null?) (@;install "synchronized" object-synchronized) (@;install "throw" object-throw) (@;install "class" object-class) (@;install "instance?" object-instance?) ))) (exception: #export Final-Field) (exception: #export Cannot-Convert-To-Class) (exception: #export Cannot-Convert-To-Parameter) (exception: #export Cannot-Convert-To-Lux-Type) (exception: #export Cannot-Cast-To-Primitive) (exception: #export JVM-Type-Is-Not-Class) (def: type-descriptor (-> java.lang.reflect.Type Text) (java.lang.reflect.Type.getTypeName [])) (def: (java-type-to-class type) (-> java.lang.reflect.Type (Meta Text)) (cond (host;instance? Class type) (meta/wrap (Class.getName [] (:! Class type))) (host;instance? ParameterizedType type) (java-type-to-class (ParameterizedType.getRawType [] (:! ParameterizedType type))) ## else (&;throw Cannot-Convert-To-Class (type-descriptor type)))) (exception: #export Unknown-Type-Var) (type: Mappings (Dict Text Type)) (def: fresh-mappings Mappings (dict;new text;Hash)) (def: (java-type-to-lux-type mappings java-type) (-> Mappings java.lang.reflect.Type (Meta Type)) (cond (host;instance? TypeVariable java-type) (let [var-name (TypeVariable.getName [] (:! TypeVariable java-type))] (case (dict;get var-name mappings) (#;Some var-type) (meta/wrap var-type) #;None (&;throw Unknown-Type-Var var-name))) (host;instance? WildcardType java-type) (let [java-type (:! WildcardType java-type)] (case [(array;read +0 (WildcardType.getUpperBounds [] java-type)) (array;read +0 (WildcardType.getLowerBounds [] java-type))] (^or [(#;Some bound) _] [_ (#;Some bound)]) (java-type-to-lux-type mappings bound) _ (meta/wrap Top))) (host;instance? Class java-type) (let [java-type (:! (Class Object) java-type) class-name (Class.getName [] java-type)] (meta/wrap (case (array;size (Class.getTypeParameters [] java-type)) +0 (#;Primitive class-name (list)) arity (|> (list;n.range +0 (n.dec arity)) list;reverse (list/map (|>. (n.* +2) n.inc #;Bound)) (#;Primitive class-name) (type;univ-q arity))))) (host;instance? ParameterizedType java-type) (let [java-type (:! ParameterizedType java-type) raw (ParameterizedType.getRawType [] java-type)] (if (host;instance? Class raw) (do meta;Monad [paramsT (|> java-type (ParameterizedType.getActualTypeArguments []) array;to-list (monad;map @ (java-type-to-lux-type mappings)))] (meta/wrap (#;Primitive (Class.getName [] (:! (Class Object) raw)) paramsT))) (&;throw JVM-Type-Is-Not-Class (type-descriptor raw)))) (host;instance? GenericArrayType java-type) (do meta;Monad [innerT (|> (:! GenericArrayType java-type) (GenericArrayType.getGenericComponentType []) (java-type-to-lux-type mappings))] (wrap (#;Primitive "#Array" (list innerT)))) ## else (&;throw Cannot-Convert-To-Lux-Type (type-descriptor java-type)))) (type: Direction #In #Out) (def: (choose direction to from) (-> Direction Text Text Text) (case direction #In to #Out from)) (def: (correspond-type-params class type) (-> (Class Object) Type (Meta Mappings)) (case type (#;Primitive name params) (let [class-name (Class.getName [] class) class-params (array;to-list (Class.getTypeParameters [] class))] (if (text/= class-name name) (if (n.= (list;size class-params) (list;size params)) (meta/wrap (|> params (list;zip2 (list/map (TypeVariable.getName []) class-params)) (dict;from-list text;Hash))) (&;fail (format "Class and host-type parameters do not match: " "class = " class-name " | host type = " name))) (&;fail (format "Class and host-type names do not match: " "class = " class-name " | host type = " name)))) _ (&;fail (format "Not a host type: " (%type type))))) (def: (cast direction to from) (-> Direction Type Type (Meta [Text Type])) (do meta;Monad [to-name (check-jvm to) from-name (check-jvm from)] (cond (dict;contains? to-name boxes) (let [box (maybe;assume (dict;get to-name boxes))] (if (text/= box from-name) (wrap [(choose direction to-name from-name) (#;Primitive to-name (list))]) (&;throw Cannot-Cast-To-Primitive (format from-name " => " to-name)))) (dict;contains? from-name boxes) (let [box (maybe;assume (dict;get from-name boxes))] (do @ [[_ castT] (cast direction to (#;Primitive box (list)))] (wrap [(choose direction to-name from-name) castT]))) (text/= to-name from-name) (wrap [(choose direction to-name from-name) from]) (text/= null-class from-name) (wrap [(choose direction to-name from-name) to]) ## else (do @ [to-class (load-class to-name) from-class (load-class from-name) _ (&;assert (format "Class '" from-name "' is not a sub-class of class '" to-name "'.") (Class.isAssignableFrom [from-class] to-class)) candiate-parents (monad;map @ (function [java-type] (do @ [class-name (java-type-to-class java-type) class (load-class class-name)] (wrap [java-type (Class.isAssignableFrom [class] to-class)]))) (list& (Class.getGenericSuperclass [] from-class) (array;to-list (Class.getGenericInterfaces [] from-class))))] (case (|> candiate-parents (list;filter product;right) (list/map product;left)) (#;Cons parent _) (do @ [mapping (correspond-type-params from-class from) parentT (java-type-to-lux-type mapping parent) [_ castT] (cast direction to parentT)] (wrap [(choose direction to-name from-name) castT])) #;Nil (&;fail (format "No valid path between " (%type from) "and " (%type to) "."))))))) (def: (infer-out outputT) (-> Type (Meta [Text Type])) (do meta;Monad [expectedT meta;expected-type [unboxed castT] (cast #Out expectedT outputT) _ (&;with-type-env (tc;check expectedT castT))] (wrap [unboxed castT]))) (def: (find-field class-name field-name) (-> Text Text (Meta [(Class Object) Field])) (do meta;Monad [class (load-class class-name)] (case (Class.getDeclaredField [field-name] class) (#e;Success field) (let [owner (Field.getDeclaringClass [] field)] (if (is owner class) (wrap [class field]) (&;fail (format "Field '" field-name "' does not belong to class '" class-name "'.\n" "Belongs to '" (Class.getName [] owner) "'.")))) (#e;Error _) (&;fail (format "Unknown field '" field-name "' for class '" class-name "'."))))) (def: (static-field class-name field-name) (-> Text Text (Meta [Type Bool])) (do meta;Monad [[class fieldJ] (find-field class-name field-name) #let [modifiers (Field.getModifiers [] fieldJ)]] (if (Modifier.isStatic [modifiers]) (let [fieldJT (Field.getGenericType [] fieldJ)] (do @ [fieldT (java-type-to-lux-type fresh-mappings fieldJT)] (wrap [fieldT (Modifier.isFinal [modifiers])]))) (&;fail (format "Field '" field-name "' of class '" class-name "' is not static."))))) (exception: #export Non-Object-Type) (def: (virtual-field class-name field-name objectT) (-> Text Text Type (Meta [Type Bool])) (do meta;Monad [[class fieldJ] (find-field class-name field-name) #let [modifiers (Field.getModifiers [] fieldJ)]] (if (not (Modifier.isStatic [modifiers])) (do @ [#let [fieldJT (Field.getGenericType [] fieldJ) var-names (|> class (Class.getTypeParameters []) array;to-list (list/map (TypeVariable.getName [])))] mappings (: (Meta Mappings) (case objectT (#;Primitive _class-name _class-params) (do @ [#let [num-params (list;size _class-params) num-vars (list;size var-names)] _ (&;assert (format "Number of paremeters in type does not match expected amount (" (%n num-vars) "): " (%type objectT)) (n.= num-params num-vars))] (wrap (|> (list;zip2 var-names _class-params) (dict;from-list text;Hash)))) _ (&;throw Non-Object-Type (%type objectT)))) fieldT (java-type-to-lux-type mappings fieldJT)] (wrap [fieldT (Modifier.isFinal [modifiers])])) (&;fail (format "Field '" field-name "' of class '" class-name "' is static."))))) (def: (analyse-object class analyse sourceC) (-> Text &;Analyser Code (Meta [Type la;Analysis])) (<| &common;with-var (function [[var-id varT]]) (do meta;Monad [target-class (load-class class) targetT (java-type-to-lux-type fresh-mappings (:! java.lang.reflect.Type target-class)) sourceA (&;with-expected-type varT (analyse sourceC)) sourceT (&;with-type-env (tc;read var-id)) [unboxed castT] (cast #Out targetT sourceT) _ (&;assert (format "Object cannot be a primitive: " unboxed) (not (dict;contains? unboxed boxes)))] (wrap [castT sourceA])))) (def: (analyse-input analyse targetT sourceC) (-> &;Analyser Type Code (Meta [Type Text la;Analysis])) (<| &common;with-var (function [[var-id varT]]) (do meta;Monad [sourceA (&;with-expected-type varT (analyse sourceC)) sourceT (&;with-type-env (tc;read var-id)) [unboxed castT] (cast #In targetT sourceT)] (wrap [castT unboxed sourceA])))) (def: (static-get proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list classC fieldC)) (case [classC fieldC] [[_ (#;Text class)] [_ (#;Text field)]] (do meta;Monad [[fieldT final?] (static-field class field) [unboxed castT] (infer-out fieldT)] (wrap (#la;Procedure proc (list (#la;Text class) (#la;Text field) (#la;Text unboxed))))) _ (&;fail (format "Wrong syntax for '" proc "'."))) _ (&;fail (@;wrong-arity proc +2 (list;size args)))))) (def: (static-put proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list classC fieldC valueC)) (case [classC fieldC] [[_ (#;Text class)] [_ (#;Text field)]] (do meta;Monad [[fieldT final?] (static-field class field) _ (&;assert (Final-Field (format class "#" field)) (not final?)) [valueT unboxed valueA] (analyse-input analyse fieldT valueC) _ (&;with-type-env (tc;check fieldT valueT)) _ (&;infer Unit)] (wrap (#la;Procedure proc (list (#la;Text class) (#la;Text field) (#la;Text unboxed) valueA)))) _ (&;fail (format "Wrong syntax for '" proc "'."))) _ (&;fail (@;wrong-arity proc +3 (list;size args)))))) (def: (virtual-get proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list classC fieldC objectC)) (case [classC fieldC] [[_ (#;Text class)] [_ (#;Text field)]] (do meta;Monad [[objectT objectA] (analyse-object class analyse objectC) [fieldT final?] (virtual-field class field objectT) [unboxed castT] (infer-out fieldT)] (wrap (#la;Procedure proc (list (#la;Text class) (#la;Text field) (#la;Text unboxed) objectA)))) _ (&;fail (format "Wrong syntax for '" proc "'."))) _ (&;fail (@;wrong-arity proc +3 (list;size args)))))) (def: (virtual-put proc) (-> Text @;Proc) (function [analyse args] (case args (^ (list classC fieldC valueC objectC)) (case [classC fieldC] [[_ (#;Text class)] [_ (#;Text field)]] (do meta;Monad [[objectT objectA] (analyse-object class analyse objectC) [fieldT final?] (virtual-field class field objectT) _ (&;assert (Final-Field (format class "#" field)) (not final?)) [valueT unboxed valueA] (analyse-input analyse fieldT valueC) _ (&;with-type-env (tc;check fieldT valueT)) _ (&;infer Unit)] (wrap (#la;Procedure proc (list (#la;Text class) (#la;Text field) (#la;Text unboxed) valueA objectA)))) _ (&;fail (format "Wrong syntax for '" proc "'."))) _ (&;fail (@;wrong-arity proc +4 (list;size args)))))) (def: (java-type-to-parameter type) (-> java.lang.reflect.Type (Meta Text)) (cond (host;instance? Class type) (meta/wrap (Class.getName [] (:! Class type))) (host;instance? ParameterizedType type) (java-type-to-parameter (ParameterizedType.getRawType [] (:! ParameterizedType type))) (or (host;instance? TypeVariable type) (host;instance? WildcardType type)) (meta/wrap "java.lang.Object") (host;instance? GenericArrayType type) (do meta;Monad [componentP (java-type-to-parameter (GenericArrayType.getGenericComponentType [] (:! GenericArrayType type)))] (wrap (format componentP "[]"))) ## else (&;throw Cannot-Convert-To-Parameter (type-descriptor type)))) (type: Method-Type #Static #Abstract #Virtual #Special #Interface) (def: (check-method class method-name method-type arg-classes method) (-> (Class Object) Text Method-Type (List Text) Method (Meta Bool)) (do meta;Monad [parameters (|> (Method.getGenericParameterTypes [] method) array;to-list (monad;map @ java-type-to-parameter)) #let [modifiers (Method.getModifiers [] method)]] (wrap (and (Object.equals [class] (Method.getDeclaringClass [] method)) (text/= method-name (Method.getName [] method)) (case #Static #Special (Modifier.isStatic [modifiers]) _ true) (case method-type #Special (not (or (Modifier.isInterface [(Class.getModifiers [] class)]) (Modifier.isAbstract [modifiers]))) _ true) (n.= (list;size arg-classes) (list;size parameters)) (list/fold (function [[expectedJC actualJC] prev] (and prev (text/= expectedJC actualJC))) true (list;zip2 arg-classes parameters)))))) (def: (check-constructor class arg-classes constructor) (-> (Class Object) (List Text) (Constructor Object) (Meta Bool)) (do meta;Monad [parameters (|> (Constructor.getGenericParameterTypes [] constructor) array;to-list (monad;map @ java-type-to-parameter))] (wrap (and (Object.equals [class] (Constructor.getDeclaringClass [] constructor)) (n.= (list;size arg-classes) (list;size parameters)) (list/fold (function [[expectedJC actualJC] prev] (and prev (text/= expectedJC actualJC))) true (list;zip2 arg-classes parameters)))))) (def: idx-to-bound (-> Nat Type) (|>. (n.* +2) n.inc #;Bound)) (def: (type-vars amount offset) (-> Nat Nat (List Type)) (if (n.= +0 amount) (list) (|> (list;n.range offset (|> amount n.dec (n.+ offset))) (list/map idx-to-bound)))) (def: (method-to-type method-type method) (-> Method-Type Method (Meta [Type (List Type)])) (let [owner (Method.getDeclaringClass [] method) owner-name (Class.getName [] owner) owner-tvars (case method-type #Static (list) _ (|> (Class.getTypeParameters [] owner) array;to-list (list/map (TypeVariable.getName [])))) method-tvars (|> (Method.getTypeParameters [] method) array;to-list (list/map (TypeVariable.getName []))) num-owner-tvars (list;size owner-tvars) num-method-tvars (list;size method-tvars) all-tvars (list/compose owner-tvars method-tvars) num-all-tvars (list;size all-tvars) owner-tvarsT (type-vars num-owner-tvars +0) method-tvarsT (type-vars num-method-tvars num-owner-tvars) mappings (: Mappings (if (list;empty? all-tvars) fresh-mappings (|> (list/compose owner-tvarsT method-tvarsT) list;reverse (list;zip2 all-tvars) (dict;from-list text;Hash))))] (do meta;Monad [inputsT (|> (Method.getGenericParameterTypes [] method) array;to-list (monad;map @ (java-type-to-lux-type mappings))) outputT (java-type-to-lux-type mappings (Method.getGenericReturnType [] method)) exceptionsT (|> (Method.getGenericExceptionTypes [] method) array;to-list (monad;map @ (java-type-to-lux-type mappings))) #let [methodT (<| (type;univ-q num-all-tvars) (type;function (case method-type #Static inputsT _ (list& (#;Primitive owner-name (list;reverse owner-tvarsT)) inputsT))) outputT)]] (wrap [methodT exceptionsT])))) (exception: #export No-Candidate-Method) (exception: #export Too-Many-Candidate-Methods) (def: (methods class-name method-name method-type arg-classes) (-> Text Text Method-Type (List Text) (Meta [Type (List Type)])) (do meta;Monad [class (load-class class-name) candidates (|> class (Class.getDeclaredMethods []) array;to-list (monad;map @ (function [method] (do @ [passes? (check-method class method-name method-type arg-classes method)] (wrap [passes? method])))))] (case (list;filter product;left candidates) #;Nil (&;throw No-Candidate-Method (format class-name "#" method-name)) (#;Cons candidate #;Nil) (|> candidate product;right (method-to-type method-type)) _ (&;throw Too-Many-Candidate-Methods (format class-name "#" method-name))))) (def: (constructor-to-type constructor) (-> (Constructor Object) (Meta [Type (List Type)])) (let [owner (Constructor.getDeclaringClass [] constructor) owner-name (Class.getName [] owner) owner-tvars (|> (Class.getTypeParameters [] owner) array;to-list (list/map (TypeVariable.getName []))) constructor-tvars (|> (Constructor.getTypeParameters [] constructor) array;to-list (list/map (TypeVariable.getName []))) num-owner-tvars (list;size owner-tvars) all-tvars (list/compose owner-tvars constructor-tvars) num-all-tvars (list;size all-tvars) owner-tvarsT (type-vars num-owner-tvars +0) constructor-tvarsT (type-vars num-all-tvars num-owner-tvars) mappings (: Mappings (if (list;empty? all-tvars) fresh-mappings (|> (list/compose owner-tvarsT constructor-tvarsT) list;reverse (list;zip2 all-tvars) (dict;from-list text;Hash))))] (do meta;Monad [inputsT (|> (Constructor.getGenericParameterTypes [] constructor) array;to-list (monad;map @ (java-type-to-lux-type mappings))) exceptionsT (|> (Constructor.getGenericExceptionTypes [] constructor) array;to-list (monad;map @ (java-type-to-lux-type mappings))) #let [objectT (#;Primitive owner-name (list;reverse owner-tvarsT)) constructorT (<| (type;univ-q num-all-tvars) (type;function inputsT) objectT)]] (wrap [constructorT exceptionsT])))) (exception: #export No-Candidate-Constructor) (exception: #export Too-Many-Candidate-Constructors) (def: (constructor-methods class-name arg-classes) (-> Text (List Text) (Meta [Type (List Type)])) (do meta;Monad [class (load-class class-name) candidates (|> class (Class.getConstructors []) array;to-list (monad;map @ (function [constructor] (do @ [passes? (check-constructor class arg-classes constructor)] (wrap [passes? constructor])))))] (case (list;filter product;left candidates) #;Nil (&;throw No-Candidate-Constructor (format class-name "(" (text;join-with ", " arg-classes) ")")) (#;Cons candidate #;Nil) (|> candidate product;right constructor-to-type) _ (&;throw Too-Many-Candidate-Constructors class-name)))) (def: (decorate-inputs typesT inputsA) (-> (List Text) (List la;Analysis) (List la;Analysis)) (|> inputsA (list;zip2 (list/map (|>. #la;Text) typesT)) (list/map (|>. #la;Product)))) (def: (sub-type-analyser analyse) (-> &;Analyser &;Analyser) (function [argC] (do meta;Monad [[argT argA] (&common;with-unknown-type (analyse argC)) expectedT meta;expected-type [unboxed castT] (cast #In expectedT argT)] (wrap argA)))) (def: (invoke//static proc) (-> Text @;Proc) (function [analyse args] (case (: (e;Error [Text Text (List [Text Code])]) (s;run args ($_ p;seq s;text s;text (p;some (s;tuple (p;seq s;text s;any)))))) (#e;Success [class method argsTC]) (do meta;Monad [#let [argsT (list/map product;left argsTC)] [methodT exceptionsT] (methods class method #Static argsT) [outputT argsA] (&inference;apply-function (sub-type-analyser analyse) methodT (list/map product;right argsTC)) [unboxed castT] (infer-out outputT)] (wrap (#la;Procedure proc (list& (#la;Text class) (#la;Text method) (#la;Text unboxed) (decorate-inputs argsT argsA))))) _ (&;fail (format "Wrong syntax for '" proc "'."))))) (def: (invoke//virtual proc) (-> Text @;Proc) (function [analyse args] (case (: (e;Error [Text Text Code (List [Text Code])]) (s;run args ($_ p;seq s;text s;text s;any (p;some (s;tuple (p;seq s;text s;any)))))) (#e;Success [class method objectC argsTC]) (do meta;Monad [#let [argsT (list/map product;left argsTC)] [methodT exceptionsT] (methods class method #Virtual argsT) [outputT allA] (&inference;apply-function (sub-type-analyser analyse) methodT (list& objectC (list/map product;right argsTC))) #let [[objectA argsA] (case allA (#;Cons objectA argsA) [objectA argsA] _ (undefined))] [unboxed castT] (infer-out outputT)] (wrap (#la;Procedure proc (list& (#la;Text class) (#la;Text method) (#la;Text unboxed) objectA (decorate-inputs argsT argsA))))) _ (&;fail (format "Wrong syntax for '" proc "'."))))) (def: (invoke//special proc) (-> Text @;Proc) (function [analyse args] (case (: (e;Error [(List Code) [Text Text Code (List [Text Code]) Unit]]) (p;run args ($_ p;seq s;text s;text s;any (p;some (s;tuple (p;seq s;text s;any))) s;end!))) (#e;Success [_ [class method objectC argsTC _]]) (do meta;Monad [#let [argsT (list/map product;left argsTC)] [methodT exceptionsT] (methods class method #Special argsT) [outputT argsA] (&inference;apply-function (sub-type-analyser analyse) methodT (list& objectC (list/map product;right argsTC))) [unboxed castT] (infer-out outputT)] (wrap (#la;Procedure proc (list& (#la;Text class) (#la;Text method) (#la;Text unboxed) (decorate-inputs argsT argsA))))) _ (&;fail (format "Wrong syntax for '" proc "'."))))) (exception: #export Not-Interface) (def: (invoke//interface proc) (-> Text @;Proc) (function [analyse args] (case (: (e;Error [Text Text Code (List [Text Code])]) (s;run args ($_ p;seq s;text s;text s;any (p;some (s;tuple (p;seq s;text s;any)))))) (#e;Success [class-name method objectC argsTC]) (do meta;Monad [#let [argsT (list/map product;left argsTC)] class (load-class class-name) _ (&;assert (Not-Interface class-name) (Modifier.isInterface [(Class.getModifiers [] class)])) [methodT exceptionsT] (methods class-name method #Interface argsT) [outputT argsA] (&inference;apply-function (sub-type-analyser analyse) methodT (list& objectC (list/map product;right argsTC))) [unboxed castT] (infer-out outputT)] (wrap (#la;Procedure proc (list& (#la;Text class-name) (#la;Text method) (#la;Text unboxed) (decorate-inputs argsT argsA))))) _ (&;fail (format "Wrong syntax for '" proc "'."))))) (def: (invoke//constructor proc) (-> Text @;Proc) (function [analyse args] (case (: (e;Error [Text (List [Text Code])]) (s;run args ($_ p;seq s;text (p;some (s;tuple (p;seq s;text s;any)))))) (#e;Success [class argsTC]) (do meta;Monad [#let [argsT (list/map product;left argsTC)] [methodT exceptionsT] (constructor-methods class argsT) [outputT argsA] (&inference;apply-function (sub-type-analyser analyse) methodT (list/map product;right argsTC)) [unboxed castT] (infer-out outputT)] (wrap (#la;Procedure proc (list& (#la;Text class) (decorate-inputs argsT argsA))))) _ (&;fail (format "Wrong syntax for '" proc "'."))))) (def: member-procs @;Bundle (<| (@;prefix "member") (|> (dict;new text;Hash) (dict;merge (<| (@;prefix "static") (|> (dict;new text;Hash) (@;install "get" static-get) (@;install "put" static-put)))) (dict;merge (<| (@;prefix "virtual") (|> (dict;new text;Hash) (@;install "get" virtual-get) (@;install "put" virtual-put)))) (dict;merge (<| (@;prefix "invoke") (|> (dict;new text;Hash) (@;install "static" invoke//static) (@;install "virtual" invoke//virtual) (@;install "special" invoke//special) (@;install "interface" invoke//interface) (@;install "constructor" invoke//constructor) ))) ))) (def: #export procedures @;Bundle (<| (@;prefix "jvm") (|> (dict;new text;Hash) (dict;merge conversion-procs) (dict;merge int-procs) (dict;merge long-procs) (dict;merge float-procs) (dict;merge double-procs) (dict;merge char-procs) (dict;merge array-procs) (dict;merge object-procs) (dict;merge member-procs) )))