diff options
author | Eduardo Julian | 2017-10-31 23:39:49 -0400 |
---|---|---|
committer | Eduardo Julian | 2017-10-31 23:39:49 -0400 |
commit | 15121222d570f8fe3c5a326208e4f0bad737e63c (patch) | |
tree | 88c93ed1f4965fd0e80677df5553a0d47e521963 /new-luxc/source/luxc/lang/analysis/procedure/host.jvm.lux | |
parent | a269ea72337852e8e57bd427773baed111ad6e92 (diff) |
- Re-organized analysis.
Diffstat (limited to 'new-luxc/source/luxc/lang/analysis/procedure/host.jvm.lux')
-rw-r--r-- | new-luxc/source/luxc/lang/analysis/procedure/host.jvm.lux | 1241 |
1 files changed, 1241 insertions, 0 deletions
diff --git a/new-luxc/source/luxc/lang/analysis/procedure/host.jvm.lux b/new-luxc/source/luxc/lang/analysis/procedure/host.jvm.lux new file mode 100644 index 000000000..3ba7713ac --- /dev/null +++ b/new-luxc/source/luxc/lang/analysis/procedure/host.jvm.lux @@ -0,0 +1,1241 @@ +(;module: + [lux #- char] + (lux (control [monad #+ do] + ["p" parser] + ["ex" exception #+ exception:]) + (concurrency ["A" atom]) + (data ["e" error] + [maybe] + [product] + [bool "bool/" Eq<Bool>] + [text "text/" Eq<Text>] + (text format + ["l" lexer]) + (coll [list "list/" Fold<List> Functor<List> Monoid<List>] + [array] + [dict #+ Dict])) + [meta "meta/" Monad<Meta>] + (meta [code] + ["s" syntax] + [type] + (type ["tc" check])) + [host]) + (luxc ["&" base] + ["&;" host] + (lang ["la" analysis] + (analysis ["&;" common] + ["&;" inference]))) + ["@" ../common] + ) + +(def: #export null-class Text "#Null") + +(do-template [<name> <class>] + [(def: #export <name> Type (#;Primitive <class> (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<Text>) + (@;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 [<name> <prefix> <type>] + [(def: <name> + @;Bundle + (<| (@;prefix <prefix>) + (|> (dict;new text;Hash<Text>) + (@;install "+" (@;binary <type> <type> <type>)) + (@;install "-" (@;binary <type> <type> <type>)) + (@;install "*" (@;binary <type> <type> <type>)) + (@;install "/" (@;binary <type> <type> <type>)) + (@;install "%" (@;binary <type> <type> <type>)) + (@;install "=" (@;binary <type> <type> Boolean)) + (@;install "<" (@;binary <type> <type> Boolean)) + (@;install "and" (@;binary <type> <type> <type>)) + (@;install "or" (@;binary <type> <type> <type>)) + (@;install "xor" (@;binary <type> <type> <type>)) + (@;install "shl" (@;binary <type> Integer <type>)) + (@;install "shr" (@;binary <type> Integer <type>)) + (@;install "ushr" (@;binary <type> Integer <type>)) + )))] + + [int-procs "int" Integer] + [long-procs "long" Long] + ) + +(do-template [<name> <prefix> <type>] + [(def: <name> + @;Bundle + (<| (@;prefix <prefix>) + (|> (dict;new text;Hash<Text>) + (@;install "+" (@;binary <type> <type> <type>)) + (@;install "-" (@;binary <type> <type> <type>)) + (@;install "*" (@;binary <type> <type> <type>)) + (@;install "/" (@;binary <type> <type> <type>)) + (@;install "%" (@;binary <type> <type> <type>)) + (@;install "=" (@;binary <type> <type> Boolean)) + (@;install "<" (@;binary <type> <type> Boolean)) + )))] + + [float-procs "float" Float] + [double-procs "double" Double] + ) + +(def: char-procs + @;Bundle + (<| (@;prefix "char") + (|> (dict;new text;Hash<Text>) + (@;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<Text>))) + +(def: (array-length proc) + (-> Text @;Proc) + (function [analyse eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list arrayC)) + (do meta;Monad<Meta> + [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 eval args] + (case args + (^ (list lengthC)) + (do meta;Monad<Meta> + [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 (code;nat (n.dec level)) (code;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 [<tag>] + (<tag> 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<Meta> + [name (check-jvm objectT)] + (if (dict;contains? name boxes) + (&;fail (format "Primitives are not objects: " name)) + (:: meta;Monad<Meta> wrap name)))) + +(def: (box-array-element-type elemT) + (-> Type (Meta [Type Text])) + (do meta;Monad<Meta> + [] + (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<Meta> wrap [elemT name])) + + _ + (&;fail (format "Invalid type for array element: " (%type elemT)))))) + +(def: (array-read proc) + (-> Text @;Proc) + (function [analyse eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list arrayC idxC)) + (do meta;Monad<Meta> + [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 (code;text elem-class) idxA arrayA)))) + + _ + (&;fail (@;wrong-arity proc +2 (list;size args)))))))) + +(def: (array-write proc) + (-> Text @;Proc) + (function [analyse eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list arrayC idxC valueC)) + (do meta;Monad<Meta> + [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 (code;text elem-class) idxA valueA arrayA)))) + + _ + (&;fail (@;wrong-arity proc +3 (list;size args)))))))) + +(def: array-procs + @;Bundle + (<| (@;prefix "array") + (|> (dict;new text;Hash<Text>) + (@;install "length" array-length) + (@;install "new" array-new) + (@;install "read" array-read) + (@;install "write" array-write) + ))) + +(def: (object-null proc) + (-> Text @;Proc) + (function [analyse eval args] + (case args + (^ (list)) + (do meta;Monad<Meta> + [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 eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list objectC)) + (do meta;Monad<Meta> + [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 eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list monitorC exprC)) + (do meta;Monad<Meta> + [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<Meta> + [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<Meta> + [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 eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list exceptionC)) + (do meta;Monad<Meta> + [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 eval args] + (case args + (^ (list classC)) + (case classC + [_ (#;Text class)] + (do meta;Monad<Meta> + [_ (load-class class) + _ (&;infer (#;Primitive "java.lang.Class" (list (#;Primitive class (list)))))] + (wrap (la;procedure proc (list (code;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 eval args] + (&common;with-var + (function [[var-id varT]] + (case args + (^ (list classC objectC)) + (case classC + [_ (#;Text class)] + (do meta;Monad<Meta> + [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 (code;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<Text>) + (@;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<Text>)) + +(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<Meta> + [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<Meta> + [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<Text>))) + (&;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<Meta> + [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<Meta> + [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<Meta> + [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<Meta> + [[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<Meta> + [[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<Text>)))) + + _ + (&;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<Meta> + [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<Meta> + [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 eval args] + (case args + (^ (list classC fieldC)) + (case [classC fieldC] + [[_ (#;Text class)] [_ (#;Text field)]] + (do meta;Monad<Meta> + [[fieldT final?] (static-field class field) + [unboxed castT] (infer-out fieldT)] + (wrap (la;procedure proc (list (code;text class) (code;text field) + (code;text unboxed))))) + + _ + (&;fail (format "Wrong syntax for '" proc "'."))) + + _ + (&;fail (@;wrong-arity proc +2 (list;size args)))))) + +(def: (static-put proc) + (-> Text @;Proc) + (function [analyse eval args] + (case args + (^ (list classC fieldC valueC)) + (case [classC fieldC] + [[_ (#;Text class)] [_ (#;Text field)]] + (do meta;Monad<Meta> + [[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 (code;text class) (code;text field) + (code;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 eval args] + (case args + (^ (list classC fieldC objectC)) + (case [classC fieldC] + [[_ (#;Text class)] [_ (#;Text field)]] + (do meta;Monad<Meta> + [[objectT objectA] (analyse-object class analyse objectC) + [fieldT final?] (virtual-field class field objectT) + [unboxed castT] (infer-out fieldT)] + (wrap (la;procedure proc (list (code;text class) (code;text field) + (code;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 eval args] + (case args + (^ (list classC fieldC valueC objectC)) + (case [classC fieldC] + [[_ (#;Text class)] [_ (#;Text field)]] + (do meta;Monad<Meta> + [[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 objectT)] + (wrap (la;procedure proc (list (code;text class) (code;text field) (code;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<Meta> + [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<Meta> + [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<Meta> + [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<Text>))))] + (do meta;Monad<Meta> + [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<Meta> + [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<Text>))))] + (do meta;Monad<Meta> + [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<Meta> + [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 code;text typesT)) + (list/map (function [[type value]] + (la;product (list type value)))))) + +(def: (sub-type-analyser analyse) + (-> &;Analyser &;Analyser) + (function [argC] + (do meta;Monad<Meta> + [[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 eval 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<Meta> + [#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& (code;text class) (code;text method) + (code;text unboxed) (decorate-inputs argsT argsA))))) + + _ + (&;fail (format "Wrong syntax for '" proc "'."))))) + +(def: (invoke//virtual proc) + (-> Text @;Proc) + (function [analyse eval 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<Meta> + [#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& (code;text class) (code;text method) + (code;text unboxed) objectA (decorate-inputs argsT argsA))))) + + _ + (&;fail (format "Wrong syntax for '" proc "'."))))) + +(def: (invoke//special proc) + (-> Text @;Proc) + (function [analyse eval 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<Meta> + [#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& (code;text class) (code;text method) + (code;text unboxed) (decorate-inputs argsT argsA))))) + + _ + (&;fail (format "Wrong syntax for '" proc "'."))))) + +(exception: #export Not-Interface) + +(def: (invoke//interface proc) + (-> Text @;Proc) + (function [analyse eval 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<Meta> + [#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& (code;text class-name) (code;text method) (code;text unboxed) + (decorate-inputs argsT argsA))))) + + _ + (&;fail (format "Wrong syntax for '" proc "'."))))) + +(def: (invoke//constructor proc) + (-> Text @;Proc) + (function [analyse eval 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<Meta> + [#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& (code;text class) (decorate-inputs argsT argsA))))) + + _ + (&;fail (format "Wrong syntax for '" proc "'."))))) + +(def: member-procs + @;Bundle + (<| (@;prefix "member") + (|> (dict;new text;Hash<Text>) + (dict;merge (<| (@;prefix "static") + (|> (dict;new text;Hash<Text>) + (@;install "get" static-get) + (@;install "put" static-put)))) + (dict;merge (<| (@;prefix "virtual") + (|> (dict;new text;Hash<Text>) + (@;install "get" virtual-get) + (@;install "put" virtual-put)))) + (dict;merge (<| (@;prefix "invoke") + (|> (dict;new text;Hash<Text>) + (@;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<Text>) + (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) + ))) |