From a2a0436347596dcf7df537dd47dd27bc31fcba98 Mon Sep 17 00:00:00 2001 From: Jonas Maebe Date: Tue, 23 Aug 2011 17:45:01 +0000 Subject: [PATCH] + support for threadvars in the JVM based on JLThreadLocal; see rtl/java/jtvarh.inc for the details git-svn-id: branches/jvmbackend@18820 - --- .gitattributes | 3 + compiler/agjasmin.pas | 4 +- compiler/jvm/hlcgcpu.pas | 7 +- compiler/jvm/jvmdef.pas | 102 ++++++++++ compiler/jvm/njvmld.pas | 19 ++ compiler/jvm/njvmutil.pas | 107 +++++++++++ compiler/nld.pas | 10 + rtl/java/java_sys.inc | 35 ++++ rtl/java/java_sysh.inc | 7 +- rtl/java/jdk15.inc | 49 +---- rtl/java/jdk15.pas | 30 +-- rtl/java/jdynarrh.inc | 2 + rtl/java/jtvar.inc | 239 +++++++++++++++++++++++ rtl/java/jtvarh.inc | 166 ++++++++++++++++ rtl/java/system.pp | 2 + tests/test/jvm/testall.bat | 4 + tests/test/jvm/testall.sh | 2 + tests/test/jvm/tthreadvar.pp | 360 +++++++++++++++++++++++++++++++++++ 18 files changed, 1087 insertions(+), 61 deletions(-) create mode 100644 rtl/java/jtvar.inc create mode 100644 rtl/java/jtvarh.inc create mode 100644 tests/test/jvm/tthreadvar.pp diff --git a/.gitattributes b/.gitattributes index d47dd31768..c4463ddfe5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7373,6 +7373,8 @@ rtl/java/jseth.inc svneol=native#text/plain rtl/java/jsystem.inc svneol=native#text/plain rtl/java/jsystemh.inc svneol=native#text/plain rtl/java/jsystemh_types.inc svneol=native#text/plain +rtl/java/jtvar.inc svneol=native#text/plain +rtl/java/jtvarh.inc svneol=native#text/plain rtl/java/objpas.inc svneol=native#text/plain rtl/java/objpas.pp svneol=native#text/plain rtl/java/objpash.inc svneol=native#text/plain @@ -9801,6 +9803,7 @@ tests/test/jvm/trange2.pp svneol=native#text/plain tests/test/jvm/trange3.pp svneol=native#text/plain tests/test/jvm/tset1.pp svneol=native#text/plain tests/test/jvm/tset3.pp svneol=native#text/plain +tests/test/jvm/tthreadvar.pp svneol=native#text/plain tests/test/jvm/ttrig.pp svneol=native#text/plain tests/test/jvm/ttrunc.pp svneol=native#text/plain tests/test/jvm/tvarpara.pp svneol=native#text/plain diff --git a/compiler/agjasmin.pas b/compiler/agjasmin.pas index 13388dc0a5..df5ed879bf 100644 --- a/compiler/agjasmin.pas +++ b/compiler/agjasmin.pas @@ -922,8 +922,8 @@ implementation if (sym.owner.symtabletype in [recordsymtable,ObjectSymtable]) and (sym.typ=staticvarsym) then exit; - { external definition -> no definition here } - if vo_is_external in sym.varoptions then + { external or threadvar definition -> no definition here } + if ([vo_is_external,vo_is_thread_var]*sym.varoptions)<>[] then exit; AsmWrite('.field '); AsmWriteln(FieldDefinition(sym)); diff --git a/compiler/jvm/hlcgcpu.pas b/compiler/jvm/hlcgcpu.pas index 3f2f6b21f8..fa2250d8c4 100644 --- a/compiler/jvm/hlcgcpu.pas +++ b/compiler/jvm/hlcgcpu.pas @@ -2025,7 +2025,12 @@ implementation intialising the constant } if [vo_is_external,vo_has_local_copy]*vs.varoptions=[vo_is_external] then continue; - if jvmimplicitpointertype(vs.vardef) then + { threadvar innitializations are handled at the node tree level } + if vo_is_thread_var in vs.varoptions then + begin + { nothing } + end + else if jvmimplicitpointertype(vs.vardef) then allocate_implicit_struct_with_base_ref(list,vs,ref) { enums are class instances in Java, while they are ordinals in Pascal. When they are initialized with enum(0), such as in diff --git a/compiler/jvm/jvmdef.pas b/compiler/jvm/jvmdef.pas index 754ce6ed39..b7d53fb5e8 100644 --- a/compiler/jvm/jvmdef.pas +++ b/compiler/jvm/jvmdef.pas @@ -81,6 +81,14 @@ interface function jvmgetcorrespondingclassdef(def: tdef): tdef; + { threadvars are wrapped via descendents of java.lang.ThreadLocal } + function jvmgetthreadvardef(def: tdef): tdef; + + { gets the number of dimensions and the final element type of a normal + array } + procedure jvmgetarraydimdef(arrdef: tdef; out eledef: tdef; out ndim: longint); + + implementation uses @@ -737,6 +745,100 @@ implementation end; + function jvmgetthreadvardef(def: tdef): tdef; + begin + if (def.typ=arraydef) and + not is_dynamic_array(def) then + begin + result:=search_system_type('FPCNORMALARRAYTHREADVAR').typedef; + exit; + end; + if jvmimplicitpointertype(def) then + begin + result:=search_system_type('FPCIMPLICITPTRTHREADVAR').typedef; + exit; + end; + case def.typ of + orddef: + begin + case torddef(def).ordtype of + pasbool8: + begin + result:=tobjectdef(search_system_type('FPCBOOLEANTHREADVAR').typedef); + end; + uwidechar: + begin + result:=tobjectdef(search_system_type('FPCCHARTHREADVAR').typedef); + end; + s8bit, + u8bit, + uchar, + bool8bit: + begin + result:=tobjectdef(search_system_type('FPCBYTETHREADVAR').typedef); + end; + s16bit, + u16bit, + bool16bit, + pasbool16: + begin + result:=tobjectdef(search_system_type('FPCSHORTTHREADVAR').typedef); + end; + s32bit, + u32bit, + bool32bit, + pasbool32: + begin + result:=tobjectdef(search_system_type('FPCINTTHREADVAR').typedef); + end; + s64bit, + u64bit, + scurrency, + bool64bit, + pasbool64: + begin + result:=tobjectdef(search_system_type('FPCLONGTHREADVAR').typedef); + end + else + internalerror(2011082101); + end; + end; + floatdef: + begin + case tfloatdef(def).floattype of + s32real: + begin + result:=tobjectdef(search_system_type('FPCFLOATTHREADVAR').typedef); + end; + s64real: + begin + result:=tobjectdef(search_system_type('FPCDOUBLETHREADVAR').typedef); + end; + else + internalerror(2011082102); + end; + end + else + begin + result:=search_system_type('FPCPOINTERTHREADVAR').typedef + end; + end; + end; + + + procedure jvmgetarraydimdef(arrdef: tdef; out eledef: tdef; out ndim: longint); + begin + eledef:=arrdef; + ndim:=0; + repeat + eledef:=tarraydef(eledef).elementdef; + inc(ndim); + until (eledef.typ<>arraydef) or + is_dynamic_array(eledef); + end; + + + function jvmmangledbasename(sym: tsym; const usesymname: TSymStr; withsignature: boolean): TSymStr; var container: tsymtable; diff --git a/compiler/jvm/njvmld.pas b/compiler/jvm/njvmld.pas index 9aa84d06f5..7c936ade28 100644 --- a/compiler/jvm/njvmld.pas +++ b/compiler/jvm/njvmld.pas @@ -35,6 +35,7 @@ type tjvmloadnode = class(tcgnestloadnode) protected function is_copyout_addr_param_load: boolean; + function handle_threadvar_access: tnode; override; public function is_addr_param_load: boolean; override; procedure pass_generate_code; override; @@ -159,6 +160,24 @@ function tjvmloadnode.is_copyout_addr_param_load: boolean; end; +function tjvmloadnode.handle_threadvar_access: tnode; + var + vs: tsym; + begin + { get the variable wrapping the threadvar } + vs:=tsym(symtable.find(symtableentry.name+'$THREADVAR')); + if not assigned(vs) or + (vs.typ<>staticvarsym) then + internalerror(2011082201); + { get a read/write reference to the threadvar value } + result:=cloadnode.create(vs,vs.owner); + typecheckpass(result); + result:=ccallnode.createinternmethod(result,'GETREADWRITEREFERENCE',nil); + result:=ctypeconvnode.create_explicit(result,getpointerdef(resultdef)); + result:=cderefnode.create(result); + end; + + function tjvmloadnode.is_addr_param_load: boolean; begin result:= diff --git a/compiler/jvm/njvmutil.pas b/compiler/jvm/njvmutil.pas index 0952eacf62..9c1cf89ace 100644 --- a/compiler/jvm/njvmutil.pas +++ b/compiler/jvm/njvmutil.pas @@ -171,6 +171,16 @@ implementation end; class procedure tjvmnodeutils.insertbssdata(sym: tstaticvarsym); + var + enuminitsym, + vs: tstaticvarsym; + block: tblocknode; + stat: tstatementnode; + temp: ttempcreatenode; + initnode: tnode; + eledef: tdef; + ndim: longint; + initnodefinished: boolean; begin { handled while generating the unit/program init code, or class constructor; add something to al_globals to indicate that we need to @@ -178,6 +188,103 @@ implementation if current_asmdata.asmlists[al_globals].empty and jvmimplicitpointertype(sym.vardef) then current_asmdata.asmlists[al_globals].concat(cai_align.Create(1)); + { in case of a threadvar, allocate a separate sym that's a subtype of the + java.lang.ThreadLocal class which will wrap the actual variable value } + if vo_is_thread_var in sym.varoptions then + begin + vs:=tstaticvarsym.create(sym.realname+'$threadvar',sym.varspez, + jvmgetthreadvardef(sym.vardef), + sym.varoptions - [vo_is_thread_var]); + sym.owner.insert(vs); + { make sure that the new sym does not get allocated (we will allocate + it when encountering the original sym, because only then we know + that it's a threadvar) } + include(vs.symoptions,sp_static); + { switch around the mangled names of sym and vs, since the wrapper + should map to the declared name } + sym.set_mangledbasename(vs.realname); + vs.set_mangledbasename(sym.realname); + + { add initialization code for the wrapper } + block:=internalstatements(stat); + if assigned(current_module.tcinitcode) then + addstatement(stat,tnode(current_module.tcinitcode)); + current_module.tcinitcode:=block; + + { create initialization value if necessary } + initnode:=nil; + initnodefinished:=false; + temp:=nil; + { in case of enum type, initialize with enum(0) if it exists } + if sym.vardef.typ=enumdef then + begin + enuminitsym:=tstaticvarsym(tenumdef(sym.vardef).getbasedef.classdef.symtable.Find('__FPC_ZERO_INITIALIZER')); + if assigned(enuminitsym) then + initnode:=cloadnode.create(enuminitsym,enuminitsym.owner); + end + { normal array -> include dimensions and element type so we can + create a deep copy } + else if (sym.vardef.typ=arraydef) and + not is_dynamic_array(sym.vardef) then + begin + temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true); + addstatement(stat,temp); + initnode:=ccallparanode.create( + ctypeconvnode.create_explicit( + caddrnode.create_internal(ctemprefnode.create(temp)), + java_jlobject), + nil); + jvmgetarraydimdef(sym.vardef,eledef,ndim); + initnode:=ccallparanode.create(genintconstnode(ndim),initnode); + initnode:=ccallparanode.create( + cordconstnode.create(ord(jvmarrtype_setlength(eledef)), + cwidechartype,false), + initnode); + initnodefinished:=true; + end + { implicitpointertype -> allocate (get temp and assign address) } + else if jvmimplicitpointertype(sym.vardef) then + begin + temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true); + addstatement(stat,temp); + initnode:=caddrnode.create_internal(ctemprefnode.create(temp)); + end + { unicodestring/ansistring -> empty string } + else if is_wide_or_unicode_string(sym.vardef) or + is_ansistring(sym.vardef) then + begin + temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true); + addstatement(stat,temp); + addstatement(stat,cassignmentnode.create( + ctemprefnode.create(temp), + cstringconstnode.createstr(''))); + initnode:=ctemprefnode.create(temp); + end + { dynamic array -> empty array } + else if is_dynamic_array(sym.vardef) then + begin + temp:=ctempcreatenode.create(sym.vardef,sym.vardef.size,tt_persistent,true); + addstatement(stat,temp); + addstatement(stat,cinlinenode.create(in_setlength_x,false, + ccallparanode.create(genintconstnode(0), + ccallparanode.create(ctemprefnode.create(temp),nil)) + ) + ); + initnode:=ctemprefnode.create(temp); + end; + + if assigned(initnode) and + not initnodefinished then + initnode:=ccallparanode.create(ctypeconvnode.create_explicit(initnode,java_jlobject),nil); + addstatement(stat,cassignmentnode.create( + cloadnode.create(vs,vs.owner), + ccallnode.createinternmethod( + cloadvmtaddrnode.create(ctypenode.create(vs.vardef)), + 'CREATE',initnode))); + { deallocate the temp if we allocated one } + if assigned(temp) then + addstatement(stat,ctempdeletenode.create(temp)); + end; end; diff --git a/compiler/nld.pas b/compiler/nld.pas index 4aa34e3d0c..e3109aaf59 100644 --- a/compiler/nld.pas +++ b/compiler/nld.pas @@ -39,6 +39,7 @@ interface protected fprocdef : tprocdef; fprocdefderef : tderef; + function handle_threadvar_access: tnode; virtual; public symtableentry : tsym; symtableentryderef : tderef; @@ -167,6 +168,13 @@ implementation TLOADNODE *****************************************************************************} + function tloadnode.handle_threadvar_access: tnode; + begin + { nothing special by default } + result:=nil; + end; + + constructor tloadnode.create(v : tsym;st : TSymtable); begin inherited create(loadn,nil); @@ -283,6 +291,8 @@ implementation ) then make_not_regable(self,[ra_addr_taken]); resultdef:=tabstractvarsym(symtableentry).vardef; + if vo_is_thread_var in tstaticvarsym(symtableentry).varoptions then + result:=handle_threadvar_access; end; paravarsym, localvarsym : diff --git a/rtl/java/java_sys.inc b/rtl/java/java_sys.inc index 480d6514ff..016bf2f127 100644 --- a/rtl/java/java_sys.inc +++ b/rtl/java/java_sys.inc @@ -174,6 +174,41 @@ class function mapLibraryName(para1: JLString): JLString; static; overload; end; + JLThreadLocal = class external 'java.lang' name 'ThreadLocal' (JLObject) + public + type + InnerThreadLocalMap = class; + Arr1InnerThreadLocalMap = array of InnerThreadLocalMap; + Arr2InnerThreadLocalMap = array of Arr1InnerThreadLocalMap; + Arr3InnerThreadLocalMap = array of Arr2InnerThreadLocalMap; + InnerEntry = class; + Arr1InnerEntry = array of InnerEntry; + Arr2InnerEntry = array of Arr1InnerEntry; + Arr3InnerEntry = array of Arr2InnerEntry; + InnerThreadLocalMap = class external 'java.lang' name 'ThreadLocalMap' + public + type + InnerEntry = class; + Arr1InnerEntry = array of InnerEntry; + Arr2InnerEntry = array of Arr1InnerEntry; + Arr3InnerEntry = array of Arr2InnerEntry; + InnerEntry = class external 'java.lang' name 'Entry' + end; + + end; + + InnerEntry = class external 'java.lang' name 'Entry' + end; + + strict protected + function initialValue(): JLObject; overload; virtual; + public + constructor create(); overload; + function get(): JLObject; overload; virtual; + procedure &set(para1: JLObject); overload; virtual; + procedure remove(); overload; virtual; + end; + JLRAnnotatedElement = interface external 'java.lang.reflect' name 'AnnotatedElement' function isAnnotationPresent(para1: JLClass): jboolean; overload; function getAnnotation(para1: JLClass): JLAAnnotation; overload; diff --git a/rtl/java/java_sysh.inc b/rtl/java/java_sysh.inc index 69d55d0255..72a5daa93a 100644 --- a/rtl/java/java_sysh.inc +++ b/rtl/java/java_sysh.inc @@ -1,4 +1,4 @@ -{ Imports for Java packages/classes: java.io.Serializable, java.lang.AbstractStringBuilder, java.lang.Appendable, java.lang.Boolean, java.lang.Byte, java.lang.CharSequence, java.lang.Character, java.lang.Class, java.lang.Cloneable, java.lang.Comparable, java.lang.Double, java.lang.Enum, java.lang.Error, java.lang.Exception, java.lang.Float, java.lang.IllegalArgumentException, java.lang.IndexOutOfBoundsException, java.lang.Integer, java.lang.Iterable, java.lang.LinkageError, java.lang.Long, java.lang.Math, java.lang.NoSuchMethodException, java.lang.Number, java.lang.Object, java.lang.Runtime, java.lang.RuntimeException, java.lang.Short, java.lang.String, java.lang.StringBuffer, java.lang.StringBuilder, java.lang.System, java.lang.Throwable, java.lang.reflect.AccessibleObject, java.lang.reflect.AnnotatedElement, java.lang.reflect.Array, java.lang.reflect.Field, java.lang.reflect.GenericDeclaration, java.lang.reflect.Member, java.lang.reflect.Method, java.lang.reflect.Type, java.math.BigInteger, java.text.Collator, java.util.AbstractCollection, java.util.AbstractMap, java.util.AbstractSet, java.util.Arrays, java.util.BitSet, java.util.Calendar, java.util.Collection, java.util.Comparator, java.util.EnumSet, java.util.HashMap, java.util.Iterator, java.util.Map, java.util.Set } +{ Imports for Java packages/classes: java.io.Serializable, java.lang.AbstractStringBuilder, java.lang.Appendable, java.lang.Boolean, java.lang.Byte, java.lang.CharSequence, java.lang.Character, java.lang.Class, java.lang.Cloneable, java.lang.Comparable, java.lang.Double, java.lang.Enum, java.lang.Error, java.lang.Exception, java.lang.Float, java.lang.IllegalArgumentException, java.lang.IndexOutOfBoundsException, java.lang.Integer, java.lang.Iterable, java.lang.LinkageError, java.lang.Long, java.lang.Math, java.lang.NoSuchMethodException, java.lang.Number, java.lang.Object, java.lang.Runtime, java.lang.RuntimeException, java.lang.Short, java.lang.String, java.lang.StringBuffer, java.lang.StringBuilder, java.lang.System, java.lang.ThreadLocal, java.lang.Throwable, java.lang.reflect.AccessibleObject, java.lang.reflect.AnnotatedElement, java.lang.reflect.Array, java.lang.reflect.Field, java.lang.reflect.GenericDeclaration, java.lang.reflect.Member, java.lang.reflect.Method, java.lang.reflect.Type, java.math.BigInteger, java.text.Collator, java.util.AbstractCollection, java.util.AbstractMap, java.util.AbstractSet, java.util.Arrays, java.util.BitSet, java.util.Calendar, java.util.Collection, java.util.Comparator, java.util.EnumSet, java.util.HashMap, java.util.Iterator, java.util.Map, java.util.Set } type JLNoSuchMethodException = class; Arr1JLNoSuchMethodException = array of JLNoSuchMethodException; @@ -120,6 +120,11 @@ type Arr2JUAbstractMap = array of Arr1JUAbstractMap; Arr3JUAbstractMap = array of Arr2JUAbstractMap; + JLThreadLocal = class; + Arr1JLThreadLocal = array of JLThreadLocal; + Arr2JLThreadLocal = array of Arr1JLThreadLocal; + Arr3JLThreadLocal = array of Arr2JLThreadLocal; + JLRArray = class; Arr1JLRArray = array of JLRArray; Arr2JLRArray = array of Arr1JLRArray; diff --git a/rtl/java/jdk15.inc b/rtl/java/jdk15.inc index 013744cf80..866af69bd0 100644 --- a/rtl/java/jdk15.inc +++ b/rtl/java/jdk15.inc @@ -2099,41 +2099,6 @@ JLTerminator = class external 'java.lang' name 'Terminator' (JLObject) end; - JLThreadLocal = class external 'java.lang' name 'ThreadLocal' (JLObject) - public - type - InnerThreadLocalMap = class; - Arr1InnerThreadLocalMap = array of InnerThreadLocalMap; - Arr2InnerThreadLocalMap = array of Arr1InnerThreadLocalMap; - Arr3InnerThreadLocalMap = array of Arr2InnerThreadLocalMap; - InnerEntry = class; - Arr1InnerEntry = array of InnerEntry; - Arr2InnerEntry = array of Arr1InnerEntry; - Arr3InnerEntry = array of Arr2InnerEntry; - InnerThreadLocalMap = class external 'java.lang' name 'ThreadLocalMap' - public - type - InnerEntry = class; - Arr1InnerEntry = array of InnerEntry; - Arr2InnerEntry = array of Arr1InnerEntry; - Arr3InnerEntry = array of Arr2InnerEntry; - InnerEntry = class external 'java.lang' name 'Entry' - end; - - end; - - InnerEntry = class external 'java.lang' name 'Entry' - end; - - strict protected - function initialValue(): JLObject; overload; virtual; - public - constructor create(); overload; - function get(): JLObject; overload; virtual; - procedure &set(para1: JLObject); overload; virtual; - procedure remove(); overload; virtual; - end; - JLVoid = class sealed external 'java.lang' name 'Void' (JLObject) public final class var @@ -23790,6 +23755,13 @@ constructor create(para1: JLString); overload; end; + JLInheritableThreadLocal = class external 'java.lang' name 'InheritableThreadLocal' (JLThreadLocal) + public + constructor create(); overload; + strict protected + function childValue(para1: JLObject): JLObject; overload; virtual; + end; + JLPackage = class external 'java.lang' name 'Package' (JLObject, JLRAnnotatedElement) public function getName(): JLString; overload; virtual; @@ -28394,13 +28366,6 @@ JCSunJCE_h = class sealed external 'javax.crypto' name 'SunJCE_h' (JLSecurityManager) end; - JLInheritableThreadLocal = class external 'java.lang' name 'InheritableThreadLocal' (JLThreadLocal) - public - constructor create(); overload; - strict protected - function childValue(para1: JLObject): JLObject; overload; virtual; - end; - JBConstructorProperties = interface external 'java.beans' name 'ConstructorProperties' (JLAAnnotation) function value(): Arr1JLString; overload; end; diff --git a/rtl/java/jdk15.pas b/rtl/java/jdk15.pas index db84a6ece8..acd629d9f1 100644 --- a/rtl/java/jdk15.pas +++ b/rtl/java/jdk15.pas @@ -1080,11 +1080,6 @@ type Arr2JMBitSieve = array of Arr1JMBitSieve; Arr3JMBitSieve = array of Arr2JMBitSieve; - JLThreadLocal = class; - Arr1JLThreadLocal = array of JLThreadLocal; - Arr2JLThreadLocal = array of Arr1JLThreadLocal; - Arr3JLThreadLocal = array of Arr2JLThreadLocal; - JAFLineBreakMeasurer = class; Arr1JAFLineBreakMeasurer = array of JAFLineBreakMeasurer; Arr2JAFLineBreakMeasurer = array of Arr1JAFLineBreakMeasurer; @@ -8965,16 +8960,16 @@ type Arr2OOCNVList = array of Arr1OOCNVList; Arr3OOCNVList = array of Arr2OOCNVList; - JNNamingException = class; - Arr1JNNamingException = array of JNNamingException; - Arr2JNNamingException = array of Arr1JNNamingException; - Arr3JNNamingException = array of Arr2JNNamingException; - JUTimer = class; Arr1JUTimer = array of JUTimer; Arr2JUTimer = array of Arr1JUTimer; Arr3JUTimer = array of Arr2JUTimer; + JNNamingException = class; + Arr1JNNamingException = array of JNNamingException; + Arr2JNNamingException = array of Arr1JNNamingException; + Arr3JNNamingException = array of Arr2JNNamingException; + JSDebugGraphicsObserver = class; Arr1JSDebugGraphicsObserver = array of JSDebugGraphicsObserver; Arr2JSDebugGraphicsObserver = array of Arr1JSDebugGraphicsObserver; @@ -20705,16 +20700,16 @@ type Arr2JLLong = array of Arr1JLLong; Arr3JLLong = array of Arr2JLLong; - JLInteger = class external 'java.lang' name 'Integer'; - Arr1JLInteger = array of JLInteger; - Arr2JLInteger = array of Arr1JLInteger; - Arr3JLInteger = array of Arr2JLInteger; - JLThrowable = class external 'java.lang' name 'Throwable'; Arr1JLThrowable = array of JLThrowable; Arr2JLThrowable = array of Arr1JLThrowable; Arr3JLThrowable = array of Arr2JLThrowable; + JLInteger = class external 'java.lang' name 'Integer'; + Arr1JLInteger = array of JLInteger; + Arr2JLInteger = array of Arr1JLInteger; + Arr3JLInteger = array of Arr2JLInteger; + JLLinkageError = class external 'java.lang' name 'LinkageError'; Arr1JLLinkageError = array of JLLinkageError; Arr2JLLinkageError = array of Arr1JLLinkageError; @@ -20725,6 +20720,11 @@ type Arr2JUAbstractCollection = array of Arr1JUAbstractCollection; Arr3JUAbstractCollection = array of Arr2JUAbstractCollection; + JLThreadLocal = class external 'java.lang' name 'ThreadLocal'; + Arr1JLThreadLocal = array of JLThreadLocal; + Arr2JLThreadLocal = array of Arr1JLThreadLocal; + Arr3JLThreadLocal = array of Arr2JLThreadLocal; + JUAbstractMap = class external 'java.util' name 'AbstractMap'; Arr1JUAbstractMap = array of JUAbstractMap; Arr2JUAbstractMap = array of Arr1JUAbstractMap; diff --git a/rtl/java/jdynarrh.inc b/rtl/java/jdynarrh.inc index 84fcb19a07..d4fa4d2910 100644 --- a/rtl/java/jdynarrh.inc +++ b/rtl/java/jdynarrh.inc @@ -16,6 +16,7 @@ } type + TJBooleanArray = array of jboolean; TJByteArray = array of jbyte; TJShortArray = array of jshort; TJIntArray = array of jint; @@ -32,6 +33,7 @@ type TJStringArray = array of unicodestring; const + FPCJDynArrTypeBoolean = 'Z'; FPCJDynArrTypeJByte = 'B'; FPCJDynArrTypeJShort = 'S'; FPCJDynArrTypeJInt = 'I'; diff --git a/rtl/java/jtvar.inc b/rtl/java/jtvar.inc new file mode 100644 index 0000000000..f4fd947cc0 --- /dev/null +++ b/rtl/java/jtvar.inc @@ -0,0 +1,239 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2011 by Jonas Maebe, + member of the Free Pascal development team. + + This file implements support routines for threadvarq with FPC/JVM + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + + function FpcImplicitPtrThreadVar.initialValue: JLObject; + var + owningClass: JLClass; + begin + { look up the clone method if we haven't done this yet } + if not assigned(fCloneMethod) then + begin + owningClass:=fInstanceToClone.getClass; + repeat + try + fCloneMethod:=owningClass.getDeclaredMethod('clone',[]); + except + on JLNoSuchMethodException do + owningClass:=owningClass.getSuperClass; + end; + until assigned(fCloneMethod); + end; + { required to enable calling methods of non-public classes (e.g. a record + type defined in the implementation of a unit -- can cause security + exceptions if the security manager doesn't allow this though... } + if not fCloneMethod.isAccessible then + fCloneMethod.setAccessible(true); + { return a copy of the record/shortstring/set/... } + result:=fCloneMethod.invoke(fInstanceToClone,[]); + end; + + + constructor FpcImplicitPtrThreadVar.create(initInstanceToClone: JLObject); + begin + fInstanceToClone:=initInstanceToClone; + end; + + + function FpcImplicitPtrThreadVar.getReadWriteReference: Pointer; + begin + { return the address of the record/shortstring/set/... } + result:=Pointer(get); + end; + + + function FpcNormalArrayThreadVar.initialValue: JLObject; + begin + result:=fpc_dynarray_copy(fInstanceToClone,-1,-1,fArrDim,fArrTyp); + end; + + + constructor FpcNormalArrayThreadVar.create(initInstanceToClone: JLObject; arrdim: longint; arrtyp: widechar); + begin + inherited create(initInstanceToClone); + fArrDim:=arrdim; + fArrTyp:=arrtyp; + end; + + + function FpcBooleanThreadVar.initialValue: JLObject; + var + arr: TJBooleanArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcBooleanThreadVar.getReadWriteReference: PBoolean; + var + arr: TJBooleanArray; + begin + arr:=TJBooleanArray(get); + result:=@arr[0]; + end; + + + function FpcByteThreadVar.initialValue: JLObject; + var + arr: TJByteArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcByteThreadVar.getReadWriteReference: PShortint; + var + arr: TJByteArray; + begin + arr:=TJByteArray(get); + result:=@arr[0]; + end; + + + function FpcShortThreadVar.initialValue: JLObject; + var + arr: TJShortArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcShortThreadVar.getReadWriteReference: PSmallint; + var + arr: TJShortArray; + begin + arr:=TJShortArray(get); + result:=@arr[0]; + end; + + + function FpcIntThreadVar.initialValue: JLObject; + var + arr: TJIntArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcIntThreadVar.getReadWriteReference: PLongint; + var + arr: TJIntArray; + begin + arr:=TJIntArray(get); + result:=@arr[0]; + end; + + + function FpcLongThreadVar.initialValue: JLObject; + var + arr: TJLongArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcLongThreadVar.getReadWriteReference: PInt64; + var + arr: TJLongArray; + begin + arr:=TJLongArray(get); + result:=@arr[0]; + end; + + + function FpcCharThreadVar.initialValue: JLObject; + var + arr: TJCharArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcCharThreadVar.getReadWriteReference: PWideChar; + var + arr: TJCharArray; + begin + arr:=TJCharArray(get); + result:=@arr[0]; + end; + + + function FpcFloatThreadVar.initialValue: JLObject; + var + arr: TJFloatArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcFloatThreadVar.getReadWriteReference: PSingle; + var + arr: TJFloatArray; + begin + arr:=TJFloatArray(get); + result:=@arr[0]; + end; + + + function FpcDoubleThreadVar.initialValue: JLObject; + var + arr: TJDoubleArray; + begin + setlength(arr,1); + result:=JLObject(arr); + end; + + + function FpcDoubleThreadVar.getReadWriteReference: PDouble; + var + arr: TJDoubleArray; + begin + arr:=TJDoubleArray(get); + result:=@arr[0]; + end; + + + function FpcPointerThreadVar.initialValue: JLObject; + var + arr: TJObjectArray; + begin + setlength(arr,1); + arr[0]:=fInitVal; + result:=JLObject(arr); + end; + + + constructor FpcPointerThreadVar.create(initVal: JLObject); + begin + fInitVal:=initVal; + end; + + + function FpcPointerThreadVar.getReadWriteReference: PPointer; + var + arr: TJObjectArray; + begin + arr:=TJObjectArray(get); + result:=PPointer(@arr[0]); + end; + + diff --git a/rtl/java/jtvarh.inc b/rtl/java/jtvarh.inc new file mode 100644 index 0000000000..0d3f0ace33 --- /dev/null +++ b/rtl/java/jtvarh.inc @@ -0,0 +1,166 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2011 by Jonas Maebe, + member of the Free Pascal development team. + + This file implements support routines for threadvarq with FPC/JVM + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + + +{ In Java, threadvars are represented by descendnts of java.lang.ThreadLocal. + This class has three important methods: set, get and initialValue. + + If you call the "get" method of a JLThreadLocal instance in a thread for the + first time before calling "set", it will call the initialValue method and + return its result. After that, it will return whatever initialValue returned, + or the previous value instated by "set". A JLThreadLocal always keeps track of + a JLObject. + + We don't want to translate accesses to threadvars into calls to get/set, since + that would mean that we would + a) have to generate different code in the compiler for read and write + accesses + b) no be able to pass threadvars to var-parameters etc + + Instead, we add a method called getReadWriteReference to all of our + descendants classes that returns a pointer to the actual value of the + threadvar for this thread. This results in several cases: + a) For primitive types, we store an array of one element of that ordinal + type. + + Their initialValue is simply an array of one element (automatically + initialized to 0). + + The pointer returned is the "address" of element 0 (pointers to primitive + types are internally arrays pointing to element 0). + + b) For non-dynamic arrays, we store that array itself (all arrays are + internally Java arrays, which descend from JLObject). + + When initializing the threadvar on startup, we pass an empty copy of such + an array to the constructor and store it. Their initialValue is a deep + copy of this array, created by fpc_dynarray_copy (it accepts a number of + dimensions, because it also has to work for making a copy of dynamic + arrays whose elements are regular arrays). + + The pointer returned is simply the address of the array. + + c) For implicit pointer types other than regular arrays, we also store the + implicit pointer itself and keep an initialized empty instance around + that is passed to the constructor. + + Their initialValue is a clone of this empty instance (can't use this for + arrays, since it would make a shallow copy of the array). Because of the + way the JLCloneable interface works, we have to call the clone method via + reflection. + + The pointer returned is again simply the implicit pointer itself. + + d) For all other types, we store an array of JLObject of one element, + similar as with primitive types. + + Their initialValue is either nil, or optionally a value passed to the + constructor when creating the JLThreadLocal instance (e.g. an empty + string for unicodestring/ansistring, or the enum instance whose ordinal + value is 0) + + The pointer returned is the address of element 0 of the array. +} + +type + FpcImplicitPtrThreadVar = class(JLThreadLocal) + protected + { all implicit pointer types are clonable } + fInstanceToClone: JLObject; + { don't look up the clone method every time } + fCloneMethod: JLRMethod; + function initialValue: JLObject; override; + public + constructor create(initInstanceToClone: JLObject); + function getReadWriteReference: Pointer; + end; + + + FpcNormalArrayThreadVar = class sealed (FpcImplicitPtrThreadVar) + protected + fArrDim: longint; + fArrTyp: widechar; + function initialValue: JLObject; override; + public + constructor create(initInstanceToClone: JLObject; arrdim: longint; arrtyp: widechar); + end; + + + FpcBooleanThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PBoolean; + end; + + FpcByteThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PShortint; + end; + + FpcShortThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PSmallint; + end; + + FpcIntThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PLongint; + end; + + FpcLongThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PInt64; + end; + + FpcCharThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PWideChar; + end; + + FpcFloatThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PSingle; + end; + + FpcDoubleThreadVar = class sealed (JLThreadLocal) + protected + function initialValue: JLObject; override; + public + function getReadWriteReference: PDouble; + end; + + FpcPointerThreadVar = class sealed (JLThreadLocal) + protected + fInitVal: JLObject; + function initialValue: JLObject; override; + public + function getReadWriteReference: PPointer; + constructor create(initVal: JLObject);overload; + end; + diff --git a/rtl/java/system.pp b/rtl/java/system.pp index 8ae462c53d..1010acc58a 100644 --- a/rtl/java/system.pp +++ b/rtl/java/system.pp @@ -84,6 +84,7 @@ const {$i jseth.inc} {$i jpvarh.inc} {$i jsystemh_types.inc} +{$i jtvarh.inc} {$i sstringh.inc} {$i jdynarrh.inc} {$i astringh.inc} @@ -95,6 +96,7 @@ const implementation {*****************************************************************************} +{$i jtvar.inc} {$i jdynarr.inc} {***************************************************************************** diff --git a/tests/test/jvm/testall.bat b/tests/test/jvm/testall.bat index 998d2e1df9..7162399f90 100644 --- a/tests/test/jvm/testall.bat +++ b/tests/test/jvm/testall.bat @@ -180,3 +180,7 @@ ppcjvm -O2 -g getbit if %errorlevel% neq 0 exit /b %errorlevel% java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. getbit if %errorlevel% neq 0 exit /b %errorlevel% +ppcjvm -O2 -g tthreadvar +if %errorlevel% neq 0 exit /b %errorlevel% +java -Dfile.encoding=UTF-8 -cp ..\..\..\rtl\units\jvm-java;. tthreadvar +if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/tests/test/jvm/testall.sh b/tests/test/jvm/testall.sh index 3f5893fea2..9da7ab1fa3 100755 --- a/tests/test/jvm/testall.sh +++ b/tests/test/jvm/testall.sh @@ -100,3 +100,5 @@ $PPC -O2 -g tdefpara java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tdefpara $PPC -O2 -g getbit java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. getbit +$PPC -O2 -g tthreadvar +java -Dfile.encoding=UTF-8 -cp ../../../rtl/units/jvm-java:. tthreadvar diff --git a/tests/test/jvm/tthreadvar.pp b/tests/test/jvm/tthreadvar.pp new file mode 100644 index 0000000000..5d79620166 --- /dev/null +++ b/tests/test/jvm/tthreadvar.pp @@ -0,0 +1,360 @@ +program tthreadvar; + +{$mode delphi} +{$modeswitch unicodestrings} + +uses + jdk15; + +type + tc = class + end; + + tmythread = class(JLThread) + procedure run; override; + end; + + +type + tthreadvarkind = (fboolean,fbyte,fsmallint,fcardinal,fint64,fchar,fwidechar,fsingle,fdouble,fsetint,fsetenum,frec,fshortstring,funicodestring,farrbyte,farrset); + + tsetint = set of 30..40; + tsetenum = set of tthreadvarkind; + tarrbyte = array[4..6] of byte; + tarrset = array[1..2] of tsetint; + trec = record + a: longint; + b: array[3..4] of ansistring; + end; + + +const + cenumin: tthreadvarkind = fcardinal; + cbooleanin: boolean = true; + cbytein: byte = 35; + csmallintin: smallint = 1234; + ccardinalin: cardinal = $1234567; + cint64in: int64 = $deadcafebabe; + ccharin: ansichar = 'S'; + cwidecharin: widechar = 'U'; + csinglein: single = 1234.5; + cdoublein: double = 1239643.75; + csetintin: tsetint = [36..39]; + csetenumin: tsetenum = [fsmallint,fint64,funicodestring]; + crecin: trec = (a:98765; b:('abc','def')); + cshortstringin: shortstring = 'greaT'; + cunicodestringin: unicodestring = 'a bit longer!'; + carrbytein: tarrbyte = (4,2,5); + carrsetin: tarrset = ([31,33,37],[]); + + cenumout: tthreadvarkind = farrbyte; + cbooleanout: boolean = false; + cbyteout: byte = 128; + csmallintout: smallint = 4321; + ccardinalout: cardinal = $7654321; + cint64out: int64 = $B4B3154713; + ccharout: ansichar = 's'; + cwidecharout: widechar = 'u'; + csingleout: single = 4321.5; + cdoubleout: double = 9876543.75; + csetintout: tsetint = [31..36]; + csetenumout: tsetenum = [fbyte]; + crecout: trec = (a:4365246; b:('cbax','iiiiii')); + cshortstringout: shortstring = 'tiny'; + cunicodestringout: unicodestring = 'yet another bit longer!'; + carrbyteout: tarrbyte = (6,6,6); + carrsetout: tarrset = ([30,31],[33..38]); + + threadvar + venum: tthreadvarkind; + vboolean: boolean; + vbyte: byte; + vsmallint: smallint; + vcardinal: cardinal; + vint64: int64; + vchar: ansichar; + vwidechar: widechar; + vsingle: single; + vdouble: double; + vsetint: tsetint; + vsetenum: tsetenum; + vrec: trec; + vshortstring: shortstring; + vunicodestring: unicodestring; + varrbyte: tarrbyte; + varrset: tarrset; + i: longint; + + +procedure checkmainthreadvarsinit; + var + i: longint; + begin + if venum<>fboolean then + raise jlexception.create('enum in'); + venum:=cenumout; + if venum<>cenumout then + raise jlexception.create('enum out'); + if vboolean<>false then + raise jlexception.create('boolean in'); + vboolean:=cbooleanout; + if vboolean<>cbooleanout then + raise jlexception.create('boolean out'); + if vbyte<>0 then + raise jlexception.create('byte in'); + vbyte:=cbyteout; + if vbyte<>cbyteout then + raise jlexception.create('byte out'); + if vsmallint<>0 then + raise jlexception.create('smallint in'); + vsmallint:=csmallintout; + if vsmallint<>csmallintout then + raise jlexception.create('smallint out'); + if vcardinal<>0 then + raise jlexception.create('cardinal in'); + vcardinal:=ccardinalout; + if vcardinal<>ccardinalout then + raise jlexception.create('cardinal out'); + if vint64<>0 then + raise jlexception.create('int64 in'); + vint64:=cint64out; + if vint64<>cint64out then + raise jlexception.create('int64 out'); + if vchar<>#0 then + raise jlexception.create('char in'); + vchar:=ccharout; + if vchar<>ccharout then + raise jlexception.create('char out'); + if vwidechar<>#0 then + raise jlexception.create('widechar in'); + vwidechar:=cwidecharout; + if vwidechar<>cwidecharout then + raise jlexception.create('widechar out'); + if vsingle<>0 then + raise jlexception.create('single in'); + vsingle:=csingleout; + if vsingle<>csingleout then + raise jlexception.create('single out'); + if vdouble<>0 then + raise jlexception.create('double in'); + vdouble:=cdoubleout; + if vdouble<>cdoubleout then + raise jlexception.create('double out'); + if vsetint<>[] then + raise jlexception.create('setint in'); + vsetint:=csetintout; + if vsetint<>csetintout then + raise jlexception.create('setint out'); + if vsetenum<>[] then + raise jlexception.create('setenum in'); + vsetenum:=csetenumout; + if vsetenum<>csetenumout then + raise jlexception.create('setenum out'); + if vrec.a<>0 then + raise jlexception.create('rec.a in'); + if vrec.b[3]<>'' then + raise jlexception.create('rec.b[3] in'); + if vrec.b[4]<>'' then + raise jlexception.create('rec.b[4] in'); + vrec:=crecout; + if crecout.a<>vrec.a then + raise jlexception.create('rec.a out'); + if crecout.b[3]<>vrec.b[3] then + raise jlexception.create('rec.b[3] out'); + if crecout.b[4]<>vrec.b[4] then + raise jlexception.create('rec.b[4] out'); + if vshortstring<>'' then + raise jlexception.create('shortstring in'); + vshortstring:=cshortstringout; + if vshortstring<>cshortstringout then + raise jlexception.create('shortstring out'); + if vunicodestring<>'' then + raise jlexception.create('unicodestring in'); + vunicodestring:=cunicodestringout; + if vunicodestring<>cunicodestringout then + raise jlexception.create('unicodestring out'); + for i:=low(varrbyte) to high(varrbyte) do + if varrbyte[i]<>0 then + raise jlexception.create('arrbyte in'); + varrbyte:=carrbyteout; + for i:=low(carrbyteout) to high(carrbyteout) do + if carrbyteout[i]<>varrbyte[i] then + raise jlexception.create('arrbyte out'); + for i:=low(varrset) to high(varrset) do + if varrset[i]<>[] then + raise jlexception.create('arrset in'); + varrset:=carrsetout; + for i:=low(carrsetout) to high(carrsetout) do + if varrset[i]<>carrsetout[i] then + raise jlexception.create('arrset out'); + end; + + +procedure testmainthreadvarsafterwards; + var + i: longint; + begin + if venum<>cenumout then + raise jlexception.create('venum out2'); + if vboolean<>cbooleanout then + raise jlexception.create('boolean out2'); + if vbyte<>cbyteout then + raise jlexception.create('byte out2'); + if vsmallint<>csmallintout then + raise jlexception.create('smallint out2'); + if vcardinal<>ccardinalout then + raise jlexception.create('cardinal out2'); + if vint64<>cint64out then + raise jlexception.create('int64 out2'); + if vchar<>ccharout then + raise jlexception.create('char out2'); + if vwidechar<>cwidecharout then + raise jlexception.create('widechar out2'); + if vsingle<>csingleout then + raise jlexception.create('single out2'); + if vdouble<>cdoubleout then + raise jlexception.create('double out2'); + if vsetint<>csetintout then + raise jlexception.create('setint out2'); + if vsetenum<>csetenumout then + raise jlexception.create('setenum out2'); + if crecout.a<>vrec.a then + raise jlexception.create('rec.a out2'); + if crecout.b[3]<>vrec.b[3] then + raise jlexception.create('rec.b[3] out2'); + if crecout.b[4]<>vrec.b[4] then + raise jlexception.create('rec.b[4] out2'); + if vshortstring<>cshortstringout then + raise jlexception.create('shortstring out2'); + if vunicodestring<>cunicodestringout then + raise jlexception.create('unicodestring out2'); + for i:=low(carrbyteout) to high(carrbyteout) do + if carrbyteout[i]<>varrbyte[i] then + raise jlexception.create('arrbyte out2'); + for i:=low(carrsetout) to high(carrsetout) do + if varrset[i]<>carrsetout[i] then + raise jlexception.create('arrset out2'); + end; + + +procedure tmythread.run; + var + i: longint; + begin + if venum<>fboolean then + raise jlexception.create('enum in'); + venum:=cenumin; + if venum<>cenumin then + raise jlexception.create('enum out'); + if vboolean<>false then + raise jlexception.create('boolean in'); + vboolean:=cbooleanin; + if vboolean<>cbooleanin then + raise jlexception.create('boolean out'); + if vbyte<>0 then + raise jlexception.create('byte in'); + vbyte:=cbytein; + if vbyte<>cbytein then + raise jlexception.create('byte out'); + if vsmallint<>0 then + raise jlexception.create('smallint in'); + vsmallint:=csmallintin; + if vsmallint<>csmallintin then + raise jlexception.create('smallint out'); + if vcardinal<>0 then + raise jlexception.create('cardinal in'); + vcardinal:=ccardinalin; + if vcardinal<>ccardinalin then + raise jlexception.create('cardinal out'); + if vint64<>0 then + raise jlexception.create('int64 in'); + vint64:=cint64in; + if vint64<>cint64in then + raise jlexception.create('int64 out'); + if vchar<>#0 then + raise jlexception.create('char in'); + vchar:=ccharin; + if vchar<>ccharin then + raise jlexception.create('char out'); + if vwidechar<>#0 then + raise jlexception.create('widechar in'); + vwidechar:=cwidecharin; + if vwidechar<>cwidecharin then + raise jlexception.create('widechar out'); + if vsingle<>0 then + raise jlexception.create('single in'); + vsingle:=csinglein; + if vsingle<>csinglein then + raise jlexception.create('single out'); + if vdouble<>0 then + raise jlexception.create('double in'); + vdouble:=cdoublein; + if vdouble<>cdoublein then + raise jlexception.create('double out'); + if vsetint<>[] then + raise jlexception.create('setint in'); + vsetint:=csetintin; + if vsetint<>csetintin then + raise jlexception.create('setint out'); + if vsetenum<>[] then + raise jlexception.create('setenum in'); + vsetenum:=csetenumin; + if vsetenum<>csetenumin then + raise jlexception.create('setenum out'); + if vrec.a<>0 then + raise jlexception.create('rec.a in'); + if vrec.b[3]<>'' then + raise jlexception.create('rec.b[3] in'); + if vrec.b[4]<>'' then + raise jlexception.create('rec.b[4] in'); + vrec:=crecin; + if crecin.a<>vrec.a then + raise jlexception.create('rec.a out'); + if crecin.b[3]<>vrec.b[3] then + raise jlexception.create('rec.b[3] out'); + if crecin.b[4]<>vrec.b[4] then + raise jlexception.create('rec.b[4] out'); + if vshortstring<>'' then + raise jlexception.create('shortstring in'); + vshortstring:=cshortstringin; + if vshortstring<>cshortstringin then + raise jlexception.create('shortstring out'); + if vunicodestring<>'' then + raise jlexception.create('unicodestring in'); + vunicodestring:=cunicodestringin; + if vunicodestring<>cunicodestringin then + raise jlexception.create('unicodestring out'); + for i:=low(varrbyte) to high(varrbyte) do + if varrbyte[i]<>0 then + raise jlexception.create('arrbyte in'); + varrbyte:=carrbytein; + for i:=low(carrbytein) to high(carrbytein) do + if carrbytein[i]<>varrbyte[i] then + raise jlexception.create('arrbyte out'); + for i:=low(varrset) to high(varrset) do + if varrset[i]<>[] then + raise jlexception.create('arrset in'); + varrset:=carrsetin; + for i:=low(carrsetin) to high(carrsetin) do + if varrset[i]<>carrsetin[i] then + raise jlexception.create('arrset out'); + end; + + +procedure test; +var + t1, t2: tmythread; +begin + checkmainthreadvarsinit; + t1:=tmythread.create; + t1.start; + t2:=tmythread.create; + t2.start; + t1.join; + t2.join; + testmainthreadvarsafterwards; +end; + +begin + test; +end.