1.1 --- a/src/share/vm/interpreter/linkResolver.cpp Mon Jul 23 13:04:59 2012 -0700 1.2 +++ b/src/share/vm/interpreter/linkResolver.cpp Tue Jul 24 10:51:00 2012 -0700 1.3 @@ -96,15 +96,21 @@ 1.4 void CallInfo::set_virtual(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS) { 1.5 assert(vtable_index >= 0 || vtable_index == methodOopDesc::nonvirtual_vtable_index, "valid index"); 1.6 set_common(resolved_klass, selected_klass, resolved_method, selected_method, vtable_index, CHECK); 1.7 + assert(!resolved_method->is_compiled_lambda_form(), "these must be handled via an invokehandle call"); 1.8 } 1.9 1.10 -void CallInfo::set_dynamic(methodHandle resolved_method, TRAPS) { 1.11 - assert(resolved_method->is_method_handle_invoke(), ""); 1.12 +void CallInfo::set_handle(methodHandle resolved_method, Handle resolved_appendix, TRAPS) { 1.13 + if (resolved_method.is_null()) { 1.14 + THROW_MSG(vmSymbols::java_lang_InternalError(), "resolved method is null"); 1.15 + } 1.16 KlassHandle resolved_klass = SystemDictionaryHandles::MethodHandle_klass(); 1.17 - assert(resolved_klass == resolved_method->method_holder(), ""); 1.18 + assert(resolved_method->intrinsic_id() == vmIntrinsics::_invokeBasic || 1.19 + resolved_method->is_compiled_lambda_form(), 1.20 + "linkMethod must return one of these"); 1.21 int vtable_index = methodOopDesc::nonvirtual_vtable_index; 1.22 assert(resolved_method->vtable_index() == vtable_index, ""); 1.23 - set_common(resolved_klass, KlassHandle(), resolved_method, resolved_method, vtable_index, CHECK); 1.24 + set_common(resolved_klass, resolved_klass, resolved_method, resolved_method, vtable_index, CHECK); 1.25 + _resolved_appendix = resolved_appendix; 1.26 } 1.27 1.28 void CallInfo::set_common(KlassHandle resolved_klass, KlassHandle selected_klass, methodHandle resolved_method, methodHandle selected_method, int vtable_index, TRAPS) { 1.29 @@ -114,6 +120,7 @@ 1.30 _resolved_method = resolved_method; 1.31 _selected_method = selected_method; 1.32 _vtable_index = vtable_index; 1.33 + _resolved_appendix = Handle(); 1.34 if (CompilationPolicy::must_be_compiled(selected_method)) { 1.35 // This path is unusual, mostly used by the '-Xcomp' stress test mode. 1.36 1.37 @@ -180,11 +187,9 @@ 1.38 void LinkResolver::lookup_method_in_klasses(methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS) { 1.39 methodOop result_oop = klass->uncached_lookup_method(name, signature); 1.40 if (EnableInvokeDynamic && result_oop != NULL) { 1.41 - switch (result_oop->intrinsic_id()) { 1.42 - case vmIntrinsics::_invokeExact: 1.43 - case vmIntrinsics::_invokeGeneric: 1.44 - case vmIntrinsics::_invokeDynamic: 1.45 - // Do not link directly to these. The VM must produce a synthetic one using lookup_implicit_method. 1.46 + vmIntrinsics::ID iid = result_oop->intrinsic_id(); 1.47 + if (MethodHandles::is_signature_polymorphic(iid)) { 1.48 + // Do not link directly to these. The VM must produce a synthetic one using lookup_polymorphic_method. 1.49 return; 1.50 } 1.51 } 1.52 @@ -213,31 +218,97 @@ 1.53 result = methodHandle(THREAD, ik->lookup_method_in_all_interfaces(name, signature)); 1.54 } 1.55 1.56 -void LinkResolver::lookup_implicit_method(methodHandle& result, 1.57 - KlassHandle klass, Symbol* name, Symbol* signature, 1.58 - KlassHandle current_klass, 1.59 - TRAPS) { 1.60 +void LinkResolver::lookup_polymorphic_method(methodHandle& result, 1.61 + KlassHandle klass, Symbol* name, Symbol* full_signature, 1.62 + KlassHandle current_klass, 1.63 + Handle* appendix_result_or_null, 1.64 + TRAPS) { 1.65 + vmIntrinsics::ID iid = MethodHandles::signature_polymorphic_name_id(name); 1.66 + if (TraceMethodHandles) { 1.67 + tty->print_cr("lookup_polymorphic_method iid=%s %s.%s%s", 1.68 + vmIntrinsics::name_at(iid), klass->external_name(), 1.69 + name->as_C_string(), full_signature->as_C_string()); 1.70 + } 1.71 if (EnableInvokeDynamic && 1.72 klass() == SystemDictionary::MethodHandle_klass() && 1.73 - methodOopDesc::is_method_handle_invoke_name(name)) { 1.74 - if (!THREAD->is_Compiler_thread() && !MethodHandles::enabled()) { 1.75 - // Make sure the Java part of the runtime has been booted up. 1.76 - klassOop natives = SystemDictionary::MethodHandleNatives_klass(); 1.77 - if (natives == NULL || instanceKlass::cast(natives)->is_not_initialized()) { 1.78 - SystemDictionary::resolve_or_fail(vmSymbols::java_lang_invoke_MethodHandleNatives(), 1.79 - Handle(), 1.80 - Handle(), 1.81 - true, 1.82 - CHECK); 1.83 + iid != vmIntrinsics::_none) { 1.84 + if (MethodHandles::is_signature_polymorphic_intrinsic(iid)) { 1.85 + // Most of these do not need an up-call to Java to resolve, so can be done anywhere. 1.86 + // Do not erase last argument type (MemberName) if it is a static linkTo method. 1.87 + bool keep_last_arg = MethodHandles::is_signature_polymorphic_static(iid); 1.88 + TempNewSymbol basic_signature = 1.89 + MethodHandles::lookup_basic_type_signature(full_signature, keep_last_arg, CHECK); 1.90 + if (TraceMethodHandles) { 1.91 + tty->print_cr("lookup_polymorphic_method %s %s => basic %s", 1.92 + name->as_C_string(), 1.93 + full_signature->as_C_string(), 1.94 + basic_signature->as_C_string()); 1.95 } 1.96 - } 1.97 - methodOop result_oop = SystemDictionary::find_method_handle_invoke(name, 1.98 - signature, 1.99 - current_klass, 1.100 - CHECK); 1.101 - if (result_oop != NULL) { 1.102 - assert(result_oop->is_method_handle_invoke() && result_oop->signature() == signature, "consistent"); 1.103 - result = methodHandle(THREAD, result_oop); 1.104 + result = SystemDictionary::find_method_handle_intrinsic(iid, 1.105 + basic_signature, 1.106 + CHECK); 1.107 + if (result.not_null()) { 1.108 + assert(result->is_method_handle_intrinsic(), "MH.invokeBasic or MH.linkTo* intrinsic"); 1.109 + assert(result->intrinsic_id() != vmIntrinsics::_invokeGeneric, "wrong place to find this"); 1.110 + assert(basic_signature == result->signature(), "predict the result signature"); 1.111 + if (TraceMethodHandles) { 1.112 + tty->print("lookup_polymorphic_method => intrinsic "); 1.113 + result->print_on(tty); 1.114 + } 1.115 + return; 1.116 + } 1.117 + } else if (iid == vmIntrinsics::_invokeGeneric 1.118 + && !THREAD->is_Compiler_thread() 1.119 + && appendix_result_or_null != NULL) { 1.120 + // This is a method with type-checking semantics. 1.121 + // We will ask Java code to spin an adapter method for it. 1.122 + if (!MethodHandles::enabled()) { 1.123 + // Make sure the Java part of the runtime has been booted up. 1.124 + klassOop natives = SystemDictionary::MethodHandleNatives_klass(); 1.125 + if (natives == NULL || instanceKlass::cast(natives)->is_not_initialized()) { 1.126 + SystemDictionary::resolve_or_fail(vmSymbols::java_lang_invoke_MethodHandleNatives(), 1.127 + Handle(), 1.128 + Handle(), 1.129 + true, 1.130 + CHECK); 1.131 + } 1.132 + } 1.133 + 1.134 + Handle appendix; 1.135 + result = SystemDictionary::find_method_handle_invoker(name, 1.136 + full_signature, 1.137 + current_klass, 1.138 + &appendix, 1.139 + CHECK); 1.140 + if (TraceMethodHandles) { 1.141 + tty->print("lookup_polymorphic_method => (via Java) "); 1.142 + result->print_on(tty); 1.143 + tty->print(" lookup_polymorphic_method => appendix = "); 1.144 + if (appendix.is_null()) tty->print_cr("(none)"); 1.145 + else appendix->print_on(tty); 1.146 + } 1.147 + if (result.not_null()) { 1.148 +#ifdef ASSERT 1.149 + TempNewSymbol basic_signature = 1.150 + MethodHandles::lookup_basic_type_signature(full_signature, CHECK); 1.151 + int actual_size_of_params = result->size_of_parameters(); 1.152 + int expected_size_of_params = ArgumentSizeComputer(basic_signature).size(); 1.153 + // +1 for MethodHandle.this, +1 for trailing MethodType 1.154 + if (!MethodHandles::is_signature_polymorphic_static(iid)) expected_size_of_params += 1; 1.155 + if (appendix.not_null()) expected_size_of_params += 1; 1.156 + if (actual_size_of_params != expected_size_of_params) { 1.157 + tty->print_cr("*** basic_signature=%s", basic_signature->as_C_string()); 1.158 + tty->print_cr("*** result for %s: ", vmIntrinsics::name_at(iid)); 1.159 + result->print(); 1.160 + } 1.161 + assert(actual_size_of_params == expected_size_of_params, 1.162 + err_msg("%d != %d", actual_size_of_params, expected_size_of_params)); 1.163 +#endif //ASSERT 1.164 + 1.165 + assert(appendix_result_or_null != NULL, ""); 1.166 + (*appendix_result_or_null) = appendix; 1.167 + return; 1.168 + } 1.169 } 1.170 } 1.171 } 1.172 @@ -267,6 +338,7 @@ 1.173 new_flags = new_flags | JVM_ACC_PUBLIC; 1.174 flags.set_flags(new_flags); 1.175 } 1.176 +// assert(extra_arg_result_or_null != NULL, "must be able to return extra argument"); 1.177 1.178 if (!Reflection::verify_field_access(ref_klass->as_klassOop(), 1.179 resolved_klass->as_klassOop(), 1.180 @@ -287,10 +359,19 @@ 1.181 } 1.182 } 1.183 1.184 -void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle& resolved_klass, 1.185 - constantPoolHandle pool, int index, TRAPS) { 1.186 +void LinkResolver::resolve_method_statically(methodHandle& resolved_method, KlassHandle& resolved_klass, 1.187 + Bytecodes::Code code, constantPoolHandle pool, int index, TRAPS) { 1.188 1.189 // resolve klass 1.190 + if (code == Bytecodes::_invokedynamic) { 1.191 + resolved_klass = SystemDictionaryHandles::MethodHandle_klass(); 1.192 + Symbol* method_name = vmSymbols::invoke_name(); 1.193 + Symbol* method_signature = pool->signature_ref_at(index); 1.194 + KlassHandle current_klass(THREAD, pool->pool_holder()); 1.195 + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); 1.196 + return; 1.197 + } 1.198 + 1.199 resolve_klass(resolved_klass, pool, index, CHECK); 1.200 1.201 Symbol* method_name = pool->name_ref_at(index); 1.202 @@ -299,7 +380,7 @@ 1.203 1.204 if (pool->has_preresolution() 1.205 || (resolved_klass() == SystemDictionary::MethodHandle_klass() && 1.206 - methodOopDesc::is_method_handle_invoke_name(method_name))) { 1.207 + MethodHandles::is_signature_polymorphic_name(resolved_klass(), method_name))) { 1.208 methodOop result_oop = constantPoolOopDesc::method_at_if_loaded(pool, index); 1.209 if (result_oop != NULL) { 1.210 resolved_method = methodHandle(THREAD, result_oop); 1.211 @@ -307,33 +388,13 @@ 1.212 } 1.213 } 1.214 1.215 - resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); 1.216 + if (code == Bytecodes::_invokeinterface) { 1.217 + resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); 1.218 + } else { 1.219 + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); 1.220 + } 1.221 } 1.222 1.223 -void LinkResolver::resolve_dynamic_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) { 1.224 - // The class is java.lang.invoke.MethodHandle 1.225 - resolved_klass = SystemDictionaryHandles::MethodHandle_klass(); 1.226 - 1.227 - Symbol* method_name = vmSymbols::invokeExact_name(); 1.228 - 1.229 - Symbol* method_signature = pool->signature_ref_at(index); 1.230 - KlassHandle current_klass (THREAD, pool->pool_holder()); 1.231 - 1.232 - resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); 1.233 -} 1.234 - 1.235 -void LinkResolver::resolve_interface_method(methodHandle& resolved_method, KlassHandle& resolved_klass, constantPoolHandle pool, int index, TRAPS) { 1.236 - 1.237 - // resolve klass 1.238 - resolve_klass(resolved_klass, pool, index, CHECK); 1.239 - Symbol* method_name = pool->name_ref_at(index); 1.240 - Symbol* method_signature = pool->signature_ref_at(index); 1.241 - KlassHandle current_klass(THREAD, pool->pool_holder()); 1.242 - 1.243 - resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); 1.244 -} 1.245 - 1.246 - 1.247 void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle resolved_klass, 1.248 Symbol* method_name, Symbol* method_signature, 1.249 KlassHandle current_klass, bool check_access, TRAPS) { 1.250 @@ -346,6 +407,8 @@ 1.251 THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); 1.252 } 1.253 1.254 + Handle nested_exception; 1.255 + 1.256 // 2. lookup method in resolved klass and its super klasses 1.257 lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK); 1.258 1.259 @@ -354,17 +417,23 @@ 1.260 lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK); 1.261 1.262 if (resolved_method.is_null()) { 1.263 - // JSR 292: see if this is an implicitly generated method MethodHandle.invoke(*...) 1.264 - lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, CHECK); 1.265 + // JSR 292: see if this is an implicitly generated method MethodHandle.linkToVirtual(*...), etc 1.266 + lookup_polymorphic_method(resolved_method, resolved_klass, method_name, method_signature, 1.267 + current_klass, (Handle*)NULL, THREAD); 1.268 + if (HAS_PENDING_EXCEPTION) { 1.269 + nested_exception = Handle(THREAD, PENDING_EXCEPTION); 1.270 + CLEAR_PENDING_EXCEPTION; 1.271 + } 1.272 } 1.273 1.274 if (resolved_method.is_null()) { 1.275 // 4. method lookup failed 1.276 ResourceMark rm(THREAD); 1.277 - THROW_MSG(vmSymbols::java_lang_NoSuchMethodError(), 1.278 - methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), 1.279 - method_name, 1.280 - method_signature)); 1.281 + THROW_MSG_CAUSE(vmSymbols::java_lang_NoSuchMethodError(), 1.282 + methodOopDesc::name_and_sig_as_C_string(Klass::cast(resolved_klass()), 1.283 + method_name, 1.284 + method_signature), 1.285 + nested_exception); 1.286 } 1.287 } 1.288 1.289 @@ -1053,6 +1122,7 @@ 1.290 case Bytecodes::_invokestatic : resolve_invokestatic (result, pool, index, CHECK); break; 1.291 case Bytecodes::_invokespecial : resolve_invokespecial (result, pool, index, CHECK); break; 1.292 case Bytecodes::_invokevirtual : resolve_invokevirtual (result, recv, pool, index, CHECK); break; 1.293 + case Bytecodes::_invokehandle : resolve_invokehandle (result, pool, index, CHECK); break; 1.294 case Bytecodes::_invokedynamic : resolve_invokedynamic (result, pool, index, CHECK); break; 1.295 case Bytecodes::_invokeinterface: resolve_invokeinterface(result, recv, pool, index, CHECK); break; 1.296 } 1.297 @@ -1116,22 +1186,91 @@ 1.298 } 1.299 1.300 1.301 -void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int raw_index, TRAPS) { 1.302 +void LinkResolver::resolve_invokehandle(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { 1.303 assert(EnableInvokeDynamic, ""); 1.304 + // This guy is reached from InterpreterRuntime::resolve_invokehandle. 1.305 + KlassHandle resolved_klass; 1.306 + Symbol* method_name = NULL; 1.307 + Symbol* method_signature = NULL; 1.308 + KlassHandle current_klass; 1.309 + resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK); 1.310 + if (TraceMethodHandles) 1.311 + tty->print_cr("resolve_invokehandle %s %s", method_name->as_C_string(), method_signature->as_C_string()); 1.312 + resolve_handle_call(result, resolved_klass, method_name, method_signature, current_klass, CHECK); 1.313 +} 1.314 1.315 - // This guy is reached from InterpreterRuntime::resolve_invokedynamic. 1.316 +void LinkResolver::resolve_handle_call(CallInfo& result, KlassHandle resolved_klass, 1.317 + Symbol* method_name, Symbol* method_signature, 1.318 + KlassHandle current_klass, 1.319 + TRAPS) { 1.320 + // JSR 292: this must be an implicitly generated method MethodHandle.invokeExact(*...) or similar 1.321 + assert(resolved_klass() == SystemDictionary::MethodHandle_klass(), ""); 1.322 + assert(MethodHandles::is_signature_polymorphic_name(method_name), ""); 1.323 + methodHandle resolved_method; 1.324 + Handle resolved_appendix; 1.325 + lookup_polymorphic_method(resolved_method, resolved_klass, 1.326 + method_name, method_signature, 1.327 + current_klass, &resolved_appendix, CHECK); 1.328 + result.set_handle(resolved_method, resolved_appendix, CHECK); 1.329 +} 1.330 1.331 - // At this point, we only need the signature, and can ignore the name. 1.332 - Symbol* method_signature = pool->signature_ref_at(raw_index); // raw_index works directly 1.333 - Symbol* method_name = vmSymbols::invokeExact_name(); 1.334 - KlassHandle resolved_klass = SystemDictionaryHandles::MethodHandle_klass(); 1.335 1.336 - // JSR 292: this must be an implicitly generated method MethodHandle.invokeExact(*...) 1.337 - // The extra MH receiver will be inserted into the stack on every call. 1.338 - methodHandle resolved_method; 1.339 - KlassHandle current_klass(THREAD, pool->pool_holder()); 1.340 - lookup_implicit_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, THREAD); 1.341 +void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { 1.342 + assert(EnableInvokeDynamic, ""); 1.343 + pool->set_invokedynamic(); // mark header to flag active call sites 1.344 + 1.345 + //resolve_pool(<resolved_klass>, method_name, method_signature, current_klass, pool, index, CHECK); 1.346 + Symbol* method_name = pool->name_ref_at(index); 1.347 + Symbol* method_signature = pool->signature_ref_at(index); 1.348 + KlassHandle current_klass = KlassHandle(THREAD, pool->pool_holder()); 1.349 + 1.350 + // Resolve the bootstrap specifier (BSM + optional arguments). 1.351 + Handle bootstrap_specifier; 1.352 + // Check if CallSite has been bound already: 1.353 + ConstantPoolCacheEntry* cpce = pool->cache()->secondary_entry_at(index); 1.354 + if (cpce->is_f1_null()) { 1.355 + int pool_index = pool->cache()->main_entry_at(index)->constant_pool_index(); 1.356 + oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK); 1.357 + assert(bsm_info != NULL, ""); 1.358 + // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic. 1.359 + bootstrap_specifier = Handle(THREAD, bsm_info); 1.360 + } 1.361 + if (!cpce->is_f1_null()) { 1.362 + methodHandle method(THREAD, cpce->f2_as_vfinal_method()); 1.363 + Handle appendix(THREAD, cpce->has_appendix() ? cpce->f1_appendix() : (oop)NULL); 1.364 + result.set_handle(method, appendix, CHECK); 1.365 + return; 1.366 + } 1.367 + 1.368 + if (TraceMethodHandles) { 1.369 + tty->print_cr("resolve_invokedynamic #%d %s %s", 1.370 + constantPoolCacheOopDesc::decode_secondary_index(index), 1.371 + method_name->as_C_string(), method_signature->as_C_string()); 1.372 + tty->print(" BSM info: "); bootstrap_specifier->print(); 1.373 + } 1.374 + 1.375 + resolve_dynamic_call(result, bootstrap_specifier, method_name, method_signature, current_klass, CHECK); 1.376 +} 1.377 + 1.378 +void LinkResolver::resolve_dynamic_call(CallInfo& result, 1.379 + Handle bootstrap_specifier, 1.380 + Symbol* method_name, Symbol* method_signature, 1.381 + KlassHandle current_klass, 1.382 + TRAPS) { 1.383 + // JSR 292: this must resolve to an implicitly generated method MH.linkToCallSite(*...) 1.384 + // The appendix argument is likely to be a freshly-created CallSite. 1.385 + Handle resolved_appendix; 1.386 + methodHandle resolved_method = 1.387 + SystemDictionary::find_dynamic_call_site_invoker(current_klass, 1.388 + bootstrap_specifier, 1.389 + method_name, method_signature, 1.390 + &resolved_appendix, 1.391 + CHECK); 1.392 if (HAS_PENDING_EXCEPTION) { 1.393 + if (TraceMethodHandles) { 1.394 + tty->print_cr("invokedynamic throws BSME for "INTPTR_FORMAT, PENDING_EXCEPTION); 1.395 + PENDING_EXCEPTION->print(); 1.396 + } 1.397 if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { 1.398 // throw these guys, since they are already wrapped 1.399 return; 1.400 @@ -1141,17 +1280,12 @@ 1.401 return; 1.402 } 1.403 // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS. 1.404 - Handle ex(THREAD, PENDING_EXCEPTION); 1.405 + Handle nested_exception(THREAD, PENDING_EXCEPTION); 1.406 CLEAR_PENDING_EXCEPTION; 1.407 - oop bsme = Klass::cast(SystemDictionary::BootstrapMethodError_klass())->java_mirror(); 1.408 - MethodHandles::raise_exception(Bytecodes::_athrow, ex(), bsme, CHECK); 1.409 - // java code should not return, but if it does throw out anyway 1.410 - THROW(vmSymbols::java_lang_InternalError()); 1.411 + THROW_MSG_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), 1.412 + "BootstrapMethodError", nested_exception) 1.413 } 1.414 - if (resolved_method.is_null()) { 1.415 - THROW(vmSymbols::java_lang_InternalError()); 1.416 - } 1.417 - result.set_dynamic(resolved_method, CHECK); 1.418 + result.set_handle(resolved_method, resolved_appendix, CHECK); 1.419 } 1.420 1.421 //------------------------------------------------------------------------------------------------------------------------