diff --git a/springloaded/.settings/org.eclipse.jdt.core.prefs b/springloaded/.settings/org.eclipse.jdt.core.prefs
index 416f4fb..51620f3 100644
--- a/springloaded/.settings/org.eclipse.jdt.core.prefs
+++ b/springloaded/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,21 @@
eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
@@ -6,6 +23,370 @@ org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
+org.eclipse.jdt.core.compiler.problem.deadCode=warning
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
+org.eclipse.jdt.core.compiler.problem.nullReference=ignore
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=80
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=1
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=1
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=false
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=false
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=120
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=false
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=120
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=2
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=tab
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/springloaded/.settings/org.eclipse.jdt.ui.prefs b/springloaded/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..7231a56
--- /dev/null
+++ b/springloaded/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,125 @@
+cleanup.add_default_serial_version_id=true
+cleanup.add_generated_serial_version_id=false
+cleanup.add_missing_annotations=false
+cleanup.add_missing_deprecated_annotations=true
+cleanup.add_missing_methods=false
+cleanup.add_missing_nls_tags=false
+cleanup.add_missing_override_annotations=true
+cleanup.add_missing_override_annotations_interface_methods=true
+cleanup.add_serial_version_id=false
+cleanup.always_use_blocks=true
+cleanup.always_use_parentheses_in_expressions=false
+cleanup.always_use_this_for_non_static_field_access=false
+cleanup.always_use_this_for_non_static_method_access=false
+cleanup.convert_to_enhanced_for_loop=false
+cleanup.correct_indentation=true
+cleanup.format_source_code=true
+cleanup.format_source_code_changes_only=false
+cleanup.make_local_variable_final=false
+cleanup.make_parameters_final=false
+cleanup.make_private_fields_final=false
+cleanup.make_type_abstract_if_missing_method=false
+cleanup.make_variable_declarations_final=true
+cleanup.never_use_blocks=false
+cleanup.never_use_parentheses_in_expressions=true
+cleanup.organize_imports=true
+cleanup.qualify_static_field_accesses_with_declaring_class=false
+cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+cleanup.qualify_static_member_accesses_with_declaring_class=true
+cleanup.qualify_static_method_accesses_with_declaring_class=false
+cleanup.remove_private_constructors=true
+cleanup.remove_trailing_whitespaces=true
+cleanup.remove_trailing_whitespaces_all=true
+cleanup.remove_trailing_whitespaces_ignore_empty=false
+cleanup.remove_unnecessary_casts=true
+cleanup.remove_unnecessary_nls_tags=true
+cleanup.remove_unused_imports=true
+cleanup.remove_unused_local_variables=false
+cleanup.remove_unused_private_fields=true
+cleanup.remove_unused_private_members=false
+cleanup.remove_unused_private_methods=true
+cleanup.remove_unused_private_types=true
+cleanup.sort_members=false
+cleanup.sort_members_all=false
+cleanup.use_blocks=true
+cleanup.use_blocks_only_for_return_and_throw=false
+cleanup.use_parentheses_in_expressions=false
+cleanup.use_this_for_non_static_field_access=false
+cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+cleanup.use_this_for_non_static_method_access=false
+cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+cleanup_profile=_Spring
+cleanup_settings_version=2
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_SLProfile
+formatter_settings_version=12
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=\#;java;javax;org;org.springframework;com;
+org.eclipse.jdt.ui.javadoc=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.ondemandthreshold=9999
+org.eclipse.jdt.ui.overrideannotation=true
+org.eclipse.jdt.ui.staticondemandthreshold=9999
+org.eclipse.jdt.ui.text.custom_code_templates=/**\n * ${tags}\n *//*\n * Copyright 2014 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the "License");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http\://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/**\n * ${tags}\n * @author ${user}\n *//**\n *\n *//**\n * ${tags}\n *//**\n * ${tags}\n * ${see_to_target}\n */${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}\n\n\n\n// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();// ${todo} Auto-generated method stub\nthrow new UnsupportedOperationException("Auto-generated method stub");${body_statement}\n// ${todo} Auto-generated constructor stubreturn ${field};${field} \= ${param};
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=true
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=true
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=true
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=true
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+sp_cleanup.use_type_arguments=false
diff --git a/springloaded/src/main/java/org/springsource/loaded/AbstractMember.java b/springloaded/src/main/java/org/springsource/loaded/AbstractMember.java
index e3e20f1..f33b8b6 100644
--- a/springloaded/src/main/java/org/springsource/loaded/AbstractMember.java
+++ b/springloaded/src/main/java/org/springsource/loaded/AbstractMember.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Modifier;
@@ -26,12 +27,17 @@ import java.lang.reflect.Modifier;
public abstract class AbstractMember implements Constants {
protected final int modifiers;
+
protected final String name;
+
protected final String descriptor; // this is the erased descriptor. There is no generic descriptor.
+
// Members have a well known id within their type - ids are unique per kind of member (methods/fields/constructors)
protected int id = -1;
+
// For generic methods, contains generic signature
protected final String signature;
+
private final boolean isPrivate; // gets asked a lot so made into a flag
protected AbstractMember(int modifiers, String name, String descriptor, String signature) {
diff --git a/springloaded/src/main/java/org/springsource/loaded/AnyTypePattern.java b/springloaded/src/main/java/org/springsource/loaded/AnyTypePattern.java
index c8b6223..c49c802 100644
--- a/springloaded/src/main/java/org/springsource/loaded/AnyTypePattern.java
+++ b/springloaded/src/main/java/org/springsource/loaded/AnyTypePattern.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
diff --git a/springloaded/src/main/java/org/springsource/loaded/Asserts.java b/springloaded/src/main/java/org/springsource/loaded/Asserts.java
index f11e334..59a79e8 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Asserts.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Asserts.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
diff --git a/springloaded/src/main/java/org/springsource/loaded/C.java b/springloaded/src/main/java/org/springsource/loaded/C.java
index 471e3f5..fce187d 100644
--- a/springloaded/src/main/java/org/springsource/loaded/C.java
+++ b/springloaded/src/main/java/org/springsource/loaded/C.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
// TODO [moredoc]
diff --git a/springloaded/src/main/java/org/springsource/loaded/ChildClassLoader.java b/springloaded/src/main/java/org/springsource/loaded/ChildClassLoader.java
index 25d1be9..03304a8 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ChildClassLoader.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ChildClassLoader.java
@@ -13,19 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.net.URL;
import java.net.URLClassLoader;
/**
- * The ChildClassLoader will load the generated dispatchers and executors which change for each reload. Instances of this can be
- * discarded which will cause 'old' dispatchers/executors to be candidates for GC too (avoiding memory leaks when lots of reloads
- * occur).
+ * The ChildClassLoader will load the generated dispatchers and executors which change for each reload. Instances of
+ * this can be discarded which will cause 'old' dispatchers/executors to be candidates for GC too (avoiding memory leaks
+ * when lots of reloads occur).
*/
public class ChildClassLoader extends URLClassLoader {
private static URL[] NO_URLS = new URL[0];
+
private int definedCount = 0;
public ChildClassLoader(ClassLoader classloader) {
@@ -41,4 +43,4 @@ public class ChildClassLoader extends URLClassLoader {
return definedCount;
}
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java b/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java
index bb1ee92..a6392a4 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.util.HashMap;
@@ -28,8 +29,8 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
- * Modify a class by changing it from one name to another. References to other types can also be changed. Basically used in the test
- * suite.
+ * Modify a class by changing it from one name to another. References to other types can also be changed. Basically used
+ * in the test suite.
*
* @author Andy Clement
* @since 0.5.0
@@ -37,9 +38,9 @@ import org.objectweb.asm.Type;
public class ClassRenamer {
/**
- * Rename a type - changing it to specified new name (which should be the dotted form of the name). Retargets are an optional
- * sequence of retargets to also perform during the rename. Retargets take the form of "a.b:a.c" which will change all
- * references to a.b to a.c.
+ * Rename a type - changing it to specified new name (which should be the dotted form of the name). Retargets are an
+ * optional sequence of retargets to also perform during the rename. Retargets take the form of "a.b:a.c" which will
+ * change all references to a.b to a.c.
*
* @param dottedNewName dotted name, e.g. com.foo.Bar
* @param classbytes the bytecode for the class to be renamed
@@ -57,18 +58,22 @@ public class ClassRenamer {
static class RenameAdapter extends ClassVisitor implements Opcodes {
private ClassWriter cw;
+
private String oldname;
+
private String newname;
+
private Map retargets = new HashMap();
public RenameAdapter(String newname, String[] retargets) {
- super(ASM5,new ClassWriter(0));
+ super(ASM5, new ClassWriter(0));
cw = (ClassWriter) cv;
this.newname = newname.replace('.', '/');
if (retargets != null) {
for (String retarget : retargets) {
int i = retarget.indexOf(":");
- this.retargets.put(retarget.substring(0, i).replace('.', '/'), retarget.substring(i + 1).replace('.', '/'));
+ this.retargets.put(retarget.substring(0, i).replace('.', '/'),
+ retarget.substring(i + 1).replace('.', '/'));
}
}
}
@@ -98,9 +103,10 @@ public class ClassRenamer {
@Override
public void visitInnerClass(String name, String outername, String innerName, int access) {
- super.visitInnerClass(renameRetargetIfNecessary(name), renameRetargetIfNecessary(outername), renameRetargetIfNecessary(innerName), access);
+ super.visitInnerClass(renameRetargetIfNecessary(name), renameRetargetIfNecessary(outername),
+ renameRetargetIfNecessary(innerName), access);
}
-
+
private String renameRetargetIfNecessary(String string) {
String value = retargets.get(string);
if (value != null) {
@@ -111,12 +117,14 @@ public class ClassRenamer {
}
return string;
}
-
+
@Override
- public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
+ public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
+ String[] exceptions) {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
- } else {
+ }
+ else {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
}
@@ -134,7 +142,8 @@ public class ClassRenamer {
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
- } else {
+ }
+ else {
for (String s : retargets.keySet()) {
if (desc.indexOf(s) != -1) {
desc = desc.replace(s, retargets.get(s));
@@ -147,10 +156,11 @@ public class ClassRenamer {
class RenameMethodAdapter extends MethodVisitor implements Opcodes {
String oldname;
+
String newname;
public RenameMethodAdapter(MethodVisitor mv, String oldname, String newname) {
- super(ASM5,mv);
+ super(ASM5, mv);
this.oldname = oldname;
this.newname = newname;
}
@@ -158,7 +168,8 @@ public class ClassRenamer {
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(oldname)) {
owner = newname;
- } else {
+ }
+ else {
String retarget = retargets.get(owner);
if (retarget != null) {
owner = retarget;
@@ -166,7 +177,8 @@ public class ClassRenamer {
}
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
- } else {
+ }
+ else {
desc = checkIfShouldBeRewritten(desc);
}
mv.visitFieldInsn(opcode, owner, name, desc);
@@ -175,11 +187,13 @@ public class ClassRenamer {
public void visitTypeInsn(int opcode, String type) {
if (type.equals(oldname)) {
type = newname;
- } else {
+ }
+ else {
String retarget = retargets.get(type);
if (retarget != null) {
type = retarget;
- } else {
+ }
+ else {
if (type.startsWith("[")) {
if (type.indexOf(oldname) != -1) {
type = type.replaceFirst(oldname, newname);
@@ -192,28 +206,32 @@ public class ClassRenamer {
@Override
public void visitLdcInsn(Object obj) {
-// System.out.println("Possibly remapping "+obj);
+ // System.out.println("Possibly remapping "+obj);
if (obj instanceof Type) {
Type t = (Type) obj;
String s = t.getInternalName();
String retarget = retargets.get(s);
if (retarget != null) {
mv.visitLdcInsn(Type.getObjectType(retarget));
- } else {
+ }
+ else {
mv.visitLdcInsn(obj);
}
- } else if (obj instanceof String) {
+ }
+ else if (obj instanceof String) {
String s = (String) obj;
String retarget = retargets.get(s.replace('.', '/'));
if (retarget != null) {
mv.visitLdcInsn(retarget.replace('/', '.'));
- } else {
+ }
+ else {
String oldnameDotted = oldname.replace('/', '.');
if (s.equals(oldnameDotted)) {
String nname = newname.replace('/', '.');
mv.visitLdcInsn(nname);
return;
- } else if (s.startsWith("[")) {
+ }
+ else if (s.startsWith("[")) {
// might be array of oldname
if (s.indexOf(oldnameDotted) != -1) {
mv.visitLdcInsn(s.replaceFirst(oldnameDotted, newname.replace('/', '.')));
@@ -222,21 +240,23 @@ public class ClassRenamer {
}
mv.visitLdcInsn(obj);
}
- } else {
+ }
+ else {
mv.visitLdcInsn(obj);
}
}
private String toString(Handle bsm) {
- return "["+bsm.getTag()+"]"+bsm.getOwner()+"."+bsm.getName()+bsm.getDesc();
+ return "[" + bsm.getTag() + "]" + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
}
-
+
private String toString(Object[] os) {
StringBuilder buf = new StringBuilder();
- if (os!=null) {
+ if (os != null) {
buf.append("[");
- for (int i=0;i0) buf.append(",");
+ for (int i = 0; i < os.length; i++) {
+ if (i > 0)
+ buf.append(",");
buf.append(os[i]);
}
buf.append("]");
@@ -246,21 +266,21 @@ public class ClassRenamer {
}
return buf.toString();
}
-
+
private Handle retargetHandle(Handle oldHandle) {
int tag = oldHandle.getTag();
String owner = oldHandle.getOwner();
String name = oldHandle.getName();
String desc = oldHandle.getDesc();
-// System.out.println("handle: owner: "+owner);
-// System.out.println("handle: name: "+name);
-// System.out.println("handle: desc: "+desc);
+ // System.out.println("handle: owner: "+owner);
+ // System.out.println("handle: name: "+name);
+ // System.out.println("handle: desc: "+desc);
owner = renameRetargetIfNecessary(owner);
desc = renameRetargetIfNecessary(desc);
- Handle newHandle = new Handle(tag,owner,name,desc);
+ Handle newHandle = new Handle(tag, owner, name, desc);
return newHandle;
}
-
+
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
// Example:
@@ -269,20 +289,22 @@ public class ClassRenamer {
// bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
desc = renameRetargetIfNecessary(desc);
if (bsmArgs[1] instanceof Handle) {
- bsmArgs[1] = retargetHandle((Handle)bsmArgs[1]);
+ bsmArgs[1] = retargetHandle((Handle) bsmArgs[1]);
}
- mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
-
+
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (owner.equals(oldname)) {
owner = newname;
- } else {
+ }
+ else {
owner = retargetIfNecessary(owner);
}
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
- } else {
+ }
+ else {
desc = checkIfShouldBeRewritten(desc);
}
mv.visitMethodInsn(opcode, owner, name, desc, itf);
diff --git a/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker.java b/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker.java
index 332ec0a..a6d7c00 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.io.ByteArrayInputStream;
@@ -25,8 +26,8 @@ import java.util.List;
// http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
/**
- * Quickly checks the constant pool for class references, it skips everything else as fast as it can. The class references are then
- * available for checking.
+ * Quickly checks the constant pool for class references, it skips everything else as fast as it can. The class
+ * references are then available for checking.
*
* @author Andy Clement
* @since 0.7.3
@@ -36,15 +37,25 @@ public class ConstantPoolChecker {
private static final boolean DEBUG = false;
private final static byte CONSTANT_Utf8 = 1;
+
private final static byte CONSTANT_Integer = 3;
+
private final static byte CONSTANT_Float = 4;
+
private final static byte CONSTANT_Long = 5;
+
private final static byte CONSTANT_Double = 6;
+
private final static byte CONSTANT_Class = 7;
+
private final static byte CONSTANT_String = 8;
+
private final static byte CONSTANT_Fieldref = 9;
+
private final static byte CONSTANT_Methodref = 10;
+
private final static byte CONSTANT_InterfaceMethodref = 11;
+
private final static byte CONSTANT_NameAndType = 12;
// Test entry point just goes through all the code in the bin folder
@@ -97,8 +108,11 @@ public class ConstantPoolChecker {
// Filled with strings and int[]
private Object[] cpdata;
+
private int cpsize;
+
private int[] type;
+
// Does not need to be a set as there are no dups in the ConstantPool (for a class from a decent compiler...)
private List referencedClasses = new ArrayList();
@@ -110,14 +124,14 @@ public class ConstantPoolChecker {
public void computeReferences() {
for (int i = 0; i < cpsize; i++) {
switch (type[i]) {
- case CONSTANT_Class:
- int classindex = ((Integer) cpdata[i]);
- String classname = (String) cpdata[classindex];
- if (classname == null) {
- throw new IllegalStateException();
- }
- referencedClasses.add(classname);
- break;
+ case CONSTANT_Class:
+ int classindex = ((Integer) cpdata[i]);
+ String classname = (String) cpdata[classindex];
+ if (classname == null) {
+ throw new IllegalStateException();
+ }
+ referencedClasses.add(classname);
+ break;
// private final static byte CONSTANT_Utf8 = 1;
// private final static byte CONSTANT_Integer = 3;
// private final static byte CONSTANT_Float = 4;
@@ -157,7 +171,8 @@ public class ConstantPoolChecker {
cpentry++;
}
}
- } catch (Exception e) {
+ }
+ catch (Exception e) {
throw new IllegalStateException("Unexpected problem processing bytes for class", e);
}
}
@@ -166,166 +181,178 @@ public class ConstantPoolChecker {
byte b = dis.readByte();
type[index] = b;
switch (b) {
- case CONSTANT_Utf8:
- // CONSTANT_Utf8_info {
- // u1 tag;
- // u2 length;
- // u1 bytes[length];
- // }
- cpdata[index] = dis.readUTF();
- if (DEBUG) {
- System.out.println(index + ":UTF8[" + cpdata[index] + "]");
- }
- break;
- case CONSTANT_Integer:
- // CONSTANT_Integer_info {
- // u1 tag;
- // u4 bytes;
- // }
- if (DEBUG) {
- int i = dis.readInt();
+ case CONSTANT_Utf8:
+ // CONSTANT_Utf8_info {
+ // u1 tag;
+ // u2 length;
+ // u1 bytes[length];
+ // }
+ cpdata[index] = dis.readUTF();
if (DEBUG) {
- System.out.println(index + ":INTEGER[" + i + "]");
+ System.out.println(index + ":UTF8[" + cpdata[index] + "]");
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_Float:
- // CONSTANT_Float_info {
- // u1 tag;
- // u4 bytes;
- // }
- if (DEBUG) {
- float f = dis.readFloat();
+ break;
+ case CONSTANT_Integer:
+ // CONSTANT_Integer_info {
+ // u1 tag;
+ // u4 bytes;
+ // }
if (DEBUG) {
- System.out.println(index + ":FLOAT[" + f + "]");
+ int i = dis.readInt();
+ if (DEBUG) {
+ System.out.println(index + ":INTEGER[" + i + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_Long:
- // CONSTANT_Long_info {
- // u1 tag;
- // u4 high_bytes;
- // u4 low_bytes;
- // }
- if (DEBUG) {
- long l = dis.readLong();
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_Float:
+ // CONSTANT_Float_info {
+ // u1 tag;
+ // u4 bytes;
+ // }
if (DEBUG) {
- System.out.println(index + ":LONG[" + l + "]");
+ float f = dis.readFloat();
+ if (DEBUG) {
+ System.out.println(index + ":FLOAT[" + f + "]");
+ }
}
- } else {
- dis.skip(8);
- }
- return true;
- case CONSTANT_Double:
- // CONSTANT_Double_info {
- // u1 tag;
- // u4 high_bytes;
- // u4 low_bytes;
- // }
- if (DEBUG) {
- double d = dis.readDouble();
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_Long:
+ // CONSTANT_Long_info {
+ // u1 tag;
+ // u4 high_bytes;
+ // u4 low_bytes;
+ // }
if (DEBUG) {
- System.out.println(index + ":DOUBLE[" + d + "]");
+ long l = dis.readLong();
+ if (DEBUG) {
+ System.out.println(index + ":LONG[" + l + "]");
+ }
}
- } else {
- dis.skip(8);
- }
- return true;
- case CONSTANT_Class:
- // CONSTANT_Class_info {
- // u1 tag;
- // u2 name_index;
- // }
- cpdata[index] = (int) dis.readShort();
- if (DEBUG) {
- System.out.println(index + ":CLASS[name_index=" + cpdata[index] + "]");
- }
- break;
- case CONSTANT_String:
- // CONSTANT_String_info {
- // u1 tag;
- // u2 string_index;
- // }
- if (DEBUG) {
+ else {
+ dis.skip(8);
+ }
+ return true;
+ case CONSTANT_Double:
+ // CONSTANT_Double_info {
+ // u1 tag;
+ // u4 high_bytes;
+ // u4 low_bytes;
+ // }
+ if (DEBUG) {
+ double d = dis.readDouble();
+ if (DEBUG) {
+ System.out.println(index + ":DOUBLE[" + d + "]");
+ }
+ }
+ else {
+ dis.skip(8);
+ }
+ return true;
+ case CONSTANT_Class:
+ // CONSTANT_Class_info {
+ // u1 tag;
+ // u2 name_index;
+ // }
cpdata[index] = (int) dis.readShort();
if (DEBUG) {
- System.out.println(index + ":STRING[string_index=" + cpdata[index] + "]");
+ System.out.println(index + ":CLASS[name_index=" + cpdata[index] + "]");
}
- } else {
- dis.skip(2);
- }
- break;
- case CONSTANT_Fieldref:
- // CONSTANT_Fieldref_info {
- // u1 tag;
- // u2 class_index;
- // u2 name_and_type_index;
- // }
- if (DEBUG) {
- cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ break;
+ case CONSTANT_String:
+ // CONSTANT_String_info {
+ // u1 tag;
+ // u2 string_index;
+ // }
if (DEBUG) {
- System.out.println(index + ":FIELDREF[class_index=" + ((int[]) cpdata[index])[0] + ",name_and_type_index="
- + ((int[]) cpdata[index])[1] + "]");
+ cpdata[index] = (int) dis.readShort();
+ if (DEBUG) {
+ System.out.println(index + ":STRING[string_index=" + cpdata[index] + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_Methodref:
- // CONSTANT_Methodref_info {
- // u1 tag;
- // u2 class_index;
- // u2 name_and_type_index;
- // }
- if (DEBUG) {
- cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ else {
+ dis.skip(2);
+ }
+ break;
+ case CONSTANT_Fieldref:
+ // CONSTANT_Fieldref_info {
+ // u1 tag;
+ // u2 class_index;
+ // u2 name_and_type_index;
+ // }
if (DEBUG) {
- System.out.println(index + ":METHODREF[class_index=" + ((int[]) cpdata[index])[0] + ",name_and_type_index="
- + ((int[]) cpdata[index])[1] + "]");
+ cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ if (DEBUG) {
+ System.out.println(index + ":FIELDREF[class_index=" + ((int[]) cpdata[index])[0]
+ + ",name_and_type_index="
+ + ((int[]) cpdata[index])[1] + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_InterfaceMethodref:
- // CONSTANT_InterfaceMethodref_info {
- // u1 tag;
- // u2 class_index;
- // u2 name_and_type_index;
- // }
- if (DEBUG) {
- cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_Methodref:
+ // CONSTANT_Methodref_info {
+ // u1 tag;
+ // u2 class_index;
+ // u2 name_and_type_index;
+ // }
if (DEBUG) {
- System.out.println(index + ":INTERFACEMETHODREF[class_index=" + ((int[]) cpdata[index])[0]
- + ",name_and_type_index=" + ((int[]) cpdata[index])[1] + "]");
+ cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ if (DEBUG) {
+ System.out.println(index + ":METHODREF[class_index=" + ((int[]) cpdata[index])[0]
+ + ",name_and_type_index="
+ + ((int[]) cpdata[index])[1] + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_NameAndType:
- // The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:
- // CONSTANT_NameAndType_info {
- // u1 tag;
- // u2 name_index;
- // u2 descriptor_index;
- // }
- if (DEBUG) {
- cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_InterfaceMethodref:
+ // CONSTANT_InterfaceMethodref_info {
+ // u1 tag;
+ // u2 class_index;
+ // u2 name_and_type_index;
+ // }
if (DEBUG) {
- System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0] + ",descriptor_index="
- + ((int[]) cpdata[index])[1] + "]");
+ cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ if (DEBUG) {
+ System.out.println(index + ":INTERFACEMETHODREF[class_index=" + ((int[]) cpdata[index])[0]
+ + ",name_and_type_index=" + ((int[]) cpdata[index])[1] + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- default:
- throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_NameAndType:
+ // The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:
+ // CONSTANT_NameAndType_info {
+ // u1 tag;
+ // u2 name_index;
+ // u2 descriptor_index;
+ // }
+ if (DEBUG) {
+ cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ if (DEBUG) {
+ System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0]
+ + ",descriptor_index="
+ + ((int[]) cpdata[index])[1] + "]");
+ }
+ }
+ else {
+ dis.skip(4);
+ }
+ break;
+ default:
+ throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
}
return false;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker2.java b/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker2.java
index e9e1ab8..9b728fe 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker2.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ConstantPoolChecker2.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.io.ByteArrayInputStream;
@@ -27,10 +28,11 @@ import java.util.List;
// http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
/**
- * Enables us to check things quickly in the constant pool. This version accumulates the class references and the method references,
- * for classes that start with 'j' (we want to catch: java/lang). It skips everything it can and the end result is a list of class
- * references and a list of method references. The former look like this 'a/b/C' whilst the latter look like this
- * 'java/lang/Foo.bar' (the descriptor for the method is not included). Interface methods are skipped.
+ * Enables us to check things quickly in the constant pool. This version accumulates the class references and the method
+ * references, for classes that start with 'j' (we want to catch: java/lang). It skips everything it can and the end
+ * result is a list of class references and a list of method references. The former look like this 'a/b/C' whilst the
+ * latter look like this 'java/lang/Foo.bar' (the descriptor for the method is not included). Interface methods are
+ * skipped.
*
* @author Andy Clement
* @since 0.7.3
@@ -40,18 +42,31 @@ public class ConstantPoolChecker2 {
private static final boolean DEBUG = false;
private final static byte CONSTANT_Utf8 = 1;
+
private final static byte CONSTANT_Integer = 3;
+
private final static byte CONSTANT_Float = 4;
+
private final static byte CONSTANT_Long = 5;
+
private final static byte CONSTANT_Double = 6;
+
private final static byte CONSTANT_Class = 7;
+
private final static byte CONSTANT_String = 8;
+
private final static byte CONSTANT_Fieldref = 9;
+
private final static byte CONSTANT_Methodref = 10;
+
private final static byte CONSTANT_InterfaceMethodref = 11;
+
private final static byte CONSTANT_NameAndType = 12;
+
private final static byte CONSTANT_MethodHandle = 15;
+
private final static byte CONSTANT_MethodType = 16;
+
private final static byte CONSTANT_InvokeDynamic = 18;
// Test entry point just goes through all the code in the bin folder
@@ -68,7 +83,8 @@ public class ConstantPoolChecker2 {
for (File f : fs) {
if (f.isDirectory()) {
checkThemAll(f.listFiles());
- } else if (f.getName().endsWith(".class")) {
+ }
+ else if (f.getName().endsWith(".class")) {
System.out.println(f);
byte[] data = Utils.loadFromStream(new FileInputStream(f));
long stime = System.nanoTime();
@@ -107,8 +123,11 @@ public class ConstantPoolChecker2 {
}
static class References {
+
String slashedClassName;
+
List referencedClasses;
+
List referencedMethods;
References(String slashedClassName, List rc, List rm) {
@@ -120,11 +139,16 @@ public class ConstantPoolChecker2 {
// Filled with strings and int[]
private Object[] cpdata;
+
private int cpsize;
+
private int[] type;
+
// Does not need to be a set as there are no dups in the ConstantPool (for a class from a decent compiler...)
private List referencedClasses = new ArrayList();
+
private List referencedMethods = new ArrayList();
+
private String slashedclassname;
private ConstantPoolChecker2(byte[] bytes) {
@@ -135,27 +159,27 @@ public class ConstantPoolChecker2 {
public void computeReferences() {
for (int i = 0; i < cpsize; i++) {
switch (type[i]) {
- case CONSTANT_Class:
- int classindex = ((Integer) cpdata[i]);
- String classname = (String) cpdata[classindex];
- if (classname == null) {
- throw new IllegalStateException();
- }
- referencedClasses.add(classname);
- break;
- case CONSTANT_Methodref:
- int[] indexes = (int[]) cpdata[i];
- int classindex2 = indexes[0];
- int nameAndTypeIndex = indexes[1];
- StringBuilder s = new StringBuilder();
- String theClassName = (String) cpdata[(Integer) cpdata[classindex2]];
- if (theClassName.charAt(0) == 'j') {
- s.append(theClassName);
- s.append(".");
- s.append((String) cpdata[(Integer) cpdata[nameAndTypeIndex]]);
- referencedMethods.add(s.toString());
- }
- break;
+ case CONSTANT_Class:
+ int classindex = ((Integer) cpdata[i]);
+ String classname = (String) cpdata[classindex];
+ if (classname == null) {
+ throw new IllegalStateException();
+ }
+ referencedClasses.add(classname);
+ break;
+ case CONSTANT_Methodref:
+ int[] indexes = (int[]) cpdata[i];
+ int classindex2 = indexes[0];
+ int nameAndTypeIndex = indexes[1];
+ StringBuilder s = new StringBuilder();
+ String theClassName = (String) cpdata[(Integer) cpdata[classindex2]];
+ if (theClassName.charAt(0) == 'j') {
+ s.append(theClassName);
+ s.append(".");
+ s.append((String) cpdata[(Integer) cpdata[nameAndTypeIndex]]);
+ referencedMethods.add(s.toString());
+ }
+ break;
// private final static byte CONSTANT_Utf8 = 1;
// private final static byte CONSTANT_Integer = 3;
// private final static byte CONSTANT_Float = 4;
@@ -195,7 +219,8 @@ public class ConstantPoolChecker2 {
int thisclassname = dis.readShort();
int classindex = ((Integer) cpdata[thisclassname]);
slashedclassname = (String) cpdata[classindex];
- } catch (Exception e) {
+ }
+ catch (Exception e) {
throw new IllegalStateException("Unexpected problem processing bytes for class", e);
}
}
@@ -203,162 +228,172 @@ public class ConstantPoolChecker2 {
private boolean processConstantPoolEntry(int index, DataInputStream dis) throws IOException {
byte b = dis.readByte();
switch (b) {
- case CONSTANT_Utf8:
- // CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
- cpdata[index] = dis.readUTF();
- // type[index] = b;
- if (DEBUG) {
- System.out.println(index + ":UTF8[" + cpdata[index] + "]");
- }
- break;
- case CONSTANT_Integer:
- // CONSTANT_Integer_info { u1 tag; u4 bytes; }
- if (DEBUG) {
- int i = dis.readInt();
+ case CONSTANT_Utf8:
+ // CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
+ cpdata[index] = dis.readUTF();
+ // type[index] = b;
if (DEBUG) {
- System.out.println(index + ":INTEGER[" + i + "]");
+ System.out.println(index + ":UTF8[" + cpdata[index] + "]");
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_Float:
- // CONSTANT_Float_info { u1 tag; u4 bytes; }
- if (DEBUG) {
- float f = dis.readFloat();
+ break;
+ case CONSTANT_Integer:
+ // CONSTANT_Integer_info { u1 tag; u4 bytes; }
if (DEBUG) {
- System.out.println(index + ":FLOAT[" + f + "]");
+ int i = dis.readInt();
+ if (DEBUG) {
+ System.out.println(index + ":INTEGER[" + i + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_Long:
- // CONSTANT_Long_info {
- // u1 tag;
- // u4 high_bytes;
- // u4 low_bytes;
- // }
- if (DEBUG) {
- long l = dis.readLong();
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_Float:
+ // CONSTANT_Float_info { u1 tag; u4 bytes; }
if (DEBUG) {
- System.out.println(index + ":LONG[" + l + "]");
+ float f = dis.readFloat();
+ if (DEBUG) {
+ System.out.println(index + ":FLOAT[" + f + "]");
+ }
}
- } else {
- dis.skip(8);
- }
- return true;
- case CONSTANT_Double:
- // CONSTANT_Double_info {
- // u1 tag;
- // u4 high_bytes;
- // u4 low_bytes;
- // }
- if (DEBUG) {
- double d = dis.readDouble();
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_Long:
+ // CONSTANT_Long_info {
+ // u1 tag;
+ // u4 high_bytes;
+ // u4 low_bytes;
+ // }
if (DEBUG) {
- System.out.println(index + ":DOUBLE[" + d + "]");
+ long l = dis.readLong();
+ if (DEBUG) {
+ System.out.println(index + ":LONG[" + l + "]");
+ }
}
- } else {
- dis.skip(8);
- }
- return true;
- case CONSTANT_Class:
- // CONSTANT_Class_info { u1 tag; u2 name_index; }
- type[index] = b;
- cpdata[index] = (int) dis.readShort();
- if (DEBUG) {
- System.out.println(index + ":CLASS[name_index=" + cpdata[index] + "]");
- }
- break;
- case CONSTANT_String:
- // CONSTANT_String_info { u1 tag; u2 string_index; }
- if (DEBUG) {
+ else {
+ dis.skip(8);
+ }
+ return true;
+ case CONSTANT_Double:
+ // CONSTANT_Double_info {
+ // u1 tag;
+ // u4 high_bytes;
+ // u4 low_bytes;
+ // }
+ if (DEBUG) {
+ double d = dis.readDouble();
+ if (DEBUG) {
+ System.out.println(index + ":DOUBLE[" + d + "]");
+ }
+ }
+ else {
+ dis.skip(8);
+ }
+ return true;
+ case CONSTANT_Class:
+ // CONSTANT_Class_info { u1 tag; u2 name_index; }
+ type[index] = b;
cpdata[index] = (int) dis.readShort();
if (DEBUG) {
- System.out.println(index + ":STRING[string_index=" + cpdata[index] + "]");
+ System.out.println(index + ":CLASS[name_index=" + cpdata[index] + "]");
}
- } else {
- dis.skip(2);
- }
- break;
- case CONSTANT_Fieldref:
- // CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
- if (DEBUG) {
+ break;
+ case CONSTANT_String:
+ // CONSTANT_String_info { u1 tag; u2 string_index; }
+ if (DEBUG) {
+ cpdata[index] = (int) dis.readShort();
+ if (DEBUG) {
+ System.out.println(index + ":STRING[string_index=" + cpdata[index] + "]");
+ }
+ }
+ else {
+ dis.skip(2);
+ }
+ break;
+ case CONSTANT_Fieldref:
+ // CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
+ if (DEBUG) {
+ cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ if (DEBUG) {
+ System.out.println(index + ":FIELDREF[class_index=" + ((int[]) cpdata[index])[0]
+ + ",name_and_type_index="
+ + ((int[]) cpdata[index])[1] + "]");
+ }
+ }
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_Methodref:
+ // CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
+ type[index] = b;
+ //if (DEBUG) {
cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
if (DEBUG) {
- System.out.println(index + ":FIELDREF[class_index=" + ((int[]) cpdata[index])[0] + ",name_and_type_index="
+ System.out.println(index + ":METHODREF[class_index=" + ((int[]) cpdata[index])[0]
+ + ",name_and_type_index="
+ ((int[]) cpdata[index])[1] + "]");
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_Methodref:
- // CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
- type[index] = b;
- //if (DEBUG) {
- cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
- if (DEBUG) {
- System.out.println(index + ":METHODREF[class_index=" + ((int[]) cpdata[index])[0] + ",name_and_type_index="
- + ((int[]) cpdata[index])[1] + "]");
- }
- // } else {
- // dis.skip(4);
- // }
- break;
- case CONSTANT_InterfaceMethodref:
- // CONSTANT_InterfaceMethodref_info {
- // u1 tag;
- // u2 class_index;
- // u2 name_and_type_index;
- // }
- if (DEBUG) {
- cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ // } else {
+ // dis.skip(4);
+ // }
+ break;
+ case CONSTANT_InterfaceMethodref:
+ // CONSTANT_InterfaceMethodref_info {
+ // u1 tag;
+ // u2 class_index;
+ // u2 name_and_type_index;
+ // }
if (DEBUG) {
- System.out.println(index + ":INTERFACEMETHODREF[class_index=" + ((int[]) cpdata[index])[0]
- + ",name_and_type_index=" + ((int[]) cpdata[index])[1] + "]");
+ cpdata[index] = new int[] { dis.readShort(), dis.readShort() };
+ if (DEBUG) {
+ System.out.println(index + ":INTERFACEMETHODREF[class_index=" + ((int[]) cpdata[index])[0]
+ + ",name_and_type_index=" + ((int[]) cpdata[index])[1] + "]");
+ }
}
- } else {
- dis.skip(4);
- }
- break;
- case CONSTANT_NameAndType:
- // The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:
- // CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }
- // type[index] = b;
- cpdata[index] = (int) dis.readShort();// new int[] { dis.readShort(), dis.readShort() };
- dis.skip(2); // skip the descriptor for now
- if (DEBUG) {
- System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0] + ",descriptor_index="
- + ((int[]) cpdata[index])[1] + "]");
- }
- break;
- case CONSTANT_InvokeDynamic:
- //CONSTANT_InvokeDynamic_info {
- // u1 tag;
- // u2 bootstrap_method_attr_index;
- // u2 name_and_type_index;
- //}
- dis.skipBytes(4);
- break;
- case CONSTANT_MethodHandle:
- //CONSTANT_MethodHandle_info {
- // u1 tag;
- // u1 reference_kind;
- // u2 reference_index;
- //}
- dis.skipBytes(3);
- break;
- case CONSTANT_MethodType:
- //CONSTANT_MethodType_info {
- // u1 tag;
- // u2 descriptor_index;
- //}
- dis.skipBytes(2);
- break;
- default:
- throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
+ else {
+ dis.skip(4);
+ }
+ break;
+ case CONSTANT_NameAndType:
+ // The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:
+ // CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }
+ // type[index] = b;
+ cpdata[index] = (int) dis.readShort();// new int[] { dis.readShort(), dis.readShort() };
+ dis.skip(2); // skip the descriptor for now
+ if (DEBUG) {
+ System.out.println(index + ":NAMEANDTYPE[name_index=" + ((int[]) cpdata[index])[0]
+ + ",descriptor_index="
+ + ((int[]) cpdata[index])[1] + "]");
+ }
+ break;
+ case CONSTANT_InvokeDynamic:
+ //CONSTANT_InvokeDynamic_info {
+ // u1 tag;
+ // u2 bootstrap_method_attr_index;
+ // u2 name_and_type_index;
+ //}
+ dis.skipBytes(4);
+ break;
+ case CONSTANT_MethodHandle:
+ //CONSTANT_MethodHandle_info {
+ // u1 tag;
+ // u1 reference_kind;
+ // u2 reference_index;
+ //}
+ dis.skipBytes(3);
+ break;
+ case CONSTANT_MethodType:
+ //CONSTANT_MethodType_info {
+ // u1 tag;
+ // u2 descriptor_index;
+ //}
+ dis.skipBytes(2);
+ break;
+ default:
+ throw new IllegalStateException("Entry: " + index + " " + Byte.toString(b));
}
return false;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/Constants.java b/springloaded/src/main/java/org/springsource/loaded/Constants.java
index 7dad0a9..7c557b4 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Constants.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Constants.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Field;
@@ -30,12 +31,19 @@ import org.objectweb.asm.Opcodes;
public interface Constants extends Opcodes {
public static final Integer DEFAULT_INT = Integer.valueOf(0);
+
public static final Byte DEFAULT_BYTE = Byte.valueOf((byte) 0);
+
public static final Character DEFAULT_CHAR = Character.valueOf((char) 0);
+
public static final Short DEFAULT_SHORT = Short.valueOf((short) 0);
+
public static final Long DEFAULT_LONG = Long.valueOf(0);
+
public static final Float DEFAULT_FLOAT = Float.valueOf(0);
+
public static final Double DEFAULT_DOUBLE = Double.valueOf(0);
+
public static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
static String magicDescriptorForGeneratedCtors = "org.springsource.loaded.C";
@@ -44,74 +52,111 @@ public interface Constants extends Opcodes {
static String PREFIX = "r$";
static String tRegistryType = "org/springsource/loaded/TypeRegistry";
+
static String lRegistryType = "L" + tRegistryType + ";";
static String tDynamicallyDispatchable = "org/springsource/loaded/__DynamicallyDispatchable";
+
static String lDynamicallyDispatchable = "L" + tDynamicallyDispatchable + ";";
static String tReloadableType = "org/springsource/loaded/ReloadableType";
+
static String lReloadableType = "L" + tReloadableType + ";";
static String tInstanceStateManager = "org/springsource/loaded/ISMgr";
+
static String lInstanceStateManager = "L" + tInstanceStateManager + ";";
static String tStaticStateManager = "org/springsource/loaded/SSMgr";
+
static String lStaticStateManager = "L" + tStaticStateManager + ";";
static String fReloadableTypeFieldName = PREFIX + "type";
// Static field holding map and accessors
static String fStaticFieldsName = PREFIX + "sfields";
+
static String mStaticFieldSetterName = PREFIX + "sets";
+
static String mStaticFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)V";
+
static String mStaticFieldGetterName = PREFIX + "gets";
// Instance field holding map and accessors
static String fInstanceFieldsName = PREFIX + "fields";
+
static String mInstanceFieldSetterName = PREFIX + "set";
+
static String mInstanceFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V";
+
static String mInstanceFieldGetterName = PREFIX + "get";
+
static String mInstanceFieldGetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
static String mStaticFieldInterceptionRequired = "staticFieldInterceptionRequired";
+
static String mInstanceFieldInterceptionRequired = "instanceFieldInterceptionRequired";
+
// method called to see if the target of what is about to be called has changed
static String mChangedForInvocationName = "anyChanges";
+
static String mChangedForInvokeStaticName = "istcheck";
+
static String mChangedForInvokeInterfaceName = "iincheck";
+
static String mChangedForInvokeDynamicName = "idycheck";
+
static String mChangedForInvokeVirtualName = "ivicheck";
+
static String mChangedForInvokeSpecialName = "ispcheck";
+
static String mPerformInvokeDynamicName = "idyrun";
+
static String descriptorChangedForInvokeSpecialName = "(ILjava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;";
+
static String mChangedForConstructorName = "ccheck";
static int WAS_INVOKESTATIC = 0x0001;
+
static int WAS_INVOKEVIRTUAL = 0x0002;
// Dynamic dispatch method
static String mDynamicDispatchName = "__execute";
+
static String mDynamicDispatchDescriptor = "([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
+
static String mInitializerName = "___init___";
+
static String mStaticInitializerName = "___clinit___";
static int ACC_PUBLIC_ABSTRACT = Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT;
+
static int ACC_PRIVATE_STATIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
+
static int ACC_PUBLIC_STATIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
+
static int ACC_PUBLIC_STATIC_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+
static int ACC_PUBLIC_INTERFACE = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT;
+
static int ACC_PUBLIC_STATIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
+
static int ACC_PUBLIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PUBLIC_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
+
static int ACC_PUBLIC_PRIVATE_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
+
static int ACC_PRIVATE_STATIC_SYNTHETIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
+
static int ACC_PRIVATE_PROTECTED = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
static int ACC_PRIVATE_STATIC_FINAL = ACC_FINAL | ACC_STATIC | ACC_PRIVATE;
static String[] NO_STRINGS = new String[0];
+
static Method[] NO_METHODS = new Method[0];
+
static Field[] NO_FIELDS = new Field[0];
//Name pattern used to recognise names of Executor classes.
@@ -121,54 +166,95 @@ public interface Constants extends Opcodes {
//
public static int JLC_GETDECLAREDFIELDS = 0x0001;
+
public static int JLC_GETDECLAREDFIELD = 0x0002;
+
public static int JLC_GETFIELD = 0x0004;
+
public static int JLC_GETDECLAREDMETHODS = 0x0008;
+
public static int JLC_GETDECLAREDMETHOD = 0x0010;
+
public static int JLC_GETMETHOD = 0x0020;
+
public static int JLC_GETDECLAREDCONSTRUCTOR = 0x0040;
+
public static int JLC_GETMODIFIERS = 0x0080;
+
public static int JLC_GETMETHODS = 0x0100;
+
public static int JLC_GETCONSTRUCTOR = 0x0200;
+
public static int JLC_GETDECLAREDCONSTRUCTORS = 0x0400;
+
public static int JLRM_INVOKE = 0x0800;
+
public static int JLRF_GET = 0x1000;
+
public static int JLRF_GETLONG = 0x2000;
+
public static int JLOS_HASSTATICINITIALIZER = 0x4000;
// For rewritten reflection in system classes, these are used:
// The member names are used for fields *and* methods
static final String jlcgdfs = "__sljlcgdfs";
+
static final String jlcgdfsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Field;";
+
static final String jlcgdf = "__sljlcgdf";
+
static final String jlcgdfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
+
static final String jlcgf = "__sljlcgf";
+
static final String jlcgfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
+
static final String jlcgdms = "__sljlcgdms";
+
static final String jlcgdmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
+
static final String jlcgdm = "__sljlcgdm";
+
static final String jlcgdmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
+
static final String jlcgm = "__sljlcgm";
+
static final String jlcgmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
+
static final String jlcgdc = "__sljlcgdc";
+
static final String jlcgdcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
+
static final String jlcgc = "__sljlcgc";
+
static final String jlcgcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
+
static final String jlcgmods = "__sljlcgmods";
+
static final String jlcgmodsDescriptor = "(Ljava/lang/Class;)I";
+
static final String jlcgms = "__sljlcgms";
+
static final String jlcgmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
-
+
// TODO migrate those above to this slightly more comprehensible format
static final String jlcGetDeclaredConstructorsMember = "__sljlcgdcs";
+
static final String jlcGetDeclaredConstructorsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;";
+
static final String jlrmInvokeMember = "__sljlrmi";
+
static final String jlrmInvokeDescriptor = "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
+
static final String jlrfGetMember = "__sljlrfg";
+
static final String jlrfGetDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;";
+
static final String jlrfGetLongMember = "__sljlrfgl";
+
static final String jlrfGetLongDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J";
+
static final String jloObjectStream_hasInitializerMethod = "__sljlos_him";
-
+
static final String methodSuffixSuperDispatcher = "_$superdispatcher$";
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java b/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java
index b2ea178..e5d04f0 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.objectweb.asm.Label;
@@ -25,6 +26,7 @@ import org.objectweb.asm.MethodVisitor;
class ConstructorCopier extends MethodVisitor implements Constants {
private final static int preInvokeSpecial = 0;
+
private final static int postInvokeSpecial = 1;
// It is important to know when an INVOKESPECIAL is hit, whether it is our actual one that delegates to the super or just
@@ -32,13 +34,17 @@ class ConstructorCopier extends MethodVisitor implements Constants {
// how many unitialized objects there are (count the NEWs) and how many INVOKESPECIALs have occurred, it is possible
// to identify the right one.
private int state = preInvokeSpecial;
+
private int unitializedObjectsCount = 0;
+
private TypeDescriptor typeDescriptor;
+
private String suffix;
+
private String classname;
public ConstructorCopier(MethodVisitor mv, TypeDescriptor typeDescriptor, String suffix, String classname) {
- super(ASM5,mv);
+ super(ASM5, mv);
this.typeDescriptor = typeDescriptor;
this.suffix = suffix;
this.classname = classname;
@@ -49,7 +55,8 @@ class ConstructorCopier extends MethodVisitor implements Constants {
// Rename 'this' to 'thiz' in executor otherwise Eclipse debugger will fail (static method with 'this')
if (index == 0 && name.equals("this")) {
super.visitLocalVariable("thiz", desc, signature, start, end, index);
- } else {
+ }
+ else {
super.visitLocalVariable(name, desc, signature, start, end, index);
}
}
@@ -74,13 +81,15 @@ class ConstructorCopier extends MethodVisitor implements Constants {
if (opcode == INVOKESPECIAL && name.charAt(0) == '<') {
if (unitializedObjectsCount != 0) {
unitializedObjectsCount--;
- } else {
+ }
+ else {
// This looks like our INVOKESPECIAL
if (state == preInvokeSpecial) {
// special case for calling jlObject, do nothing!
if (owner.equals("java/lang/Object")) {
mv.visitInsn(POP);
- } else {
+ }
+ else {
// Need to replace this INVOKESPECIAL call.
String supertypename = typeDescriptor.getSupertypeName();
ReloadableType superRtype = typeDescriptor.getReloadableType().getTypeRegistry()
@@ -101,10 +110,12 @@ class ConstructorCopier extends MethodVisitor implements Constants {
}
Utils.insertPopsForAllParameters(mv, desc);
mv.visitInsn(POP); // pop 'this'
- } else {
+ }
+ else {
// Check the original form of the supertype for a constructor to call
- MethodMember existingCtor = (superRtype == null ? null : superRtype.getTypeDescriptor().getConstructor(
- desc));
+ MethodMember existingCtor = (superRtype == null ? null
+ : superRtype.getTypeDescriptor().getConstructor(
+ desc));
if (existingCtor == null) {
// It did not exist in the original supertype version, need to use dynamic dispatch method
// collapse the arguments on the stack
@@ -114,12 +125,15 @@ class ConstructorCopier extends MethodVisitor implements Constants {
mv.visitInsn(DUP_X1);
// no stack is instance then params then instance
mv.visitLdcInsn("" + desc);
- mv.visitMethodInsn(INVOKESPECIAL, typeDescriptor.getSupertypeName(), mDynamicDispatchName,
+ mv.visitMethodInsn(INVOKESPECIAL, typeDescriptor.getSupertypeName(),
+ mDynamicDispatchName,
mDynamicDispatchDescriptor, false);
mv.visitInsn(POP);
- } else {
+ }
+ else {
// it did exist in the original, so there will be parallel constructor
- mv.visitMethodInsn(INVOKESPECIAL, typeDescriptor.getSupertypeName(), mInitializerName, desc, false);
+ mv.visitMethodInsn(INVOKESPECIAL, typeDescriptor.getSupertypeName(), mInitializerName,
+ desc, false);
}
}
}
@@ -136,7 +150,8 @@ class ConstructorCopier extends MethodVisitor implements Constants {
// leaving the invokespecial alone will cause a verify error
String descriptor = Utils.insertExtraParameter(owner, desc);
super.visitMethodInsn(INVOKESTATIC, Utils.getExecutorName(classname, suffix), name, descriptor, false);
- } else {
+ }
+ else {
boolean done = false;
// TODO dup of code in method copier - can we refactor?
if (opcode == INVOKESTATIC) {
@@ -151,4 +166,4 @@ class ConstructorCopier extends MethodVisitor implements Constants {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/CurrentLiveVersion.java b/springloaded/src/main/java/org/springsource/loaded/CurrentLiveVersion.java
index 83fddee..ef0c8dc 100644
--- a/springloaded/src/main/java/org/springsource/loaded/CurrentLiveVersion.java
+++ b/springloaded/src/main/java/org/springsource/loaded/CurrentLiveVersion.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Field;
@@ -45,24 +46,31 @@ public class CurrentLiveVersion {
public final IncrementalTypeDescriptor incrementalTypeDescriptor;
String dispatcherName;
+
byte[] dispatcher;
+
Class> dispatcherClass;
+
Object dispatcherInstance;
String executorName;
+
byte[] executor;
+
Class> executorClass;
TypeDelta typeDelta;
private Method staticInitializer;
+
private boolean haveLookedForStaticInitializer;
public boolean staticInitializedNeedsRerunningOnDefine = false;
public CurrentLiveVersion(ReloadableType reloadableType, String versionstamp, byte[] newbytedata) {
if (GlobalConfiguration.logging && log.isLoggable(Level.FINER)) {
- log.entering("CurrentLiveVersion", "", " new version of " + reloadableType.getName() + " loaded, version stamp '"
+ log.entering("CurrentLiveVersion", "", " new version of " + reloadableType.getName()
+ + " loaded, version stamp '"
+ versionstamp + "'");
}
this.reloadableType = reloadableType;
@@ -71,19 +79,22 @@ public class CurrentLiveVersion {
if (GlobalConfiguration.assertsMode) {
if (!this.typeDescriptor.getName().equals(reloadableType.typedescriptor.getName())) {
- throw new IllegalStateException("New version has wrong name. Expected " + reloadableType.typedescriptor.getName()
+ throw new IllegalStateException("New version has wrong name. Expected "
+ + reloadableType.typedescriptor.getName()
+ " but was " + typeDescriptor.getName());
}
}
- newbytedata = GlobalConfiguration.callsideRewritingOn ? MethodInvokerRewriter.rewrite(reloadableType.typeRegistry,
+ newbytedata = GlobalConfiguration.callsideRewritingOn ? MethodInvokerRewriter.rewrite(
+ reloadableType.typeRegistry,
newbytedata) : newbytedata;
this.incrementalTypeDescriptor = new IncrementalTypeDescriptor(reloadableType.typedescriptor);
this.incrementalTypeDescriptor.setLatestTypeDescriptor(this.typeDescriptor);
// Executors for interfaces simply hold annotations
- this.executor = reloadableType.getTypeRegistry().executorBuilder.createFor(reloadableType, versionstamp, typeDescriptor,
+ this.executor = reloadableType.getTypeRegistry().executorBuilder.createFor(reloadableType, versionstamp,
+ typeDescriptor,
newbytedata);
if (GlobalConfiguration.classesToDump != null
@@ -100,8 +111,8 @@ public class CurrentLiveVersion {
}
/**
- * Defines this version. Called up front but can also be called later if the ChildClassLoader in a type registry is discarded
- * and recreated.
+ * Defines this version. Called up front but can also be called later if the ChildClassLoader in a type registry is
+ * discarded and recreated.
*/
public void define() {
staticInitializer = null;
@@ -109,31 +120,37 @@ public class CurrentLiveVersion {
if (!typeDescriptor.isInterface()) {
try {
dispatcherClass = reloadableType.typeRegistry.defineClass(dispatcherName, dispatcher, false);
- } catch (RuntimeException t) {
+ }
+ catch (RuntimeException t) {
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
if (t.getMessage().indexOf("duplicate class definition") == -1) {
throw t;
- } else {
+ }
+ else {
t.printStackTrace();
}
}
}
try {
executorClass = reloadableType.typeRegistry.defineClass(executorName, executor, false);
- } catch (RuntimeException t) {
+ }
+ catch (RuntimeException t) {
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
if (t.getMessage().indexOf("duplicate class definition") == -1) {
throw t;
- } else {
+ }
+ else {
t.printStackTrace();
}
}
if (!typeDescriptor.isInterface()) {
try {
dispatcherInstance = dispatcherClass.newInstance();
- } catch (InstantiationException e) {
+ }
+ catch (InstantiationException e) {
throw new RuntimeException("Unable to build dispatcher class instance", e);
- } catch (IllegalAccessException e) {
+ }
+ catch (IllegalAccessException e) {
throw new RuntimeException("Unable to build dispatcher class instance", e);
}
}
@@ -160,7 +177,8 @@ public class CurrentLiveVersion {
//What to search for:
if (methodMember.isConstructor()) {
name = Constants.mInitializerName;
- } else {
+ }
+ else {
name = methodMember.getName();
}
executorDescriptor = getExecutorDescriptor(methodMember);
@@ -185,13 +203,15 @@ public class CurrentLiveVersion {
System.arraycopy(params, 0, newParametersArray, 1, params.length);
newParametersArray[0] = Type.getType(reloadableType.getClazz());
}
- String executorDescriptor = Type.getMethodDescriptor(Type.getReturnType(methodMember.getDescriptor()), newParametersArray);
+ String executorDescriptor = Type.getMethodDescriptor(Type.getReturnType(methodMember.getDescriptor()),
+ newParametersArray);
return executorDescriptor;
}
@Override
public String toString() {
- return "CurrentLiveVersion [reloadableType=" + reloadableType + ", typeDescriptor=" + typeDescriptor + ", versionstamp="
+ return "CurrentLiveVersion [reloadableType=" + reloadableType + ", typeDescriptor=" + typeDescriptor
+ + ", versionstamp="
+ versionstamp + ", dispatcherName=" + dispatcherName + ", executorName=" + executorName + "]";
}
@@ -277,7 +297,8 @@ public class CurrentLiveVersion {
if (!haveLookedForStaticInitializer) {
try {
staticInitializer = this.getExecutorClass().getDeclaredMethod(Constants.mStaticInitializerName);
- } catch (NoSuchMethodException e) {
+ }
+ catch (NoSuchMethodException e) {
// some types don't have a static initializer, that is OK
}
haveLookedForStaticInitializer = true;
@@ -285,8 +306,10 @@ public class CurrentLiveVersion {
if (staticInitializer != null) {
try {
staticInitializer.invoke(null);
- } catch (Exception e) {
- log.severe("Unexpected exception whilst trying to call the static initializer on " + this.reloadableType.getName());
+ }
+ catch (Exception e) {
+ log.severe("Unexpected exception whilst trying to call the static initializer on "
+ + this.reloadableType.getName());
e.printStackTrace(); // TODO remove when happy
}
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java b/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java
index ff6cf8b..565a249 100644
--- a/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java
+++ b/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.util.ArrayList;
@@ -31,8 +32,8 @@ import org.springsource.loaded.Utils.ReturnType;
/**
- * Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which then
- * delegates to the executor. A new dispatcher (and executor) is built for each class reload.
+ * Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which
+ * then delegates to the executor. A new dispatcher (and executor) is built for each class reload.
*
* @author Andy Clement
* @since 0.5.0
@@ -43,13 +44,16 @@ public class DispatcherBuilder {
* Factory method that builds the dispatcher for a specified reloadabletype.
*
* @param rtype the reloadable type
- * @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to this)
+ * @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to
+ * this)
* @param versionstamp the suffix that should be appended to the generated dispatcher
* @return the bytecode for the new dispatcher
*/
- public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor, String versionstamp) {
+ public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor,
+ String versionstamp) {
ClassReader fileReader = new ClassReader(rtype.interfaceBytes);
- DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor, versionstamp);
+ DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor,
+ versionstamp);
fileReader.accept(dispatcherVisitor, 0);
return dispatcherVisitor.getBytes();
}
@@ -62,9 +66,13 @@ public class DispatcherBuilder {
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
private String classname;
+
private String executorClassName;
+
private String suffix;
+
private ReloadableType rtype;
+
private IncrementalTypeDescriptor typeDescriptor;
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
@@ -80,10 +88,12 @@ public class DispatcherBuilder {
return cw.toByteArray();
}
- public void visit(int version, int flags, String name, String signature, String superclassName, String[] interfaceNames) {
+ public void visit(int version, int flags, String name, String signature, String superclassName,
+ String[] interfaceNames) {
String dispatcherName = Utils.getDispatcherName(classname, suffix);
cw.visit(version, Opcodes.ACC_PUBLIC, dispatcherName, null, "java/lang/Object",
- new String[] { Utils.getInterfaceName(classname), "org/springsource/loaded/__DynamicallyDispatchable" });
+ new String[] { Utils.getInterfaceName(classname),
+ "org/springsource/loaded/__DynamicallyDispatchable" });
generateDefaultConstructor();
}
@@ -123,18 +133,20 @@ public class DispatcherBuilder {
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
- public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
+ public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
+ String[] exceptions) {
if (name.equals(mDynamicDispatchName)) {
generateDynamicDispatchMethod(name, descriptor, signature, exceptions);
- } else if (!name.equals("")) {
+ }
+ else if (!name.equals("")) {
generateRegularMethod(name, descriptor, signature, exceptions);
}
return null;
}
/**
- * Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods that are added
- * to a type after the first time it is defined.
+ * Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods
+ * that are added to a type after the first time it is defined.
*/
private void generateDynamicDispatchMethod(String name, String descriptor, String signature, String[] exceptions) {
final int indexDispatcherInstance = 0;
@@ -144,7 +156,7 @@ public class DispatcherBuilder {
// Should be generating the code for each additional method in
// the executor (new version) that wasn't in the original.
- MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC , name, descriptor, signature, exceptions);
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name, descriptor, signature, exceptions);
mv.visitCode();
// Entries required here for all methods that exist in the new version but didn't exist in the original version
@@ -199,7 +211,8 @@ public class DispatcherBuilder {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, method.name, callDescriptor, false);
if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
- } else if (returnType.isPrimitive()) {
+ }
+ else if (returnType.isPrimitive()) {
Utils.insertBoxInsns(mv, returnType.descriptor);
}
mv.visitInsn(Opcodes.ARETURN);
@@ -262,7 +275,8 @@ public class DispatcherBuilder {
mv.visitVarInsn(ALOAD, indexArgs);
mv.visitVarInsn(ALOAD, indexTarget);
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
- mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName, mDynamicDispatchDescriptor, false);
+ mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName,
+ mDynamicDispatchDescriptor, false);
mv.visitInsn(ARETURN);
// mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
@@ -275,8 +289,8 @@ public class DispatcherBuilder {
}
/**
- * Called to generate the implementation of a normal method on the interface - a normal method is one that did exist when
- * the type was first defined. Might be a catcher.
+ * Called to generate the implementation of a normal method on the interface - a normal method is one that did
+ * exist when the type was first defined. Might be a catcher.
*/
private void generateRegularMethod(String name, String descriptor, String signature, String[] exceptions) {
// The original descriptor is how it was defined on the original type and how it is defined in the executor class.
@@ -289,23 +303,27 @@ public class DispatcherBuilder {
if (name.equals("___init___")) {
// it is a ctor
method = rtype.getConstructor(originalDescriptor);
- } else {
+ }
+ else {
if (isClinit) {
generateClinitDispatcher();
return;
- } else {
+ }
+ else {
// TODO need a better solution that these __
if (name.startsWith("__") && !name.equals("__$swapInit")) { // __$swapInit is the groovy reset method
// clash avoidance name
method = rtype.getMethod(name.substring(2), originalDescriptor);
- } else {
+ }
+ else {
method = rtype.getMethod(name, originalDescriptor);
}
}
}
boolean isStatic = method.isStatic();
- MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature, exceptions);
+ MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature,
+ exceptions);
mv.visitCode();
// The input descriptor will include the extra initial parameter (the instance, or null for static methods)
ReturnType returnTypeDescriptor = Utils.getReturnTypeDescriptor(descriptor);
diff --git a/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java b/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java
index d2024fa..d571e9c 100644
--- a/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.objectweb.asm.AnnotationVisitor;
diff --git a/springloaded/src/main/java/org/springsource/loaded/ExactTypePattern.java b/springloaded/src/main/java/org/springsource/loaded/ExactTypePattern.java
index 9b4d082..b77501c 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ExactTypePattern.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ExactTypePattern.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
diff --git a/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java b/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java
index f3d2f2f..9ec53c3 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Modifier;
@@ -31,16 +32,17 @@ import org.objectweb.asm.Opcodes;
*
* The executor is the class full of static methods that looks very like the original class.
*
- * Methods. For each method in the original type we have a method in the executor, it has the same SourceFile attribute and
- * the same local variable and line number details for debugging to work. Note the first variable will have been renamed from 'this'
- * to 'thiz' to prevent the eclipse debugger crashing. All annotations from the new version will be copied to the methods on an
- * executor.
+ * Methods. For each method in the original type we have a method in the executor, it has the same SourceFile
+ * attribute and the same local variable and line number details for debugging to work. Note the first variable will
+ * have been renamed from 'this' to 'thiz' to prevent the eclipse debugger crashing. All annotations from the new
+ * version will be copied to the methods on an executor.
*
- * Fields. Fields are copied into the executor but only so that there is a place to hang the annotations off (so that they
- * can be accessed through reflection).
+ * Fields. Fields are copied into the executor but only so that there is a place to hang the annotations off (so
+ * that they can be accessed through reflection).
*
- * Constructors. Constructors are added to the executor as ___init___ methods, with the invokespecials within them
- * transformed, either removed if they are calls to Object.<init> or mutated into ___init___ calls on the supertype instance.
+ * Constructors. Constructors are added to the executor as ___init___ methods, with the invokespecials within
+ * them transformed, either removed if they are calls to Object.<init> or mutated into ___init___ calls on the
+ * supertype instance.
*
* @author Andy Clement
* @since 0.5.0
@@ -53,28 +55,32 @@ public class ExecutorBuilder {
this.typeRegistry = typeRegistry;
}
- public byte[] createFor(ReloadableType reloadableType, String versionstamp, TypeDescriptor typeDescriptor, byte[] newVersionData) {
+ public byte[] createFor(ReloadableType reloadableType, String versionstamp, TypeDescriptor typeDescriptor,
+ byte[] newVersionData) {
if (typeDescriptor == null) {
// must be reloadable or we would not be here - so can pass 'true'
typeDescriptor = typeRegistry.getExtractor().extract(newVersionData, true);
}
ClassReader fileReader = new ClassReader(newVersionData);
- ExecutorBuilderVisitor executorVisitor = new ExecutorBuilderVisitor(reloadableType.getSlashedName(), versionstamp,
+ ExecutorBuilderVisitor executorVisitor = new ExecutorBuilderVisitor(reloadableType.getSlashedName(),
+ versionstamp,
typeDescriptor);
fileReader.accept(executorVisitor, 0);
return executorVisitor.getBytes();
}
/**
- * ClassVisitor that constructs the executor by visiting the original class. The basic goal is to visit the original class and
- * 'copy' the methods into the executor, making adjustments as we go.
+ * ClassVisitor that constructs the executor by visiting the original class. The basic goal is to visit the original
+ * class and 'copy' the methods into the executor, making adjustments as we go.
*/
static class ExecutorBuilderVisitor extends ClassVisitor implements Constants {
private ClassWriter cw = new ClassWriter(0);
private String classname;
+
private String suffix;
+
protected TypeDescriptor typeDescriptor;
public ExecutorBuilderVisitor(String classname, String suffix, TypeDescriptor typeDescriptor) {
@@ -88,8 +94,10 @@ public class ExecutorBuilder {
return cw.toByteArray();
}
- public void visit(int version, int flags, String name, String signature, String superclassName, String[] interfaceNames) {
- cw.visit(version, Opcodes.ACC_PUBLIC, Utils.getExecutorName(classname, suffix), null, "java/lang/Object", null);
+ public void visit(int version, int flags, String name, String signature, String superclassName,
+ String[] interfaceNames) {
+ cw.visit(version, Opcodes.ACC_PUBLIC, Utils.getExecutorName(classname, suffix), null, "java/lang/Object",
+ null);
}
// For type level annotation copying
@@ -104,15 +112,18 @@ public class ExecutorBuilder {
}
// For each method, copy it into the new class making appropriate adjustments
- public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
+ public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
+ String[] exceptions) {
if (!Utils.isInitializer(name)) {
// method
if (!Modifier.isStatic(flags)) {
// For non static methods add the extra initial parameter which is 'this'
descriptor = Utils.insertExtraParameter(classname, descriptor);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, name, descriptor, signature, exceptions);
- return new MethodCopier(mv, typeDescriptor.isInterface(), descriptor, typeDescriptor, classname, suffix);
- } else {
+ return new MethodCopier(mv, typeDescriptor.isInterface(), descriptor, typeDescriptor, classname,
+ suffix);
+ }
+ else {
// If this static method would 'clash' with an instance method that has the extra parameter added then
// we have a couple of options to make them different:
// 1. tweak the name
@@ -122,31 +133,37 @@ public class ExecutorBuilder {
name = "__" + name;
}
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, name, descriptor, signature, exceptions);
- return new MethodCopier(mv, typeDescriptor.isInterface(), descriptor, typeDescriptor, classname, suffix);
+ return new MethodCopier(mv, typeDescriptor.isInterface(), descriptor, typeDescriptor, classname,
+ suffix);
}
- } else {
+ }
+ else {
// constructor
if (name.charAt(1) != 'c') {
// regular constructor
// want to create the ___init___ handler for this constructor
-
+
// With the JDT compiler the inner class constructor gets an extra first parameter that is the type of
// containing class. But with javac the inner class constructor gets an extra first parameter that is of
// a special anonymous type (inner class of the containing class)
// For example: class Foo { class Bar {}}
// JDT: ctor in Bar is (Foo) {}
// JAVAC: ctor in Bar is (Foo$1) {}
-
+
descriptor = Utils.insertExtraParameter(classname, descriptor);
-
- MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mInitializerName, descriptor, signature, exceptions);
+
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mInitializerName, descriptor, signature,
+ exceptions);
ConstructorCopier cc = new ConstructorCopier(mv, typeDescriptor, suffix, classname);
return cc;
- } else {
+ }
+ else {
// static initializer
- MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mStaticInitializerName, descriptor, signature, exceptions);
- return new MethodCopier(mv, typeDescriptor.isInterface(), descriptor, typeDescriptor, classname, suffix);
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mStaticInitializerName, descriptor, signature,
+ exceptions);
+ return new MethodCopier(mv, typeDescriptor.isInterface(), descriptor, typeDescriptor, classname,
+ suffix);
}
}
}
@@ -154,7 +171,7 @@ public class ExecutorBuilder {
public void visitSource(String sourcefile, String debug) {
cw.visitSource(sourcefile, debug);//getSMAP(sourcefile));
}
-
+
/**
* Create the SMAP according to the JSR45 spec, *Note* this method is a work in progress not currently used.
*
@@ -162,30 +179,30 @@ public class ExecutorBuilder {
* @return debug extension attribute encoded into a string
*/
public String getSMAP(String sourcefile) {
- System.out.println("Building smap for "+sourcefile);
+ System.out.println("Building smap for " + sourcefile);
StringBuilder s = new StringBuilder();
-
+
// Header
s.append("SMAP\n");
- s.append(sourcefile+"\n"); // name of the generated file
+ s.append(sourcefile + "\n"); // name of the generated file
s.append("SpringLoaded\n"); // Default stratum (Java)
// StratumSection
s.append("*S SpringLoaded\n");
-
+
// FileSection
s.append("*F\n");
- s.append("+ 1 "+sourcefile+"\n");
- s.append("jaapplication1/"+sourcefile+"\n");
-// s.append("1 javaapplication1/"+sourcefile+"\n");
-
+ s.append("+ 1 " + sourcefile + "\n");
+ s.append("jaapplication1/" + sourcefile + "\n");
+ // s.append("1 javaapplication1/"+sourcefile+"\n");
+
// LineSection
s.append("*L\n");
s.append("1#1,1000:1,1\n");
-
+
// EndSection
s.append("*E\n");
-
+
System.out.println(s.toString());
return s.toString();
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/FieldDelta.java b/springloaded/src/main/java/org/springsource/loaded/FieldDelta.java
index 2cfcb7a..503590a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/FieldDelta.java
+++ b/springloaded/src/main/java/org/springsource/loaded/FieldDelta.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
@@ -22,18 +23,24 @@ package org.springsource.loaded;
* @since 0.5.0
*/
public class FieldDelta {
+
public int changed;
private final static int CHANGED_TYPE = 0x0001;
+
private final static int CHANGED_ACCESS = 0x0002;
+
private final static int CHANGED_ANNOTATIONS = 0x0004;
private final static int CHANGED_MASK = CHANGED_TYPE | CHANGED_ACCESS | CHANGED_ANNOTATIONS;
public final String name;
+
// o = original, n = new
String oDesc, nDesc;
+
String annotationChanges;
+
int oAccess, nAccess;
public FieldDelta(String name) {
@@ -77,4 +84,4 @@ public class FieldDelta {
return s.toString();
}
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/FieldMember.java b/springloaded/src/main/java/org/springsource/loaded/FieldMember.java
index 372cbc4..9ca530b 100644
--- a/springloaded/src/main/java/org/springsource/loaded/FieldMember.java
+++ b/springloaded/src/main/java/org/springsource/loaded/FieldMember.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
@@ -24,6 +25,7 @@ package org.springsource.loaded;
public class FieldMember extends AbstractMember {
final static FieldMember[] NONE = new FieldMember[0];
+
String typename;
protected FieldMember(String typename, int modifiers, String name, String descriptor, String signature) {
diff --git a/springloaded/src/main/java/org/springsource/loaded/FieldReaderWriter.java b/springloaded/src/main/java/org/springsource/loaded/FieldReaderWriter.java
index 5c6d88c..e9c1096 100644
--- a/springloaded/src/main/java/org/springsource/loaded/FieldReaderWriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/FieldReaderWriter.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Field;
@@ -23,8 +24,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * Able to read or write a particular field in a type. Knows nothing about the instance upon which the read/write may be getting
- * done.
+ * Able to read or write a particular field in a type. Knows nothing about the instance upon which the read/write may be
+ * getting done.
*
* @author Andy Clement
* @since 0.5.0
@@ -50,12 +51,13 @@ public class FieldReaderWriter {
}
/**
- * Set the value of an instance field on the specified instance to the specified value. If a state manager is passed in things
- * can be done in a more optimal way, otherwise the state manager has to be discovered from the instance.
+ * Set the value of an instance field on the specified instance to the specified value. If a state manager is passed
+ * in things can be done in a more optimal way, otherwise the state manager has to be discovered from the instance.
*
* @param instance the object instance upon which to set the field
* @param newValue the new value for that field
- * @param stateManager the optional state manager for this instance, which will be looked up (expensive) if not passed in
+ * @param stateManager the optional state manager for this instance, which will be looked up (expensive) if not
+ * passed in
* @throws IllegalAccessException if the field value cannot be set
*/
public void setValue(Object instance, Object newValue, ISMgr stateManager) throws IllegalAccessException {
@@ -72,13 +74,16 @@ public class FieldReaderWriter {
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(theField.getName(), newValue);
- } else { // the type is not reloadable, must use reflection to access the value
+ }
+ else { // the type is not reloadable, must use reflection to access the value
// TODO generate get/set in the topmost reloader for these kinds of field and use them?
if (typeDescriptor.isInterface()) {
// field resolution has left us with an interface field, those can't be set like this
- throw new IncompatibleClassChangeError("Expected non-static field " + instance.getClass().getName() + "."
+ throw new IncompatibleClassChangeError("Expected non-static field " + instance.getClass().getName()
+ + "."
+ theField.getName());
- } else {
+ }
+ else {
findAndSetFieldValueInHierarchy(instance, newValue);
}
}
@@ -102,23 +107,26 @@ public class FieldReaderWriter {
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(theField.getName(), newValue);
- } else { // the type is not reloadable, must use reflection to access the value
+ }
+ else { // the type is not reloadable, must use reflection to access the value
try {
Field f = locateFieldByReflection(clazz, typeDescriptor.getDottedName(), typeDescriptor.isInterface(),
theField.getName());
f.setAccessible(true);
f.set(null, newValue);
// cant cache result - we dont control the sets so won't know it is happening anyway
- } catch (Exception e) {
- throw new IllegalStateException("Unexpectedly unable to reflectively set the field " + theField.getName()
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Unexpectedly unable to reflectively set the field "
+ + theField.getName()
+ " on the type " + clazz.getName());
}
}
}
/**
- * Return the value of the field for which is reader-writer exists. To improve performance a fieldAccessor can be supplied but
- * if it is missing the code will go and discover it.
+ * Return the value of the field for which is reader-writer exists. To improve performance a fieldAccessor can be
+ * supplied but if it is missing the code will go and discover it.
*
* @param instance the instance for which the field should be fetched
* @param stateManager an optional state manager containing the map of values (will be discovered if not supplied)
@@ -164,11 +172,14 @@ public class FieldReaderWriter {
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(fieldname, result);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpectedly unable to access field " + fieldname + " on class "
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Unexpectedly unable to access field " + fieldname
+ + " on class "
+ rt.getClazz(), e);
}
- } else {
+ }
+ else {
// The field was not on the original type. As not seen before, can default it
result = Utils.toResultCheckIfNull(null, theField.getDescriptor());
if (typeLevelValues == null) {
@@ -187,14 +198,17 @@ public class FieldReaderWriter {
}
}
result = Utils.toResultCheckIfNull(result, theField.getDescriptor());
- } else {
+ }
+ else {
// the type is not reloadable, must use reflection to access the value.
// TODO measure how often we hit the reflection path, should never happen unless reflection is already on the frame
if (typeDescriptor.isInterface()) { // cant be an instance field if it is found to be on an interface
- throw new IncompatibleClassChangeError("Expected non-static field " + instance.getClass().getName() + "."
+ throw new IncompatibleClassChangeError("Expected non-static field " + instance.getClass().getName()
+ + "."
+ fieldname);
- } else {
+ }
+ else {
result = findAndGetFieldValueInHierarchy(instance);
}
}
@@ -205,7 +219,8 @@ public class FieldReaderWriter {
return result;
}
- public Object getStaticFieldValue(Class> clazz, SSMgr stateManager) throws IllegalAccessException, IllegalArgumentException {
+ public Object getStaticFieldValue(Class> clazz, SSMgr stateManager) throws IllegalAccessException,
+ IllegalArgumentException {
Object result = null;
if (clazz == null) {
throw new IllegalStateException();
@@ -265,11 +280,14 @@ public class FieldReaderWriter {
stateManager.getMap().put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(theField.getName(), result);
- } catch (Exception e) {
- throw new IllegalStateException("Unexpectedly unable to read field " + theField.getName() + " on type "
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Unexpectedly unable to read field " + theField.getName()
+ + " on type "
+ rt.getClazz(), e);
}
- } else {
+ }
+ else {
// The field was not on the original type. As not seen before, can default it
result = Utils.toResultCheckIfNull(null, theField.getDescriptor());
if (typeLevelValues == null) {
@@ -290,7 +308,8 @@ public class FieldReaderWriter {
}
}
result = Utils.toResultCheckIfNull(result, theField.getDescriptor());
- } else { // the type is not reloadable, must use reflection to access the value
+ }
+ else { // the type is not reloadable, must use reflection to access the value
// TODO measure how often this code gets hit - ensure it does not in the common (non reflective) case
try {
Field f = locateFieldByReflection(clazz, typeDescriptor.getDottedName(), typeDescriptor.isInterface(),
@@ -298,8 +317,10 @@ public class FieldReaderWriter {
f.setAccessible(true);
result = f.get(null);
// cant cache result - we dont control the sets so won't know it is happening anyway
- } catch (Exception e) {
- throw new IllegalStateException("Unexpectedly unable to set static field " + theField.getName() + " on type "
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Unexpectedly unable to set static field " + theField.getName()
+ + " on type "
+ typeDescriptor.getDottedName());
}
@@ -346,13 +367,15 @@ public class FieldReaderWriter {
Class> superclass = clazz.getSuperclass();
if (superclass == null) {
return null;
- } else {
+ }
+ else {
return locateFieldByReflection(superclass, typeWanted, isInterface, name);
}
}
/**
- * Discover the instance state manager for the specific object instance. Will fail by exception rather than returning null.
+ * Discover the instance state manager for the specific object instance. Will fail by exception rather than
+ * returning null.
*
* @param instance the object instance on which to look
* @return the discovered state manager
@@ -369,7 +392,7 @@ public class FieldReaderWriter {
// Looks to not have been initialized yet, this can happen if a non standard ctor was used.
// We could push this step into the generated ctors...
ISMgr instanceStateManager = new ISMgr(instance, typeDescriptor.getReloadableType());
- fieldAccessorField.set(instance,instanceStateManager);
+ fieldAccessorField.set(instance, instanceStateManager);
stateManager = (ISMgr) fieldAccessorField.get(instance);
// For some reason it didn't stick!
if (stateManager == null) {
@@ -378,13 +401,16 @@ public class FieldReaderWriter {
}
}
return stateManager;
- } catch (Exception e) {
- throw new IllegalStateException("Unexpectedly unable to find instance state manager on class " + clazz.getName(), e);
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Unexpectedly unable to find instance state manager on class "
+ + clazz.getName(), e);
}
}
/**
- * Discover the static state manager on the specified class and return it. Will fail by exception rather than returning null.
+ * Discover the static state manager on the specified class and return it. Will fail by exception rather than
+ * returning null.
*
* @param clazz the class on which to look
* @return the discovered state manager
@@ -393,7 +419,8 @@ public class FieldReaderWriter {
try {
Field stateManagerField = clazz.getField(Constants.fStaticFieldsName);
if (stateManagerField == null) {
- throw new IllegalStateException("Cant find field accessor for type " + typeDescriptor.getReloadableType().getName());
+ throw new IllegalStateException("Cant find field accessor for type "
+ + typeDescriptor.getReloadableType().getName());
}
SSMgr stateManager = (SSMgr) stateManagerField.get(null);
// Field should always have been initialized - it is done at the start of the top most reloadable type
@@ -401,8 +428,10 @@ public class FieldReaderWriter {
throw new IllegalStateException("Instance of this class has no state manager: " + clazz.getName());
}
return stateManager;
- } catch (Exception e) {
- throw new IllegalStateException("Unexpectedly unable to find static state manager on class " + clazz.getName(), e);
+ }
+ catch (Exception e) {
+ throw new IllegalStateException("Unexpectedly unable to find static state manager on class "
+ + clazz.getName(), e);
}
}
@@ -411,8 +440,8 @@ public class FieldReaderWriter {
}
/**
- * Walk up the instance hierarchy looking for the field, and when it is found access it and return the result. Will exit via
- * exception if it cannot find the field or something goes wrong when accessing it.
+ * Walk up the instance hierarchy looking for the field, and when it is found access it and return the result. Will
+ * exit via exception if it cannot find the field or something goes wrong when accessing it.
*
* @param instance the object instance upon which the field is being accessed
* @return the value of the field
@@ -431,15 +460,16 @@ public class FieldReaderWriter {
Field f = clazz.getDeclaredField(fieldname);
f.setAccessible(true);
return f.get(instance);
- } catch (Exception e) {
+ }
+ catch (Exception e) {
throw new IllegalStateException("Unexpectedly could not access field named " + fieldname + " on class "
+ clazz.getName());
}
}
/**
- * Walk up the instance hierarchy looking for the field, and when it is found set it. Will exit via exception if it cannot find
- * the field or something goes wrong when accessing it.
+ * Walk up the instance hierarchy looking for the field, and when it is found set it. Will exit via exception if it
+ * cannot find the field or something goes wrong when accessing it.
*
* @param instance the object instance upon which the field is being set
* @param newValue the new value for the field
@@ -458,10 +488,11 @@ public class FieldReaderWriter {
Field f = clazz.getDeclaredField(fieldname);
f.setAccessible(true);
f.set(instance, newValue);
- } catch (Exception e) {
+ }
+ catch (Exception e) {
throw new IllegalStateException("Unexpectedly could not access field named " + fieldname + " on class "
+ clazz.getName());
}
}
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/FileChangeListener.java b/springloaded/src/main/java/org/springsource/loaded/FileChangeListener.java
index 41492aa..6521288 100644
--- a/springloaded/src/main/java/org/springsource/loaded/FileChangeListener.java
+++ b/springloaded/src/main/java/org/springsource/loaded/FileChangeListener.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.io.File;
@@ -29,4 +30,4 @@ public interface FileChangeListener {
void register(ReloadableType rtype, File file);
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java b/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java
index fcba3c1..c38d910 100644
--- a/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java
+++ b/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.io.File;
@@ -26,9 +27,9 @@ import java.util.logging.Logger;
import org.springsource.loaded.agent.SpringPlugin;
/**
- * Encapsulates configurable elements - these are set (to values other than the defaults) in TypeRegistry when the system property
- * springloaded configuration is processed. Some of the options are only used by testcases to make the testcases easier to write and
- * more straightforward.
+ * Encapsulates configurable elements - these are set (to values other than the defaults) in TypeRegistry when the
+ * system property springloaded configuration is processed. Some of the options are only used by testcases to make the
+ * testcases easier to write and more straightforward.
*
* @author Andy Clement
* @since 0.5.0
@@ -45,7 +46,8 @@ public class GlobalConfiguration {
public static boolean catchersOn = true;
/**
- * If active, SpringLoaded will be trying to watch for types changing on the file system once they have been made reloadable.
+ * If active, SpringLoaded will be trying to watch for types changing on the file system once they have been made
+ * reloadable.
*/
public static boolean fileSystemMonitoring = false;
@@ -58,15 +60,14 @@ public class GlobalConfiguration {
* verbose mode can trigger extra messages. Enable with 'verbose=true'
*/
public static boolean verboseMode = false;
-
+
/**
* asserts mode will trigger extra checking (performance impact but confirms correctness)
*/
public static boolean assertsMode = false;
-
+
/**
- * Can be turned on to enable users to determine the decision process around why
- * something is not reloadable.
+ * Can be turned on to enable users to determine the decision process around why something is not reloadable.
*/
public static boolean explainMode = false;
@@ -86,8 +87,9 @@ public class GlobalConfiguration {
* Determine whether on disk caching will be used.
*/
public static boolean isCaching = false;
-
+
public static boolean investigateSystemClassReflection = false;
+
public static boolean rewriteAllSystemClasses = false;
/**
@@ -110,29 +112,30 @@ public class GlobalConfiguration {
public static boolean directlyDefineTypes = true;
public final static boolean interceptReflection = true;
-
+
// max number of values before we prevent them being reloaded (the clinit rewrite blows the codesize limit)
public static int enumLimit = 1000;
public static boolean reloadMessages = false;// can be forced on for testing
/**
- * When a reload is attempted, if this is true it will be checked to confirm it is allowed and does not violate the supported
- * reloadable changes that can be made to a type.
+ * When a reload is attempted, if this is true it will be checked to confirm it is allowed and does not violate the
+ * supported reloadable changes that can be made to a type.
*/
public static boolean verifyReloads = true;
/**
- * When classes are dumped by Utils.dump() this specifies where. A null value will cause us to dump into the default temp
- * folder.
+ * When classes are dumped by Utils.dump() this specifies where. A null value will cause us to dump into the default
+ * temp folder.
*/
public static String dumpFolder = null;
/**
- * Global configuration properties set based on the value of system property 'springloaded'. If null then not yet initialized
- * (and a call to initializeFromSystemProperty()) is needed. If settings are truely once per VM, they are set directly in
- * GlobalConfiguration whereas if they may be overridden on a per classloader level, they are set in this properties object and
- * may be overridden by the springloaded.properties files accessible through each classloader.
+ * Global configuration properties set based on the value of system property 'springloaded'. If null then not yet
+ * initialized (and a call to initializeFromSystemProperty()) is needed. If settings are truely once per VM, they
+ * are set directly in GlobalConfiguration whereas if they may be overridden on a per classloader level, they are
+ * set in this properties object and may be overridden by the springloaded.properties files accessible through each
+ * classloader.
*/
public static Properties globalConfigurationProperties;
@@ -150,7 +153,7 @@ public class GlobalConfiguration {
public final static boolean debugplugins;
-
+
private static void printUsage() {
System.out.println("SpringLoaded");
System.out.println("============");
@@ -166,7 +169,7 @@ public class GlobalConfiguration {
System.out.println("Options:");
System.exit(0);
}
-
+
/**
* Look for a springloaded system property and initialize the 'default system wide' configuration based upon it.
* Support configuration options:
@@ -209,21 +212,27 @@ public class GlobalConfiguration {
// if (isRuntimeLogging && log.isLoggable(Level.INFO)) {
// log.info("configuration: interceptReflection = " + interceptReflection);
// }
- } else if (key.equals("cleanCache")) {
+ }
+ else if (key.equals("cleanCache")) {
cleanCache = kv.substring(equals + 1).equalsIgnoreCase("true");
- } else if (key.equals("caching")) {
+ }
+ else if (key.equals("caching")) {
specifiedCaching = true;
isCaching = kv.substring(equals + 1).equalsIgnoreCase("true");
- } else if (key.equals("debugplugins")) {
+ }
+ else if (key.equals("debugplugins")) {
debugPlugins = true;
}
- else if(key.equals("enumlimit")) {
- enumLimit = toInt(kv.substring(equals+1),enumLimit);
- } else if (key.equals("profile")) {
+ else if (key.equals("enumlimit")) {
+ enumLimit = toInt(kv.substring(equals + 1), enumLimit);
+ }
+ else if (key.equals("profile")) {
profile = kv.substring(equals + 1);
- } else if (key.equals("cacheDir")) {
+ }
+ else if (key.equals("cacheDir")) {
cacheDir = kv.substring(equals + 1);
- } else if (key.equals("callsideRewritingOn")) { // global setting
+ }
+ else if (key.equals("callsideRewritingOn")) { // global setting
callsideRewritingOn = kv.substring(equals + 1).equalsIgnoreCase("true");
if (isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("configuration: callsideRewritingOn = " + callsideRewritingOn);
@@ -233,31 +242,38 @@ public class GlobalConfiguration {
// if (isRuntimeLogging && log.isLoggable(Level.INFO)) {
// log.info("configuration: logNonInterceptedReflectiveCalls = " + logNonInterceptedReflectiveCalls);
// }
- } else if (key.equals("verifyReloads")) { // global setting
+ }
+ else if (key.equals("verifyReloads")) { // global setting
verifyReloads = kv.substring(equals + 1).equalsIgnoreCase("true");
if (isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("configuration: verifyReloads = " + verifyReloads);
}
- } else if (key.equals("dumpFolder")) { // global setting
+ }
+ else if (key.equals("dumpFolder")) { // global setting
dumpFolder = kv.substring(equals + 1);
if (isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("configuration: dumpFolder = " + dumpFolder);
}
- } else if (key.equals("maxClassDefinitions")) {
+ }
+ else if (key.equals("maxClassDefinitions")) {
try {
maxClassDefinitions = Integer.parseInt(kv.substring(equals + 1));
if (isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("configuration: maxClassDefinitions = " + maxClassDefinitions);
}
- } catch (NumberFormatException nfe) {
- System.err.println("ERROR: unable to parse " + kv.substring(equals + 1) + " as a integer");
}
- } else if (key.equals("logging")) {
+ catch (NumberFormatException nfe) {
+ System.err.println("ERROR: unable to parse " + kv.substring(equals + 1)
+ + " as a integer");
+ }
+ }
+ else if (key.equals("logging")) {
GlobalConfiguration.isRuntimeLogging = kv.substring(equals + 1).equalsIgnoreCase("true");
GlobalConfiguration.logging = kv.substring(equals + 1).equalsIgnoreCase("true");
System.out.println("Spring-Loaded logging = (" + GlobalConfiguration.isRuntimeLogging + ","
+ GlobalConfiguration.logging + ")");
- } else if (key.equals("verbose")) {
+ }
+ else if (key.equals("verbose")) {
verboseMode = kv.substring(equals + 1).equalsIgnoreCase("true");
reloadMessages = verboseMode;
}
@@ -267,11 +283,14 @@ public class GlobalConfiguration {
else if (key.equals("rebasePaths")) {
// value is a series of "a=b,c=d,e=f" indicating from and to
globalConfigurationProperties.put("rebasePaths", kv.substring(equals + 1));
- } else if (key.equals("inclusions")) {
+ }
+ else if (key.equals("inclusions")) {
globalConfigurationProperties.put("inclusions", kv.substring(equals + 1));
- } else if (key.equals("exclusions")) {
+ }
+ else if (key.equals("exclusions")) {
globalConfigurationProperties.put("exclusions", kv.substring(equals + 1));
- } else if (key.equals("plugins")) {
+ }
+ else if (key.equals("plugins")) {
// plugins=com.myplugin.Plugin,com.somethingelse.SomeOtherPlugin
String pluginList = kv.substring(equals + 1);
StringTokenizer pluginListTokenizer = new StringTokenizer(pluginList, ",");
@@ -280,12 +299,13 @@ public class GlobalConfiguration {
pluginClassnameList.add(pluginListTokenizer.nextToken());
}
}
- } else {
+ }
+ else {
if (kv.equals("?")) {
printUsage();
}
else if (kv.equals("verbose")) {
- Log.log("[verbose mode on] Full configuration is:"+value);
+ Log.log("[verbose mode on] Full configuration is:" + value);
verboseMode = true;
reloadMessages = true;
}
@@ -319,7 +339,8 @@ public class GlobalConfiguration {
cacheDir = new StringBuilder(userhome).append(File.separator).append(".grails").toString();
new File(cacheDir).mkdir();
}
- } catch (Throwable t) {
+ }
+ catch (Throwable t) {
System.err.println("looks like user.home is not set, or cannot write to it: cannot create cache.");
t.printStackTrace(System.err);
}
@@ -336,7 +357,8 @@ public class GlobalConfiguration {
// turn off the 3.0 reloading, for now (just because it hasn't been tested)
SpringPlugin.support305 = false;
}
- } else {
+ }
+ else {
if (isCaching) {
if (cacheDir == null) {
try {
@@ -344,7 +366,8 @@ public class GlobalConfiguration {
if (userhome != null) {
cacheDir = userhome;
}
- } catch (Throwable t) {
+ }
+ catch (Throwable t) {
System.err.println("looks like user.home is not set: cannot create cache.");
t.printStackTrace(System.err);
}
@@ -361,41 +384,45 @@ public class GlobalConfiguration {
System.err.println("Caching deactivated: failed to create cache directory: " + cacheDir);
isCaching = false;
}
- } else {
+ }
+ else {
if (!cacheDirFile.isDirectory()) {
System.err.println("Caching deactivated: unable to use specified cache area, it is not a directory: "
+ cacheDirFile);
isCaching = false;
}
}
- } catch (Exception e) {
+ }
+ catch (Exception e) {
System.err.println("Unexpected problem creating specified cachedir: " + cacheDir);
e.printStackTrace();
}
}
-
+
// Alternative route for specifying values
value = System.getProperty("springloaded.enumlimit");
if (value != null) {
- enumLimit = toInt(value,enumLimit);
+ enumLimit = toInt(value, enumLimit);
}
- } catch (Throwable t) {
+ }
+ catch (Throwable t) {
System.err.println("Unexpected problem reading global configuration setting:" + t.toString());
t.printStackTrace();
}
debugplugins = debugPlugins;
}
-
+
private static int toInt(String value, int defaultValue) {
try {
return Integer.parseInt(value);
- } catch (NumberFormatException nfe) {
+ }
+ catch (NumberFormatException nfe) {
return defaultValue;
}
}
-
+
public final static boolean isJava18orHigher;
-
+
static {
String version = System.getProperty("java.version");
if (version.startsWith("1.8")) {
diff --git a/springloaded/src/main/java/org/springsource/loaded/ISMgr.java b/springloaded/src/main/java/org/springsource/loaded/ISMgr.java
index 034d100..bd38cc2 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ISMgr.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ISMgr.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Field;
@@ -22,10 +23,11 @@ import java.util.logging.Level;
import java.util.logging.Logger;
/**
- * Every reloadable hierarchy gets an Instance State Manager (ISMgr). The instance state manager is used to find the value of a
- * field for a particular object instance. The manager is added to the top most type in a reloadable hierarchy and is accessible to
- * all the subtypes. It maintains a map from types to secondary maps. The secondary maps record name,value pairs for each field. The
- * maps are only used if something has happened to mean we cannot continue to store the values in the original fields.
+ * Every reloadable hierarchy gets an Instance State Manager (ISMgr). The instance state manager is used to find the
+ * value of a field for a particular object instance. The manager is added to the top most type in a reloadable
+ * hierarchy and is accessible to all the subtypes. It maintains a map from types to secondary maps. The secondary maps
+ * record name,value pairs for each field. The maps are only used if something has happened to mean we cannot continue
+ * to store the values in the original fields.
*
* @author Andy Clement
* @since 0.5.0
@@ -48,8 +50,8 @@ public class ISMgr {
* Get the value of a instance field - this will use 'any means necessary' to get to it.
*
* @param rtype the reloadabletype
- * @param instance the object instance on which the field is being accessed (whose type may not be that which declares the
- * field)
+ * @param instance the object instance on which the field is being accessed (whose type may not be that which
+ * declares the field)
* @param name the name of the field
*
* @return the value of the field
@@ -73,14 +75,17 @@ public class ISMgr {
// Used to be caused because we were not reloading constructors - so when a new version of the type was
// loaded, maybe a field was removed, but the constructor may still be referring to it. Should no longer
// happen but what about static initializers that aren't run straightaway?
- log.info("Unexpectedly unable to locate instance field " + name + " starting from type " + rtype.dottedtypename
+ log.info("Unexpectedly unable to locate instance field " + name + " starting from type "
+ + rtype.dottedtypename
+ ": clinit running late?");
return null;
}
result = frw.getValue(instance, this);
- } else {
+ }
+ else {
if (field.isStatic()) {
- throw new IncompatibleClassChangeError("Expected non-static field " + rtype.dottedtypename + "." + field.getName());
+ throw new IncompatibleClassChangeError("Expected non-static field " + rtype.dottedtypename + "."
+ + field.getName());
}
String declaringTypeName = field.getDeclaringTypeName();
Map typeLevelValues = values.get(declaringTypeName);
@@ -103,7 +108,8 @@ public class ISMgr {
// 'field' tells us if we know about it now, it doesn't tell us if we've always known about it
// TODO lookup performance
- FieldMember fieldOnOriginalType = rtype.getTypeRegistry().getReloadableType(field.getDeclaringTypeName())
+ FieldMember fieldOnOriginalType = rtype.getTypeRegistry().getReloadableType(
+ field.getDeclaringTypeName())
.getTypeDescriptor().getField(name);
if (fieldOnOriginalType != null) {
@@ -118,11 +124,13 @@ public class ISMgr {
values.put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(name, result);
- } catch (Exception e) {
+ }
+ catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to access field " + name + " on type "
+ rt.getClazz().getName(), e);
}
- } else {
+ }
+ else {
// The field was not on the original type. As not seen before, can default it
result = Utils.toResultCheckIfNull(null, field.getDescriptor());
if (typeLevelValues == null) {
@@ -157,7 +165,8 @@ public class ISMgr {
* @param name the name of the field
* @throws IllegalAccessException if there is a problem setting the field value
*/
- public void setValue(ReloadableType rtype, Object instance, Object value, String name) throws IllegalAccessException {
+ public void setValue(ReloadableType rtype, Object instance, Object value, String name)
+ throws IllegalAccessException {
// System.err.println(">setValue(rtype=" + rtype + ",instance=" + instance + ",value=" + value + ",name=" + name + ")");
// Look up through our reloadable hierarchy to find it
@@ -170,12 +179,14 @@ public class ISMgr {
FieldReaderWriter frw = rtype.locateField(name);
if (frw == null) {
// bad code redeployed?
- log.info("Unexpectedly unable to locate instance field " + name + " starting from type " + rtype.dottedtypename
+ log.info("Unexpectedly unable to locate instance field " + name + " starting from type "
+ + rtype.dottedtypename
+ ": clinit running late?");
return;
}
frw.setValue(instance, value, this);
- } else {
+ }
+ else {
if (fieldmember.isStatic()) {
throw new IncompatibleClassChangeError("Expected non-static field " + rtype.dottedtypename + "."
+ fieldmember.getName());
diff --git a/springloaded/src/main/java/org/springsource/loaded/IncrementalTypeDescriptor.java b/springloaded/src/main/java/org/springsource/loaded/IncrementalTypeDescriptor.java
index db327d3..6c3cb82 100644
--- a/springloaded/src/main/java/org/springsource/loaded/IncrementalTypeDescriptor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/IncrementalTypeDescriptor.java
@@ -14,6 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.util.ArrayList;
@@ -22,7 +23,8 @@ import java.util.List;
import java.util.Map;
/**
- * This class computes and then encapsulates what has changed between the original form of a type and a newly loaded version.
+ * This class computes and then encapsulates what has changed between the original form of a type and a newly loaded
+ * version.
*
* @author Andy Clement
* @since 0.5.0
@@ -30,6 +32,7 @@ import java.util.Map;
public class IncrementalTypeDescriptor implements Constants {
private TypeDescriptor initialTypeDescriptor;
+
private TypeDescriptor latestTypeDescriptor;
private int bits;
@@ -37,8 +40,11 @@ public class IncrementalTypeDescriptor implements Constants {
private final static int BIT_COMPUTED_DIFF = 0x0001;
private Map latestMethods; // Map from nameAndDescriptor to the MethodMember
+
private List newOrChangedMethods;
+
private List newOrChangedConstructors; // TODO required?
+
private List deletedMethods;
public IncrementalTypeDescriptor(TypeDescriptor initialTypeDescriptor) {
@@ -71,6 +77,7 @@ public class IncrementalTypeDescriptor implements Constants {
*
method was represented as a catcher in the original, but is now 'real'
*
* It does not include catchers.
+ *
* @return list of new or changed methods in this type
*/
public List getNewOrChangedMethods() {
@@ -84,6 +91,7 @@ public class IncrementalTypeDescriptor implements Constants {
*
constructors that did not exist in the original class as loaded, but do now
*
constructors that did exist in the original class but have changed in some way (visibility)
*
+ *
* @return list of new or changed constructors in this type
*/
public List getNewOrChangedConstructors() {
@@ -113,7 +121,8 @@ public class IncrementalTypeDescriptor implements Constants {
if (original == null) {
latest.bits |= MethodMember.IS_NEW;
newOrChangedMethods.add(latest);
- } else {
+ }
+ else {
if (!original.equals(latest)) { // check more than just name/descriptor
newOrChangedMethods.add(latest);
}
@@ -127,7 +136,7 @@ public class IncrementalTypeDescriptor implements Constants {
latest.bits |= MethodMember.IS_NEW;
newOrChangedMethods.add(latest);
}
-
+
// If it now is a catcher where it didn't used to be, it has been deleted
if (MethodMember.isCatcher(latest) && !MethodMember.isCatcher(original)) {
latest.bits |= MethodMember.WAS_DELETED;
@@ -142,7 +151,8 @@ public class IncrementalTypeDescriptor implements Constants {
if (wasStatic) {
// has been made non-static
latest.bits |= MethodMember.MADE_NON_STATIC;
- } else {
+ }
+ else {
// has been made static
latest.bits |= MethodMember.MADE_STATIC;
}
@@ -283,7 +293,8 @@ public class IncrementalTypeDescriptor implements Constants {
public String toString(boolean compute) {
StringBuilder s = new StringBuilder();
- s.append("Original:\n").append(this.initialTypeDescriptor).append("\nCurrent:\n").append(this.latestTypeDescriptor);
+ s.append("Original:\n").append(this.initialTypeDescriptor).append("\nCurrent:\n").append(
+ this.latestTypeDescriptor);
s.append('\n');
if (compute) {
compute();
diff --git a/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java b/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java
index b7f6766..c19ca81 100644
--- a/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.objectweb.asm.AnnotationVisitor;
@@ -24,21 +25,26 @@ import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
/**
- * Extract an interface for a type. The interface embodies the shape of the type as originally loaded. The key difference with
- * methods in the interface is that they contain an extra (leading) parameter that is the type of the original loaded class.
+ * Extract an interface for a type. The interface embodies the shape of the type as originally loaded. The key
+ * difference with methods in the interface is that they contain an extra (leading) parameter that is the type of the
+ * original loaded class.
* For example:
*
- *
+ *
+ *
* class Foo {
* public String foo(int i) {}
* }
- *
+ *
+ *
*
* will cause creation of an interface method:
*
- *
+ *
+ *
* String foo(Foo instance, int i) {}
- *
+ *
+ *
*
* @author Andy Clement
* @since 0.5.0
@@ -74,7 +80,9 @@ public class InterfaceExtractor {
class ExtractorVisitor extends ClassVisitor implements Constants {
private TypeDescriptor typeDescriptor;
+
private ClassWriter interfaceWriter = new ClassWriter(0);
+
private String slashedtypename;
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
@@ -86,13 +94,16 @@ public class InterfaceExtractor {
return interfaceWriter.toByteArray();
}
- public void visit(int version, int flags, String name, String signature, String superclassName, String[] interfaceNames) {
+ public void visit(int version, int flags, String name, String signature, String superclassName,
+ String[] interfaceNames) {
// Create interface "public interface [typename]__I {"
- interfaceWriter.visit(version, ACC_PUBLIC_INTERFACE, Utils.getInterfaceName(name), null, "java/lang/Object", null);
+ interfaceWriter.visit(version, ACC_PUBLIC_INTERFACE, Utils.getInterfaceName(name), null,
+ "java/lang/Object", null);
this.slashedtypename = name;
}
- public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
+ public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
+ String[] exceptions) {
// TODO should we special case statics (and not have them require an extra leading param)?
if (isClinitOrInit(name)) {
if (name.charAt(1) != 'c') { // avoid
@@ -102,7 +113,8 @@ public class InterfaceExtractor {
name = "___init___";
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, signature, exceptions);
}
- } else {
+ }
+ else {
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
// generic signature is erased
MethodMember method = typeDescriptor.getByDescriptor(name, descriptor);
@@ -149,13 +161,15 @@ public class InterfaceExtractor {
continue;
}
descriptor = createDescriptorWithPrefixedParameter(method.getDescriptor());
- interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, method.getName(), descriptor, null, method.getExceptions());
+ interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, method.getName(), descriptor, null,
+ method.getExceptions());
}
}
/**
- * Modify the descriptor to include a leading parameter of the type of the class being visited. For example: if visiting
- * type "com.Bar" and hit method "(Ljava/lang/String;)V" then this method will return "(Lcom/Bar;Ljava/lang/String;)V"
+ * Modify the descriptor to include a leading parameter of the type of the class being visited. For example: if
+ * visiting type "com.Bar" and hit method "(Ljava/lang/String;)V" then this method will return
+ * "(Lcom/Bar;Ljava/lang/String;)V"
*
* @return new descriptor with extra leading parameter
*/
diff --git a/springloaded/src/main/java/org/springsource/loaded/IsReloadableTypePlugin.java b/springloaded/src/main/java/org/springsource/loaded/IsReloadableTypePlugin.java
index 50ac5c8..9daa19b 100644
--- a/springloaded/src/main/java/org/springsource/loaded/IsReloadableTypePlugin.java
+++ b/springloaded/src/main/java/org/springsource/loaded/IsReloadableTypePlugin.java
@@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
+
import java.security.ProtectionDomain;
import org.springsource.loaded.agent.ReloadDecision;
-
/**
- * Plugins implementing this interface are allowed to participate in determining whether a type should be made reloadable.
+ * Plugins implementing this interface are allowed to participate in determining whether a type should be made
+ * reloadable.
*
* @author Andy Clement
* @since 0.7.1
@@ -35,6 +37,7 @@ public interface IsReloadableTypePlugin extends Plugin {
* @param bytes the classfile data
* @return a decision indicating whether the specified type should be made reloadable
*/
- ReloadDecision shouldBeMadeReloadable(TypeRegistry typeRegistry, String typename, ProtectionDomain protectionDomain, byte[] bytes);
+ ReloadDecision shouldBeMadeReloadable(TypeRegistry typeRegistry, String typename,
+ ProtectionDomain protectionDomain, byte[] bytes);
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/LoadtimeInstrumentationPlugin.java b/springloaded/src/main/java/org/springsource/loaded/LoadtimeInstrumentationPlugin.java
index 81f89b2..2480b81 100644
--- a/springloaded/src/main/java/org/springsource/loaded/LoadtimeInstrumentationPlugin.java
+++ b/springloaded/src/main/java/org/springsource/loaded/LoadtimeInstrumentationPlugin.java
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.security.ProtectionDomain;
/**
- * Plugins that implement this interface are allowed to modify types as they are loaded - this can be necessary sometimes to ensure,
- * for example, that a particular field is accessible later when a reload event occurs or that some factory method returns a wrapper
- * rather than the original object it intended to. For information on how to register plugins with the agent, see {@link Plugin}
+ * Plugins that implement this interface are allowed to modify types as they are loaded - this can be necessary
+ * sometimes to ensure, for example, that a particular field is accessible later when a reload event occurs or that some
+ * factory method returns a wrapper rather than the original object it intended to. For information on how to register
+ * plugins with the agent, see {@link Plugin}
*
* @author Andy Clement
* @since 0.5.0
@@ -29,9 +31,9 @@ public interface LoadtimeInstrumentationPlugin extends Plugin {
// TODO should probably be dotted names rather than slashed
/**
- * Called by the agent to determine if this plugin is interested in changing the specified type at load time. This is used when
- * the plugin wishes to do some kind of transformation itself before the type is loaded - for example modify it to record
- * something that will later be used on a reload event.
+ * Called by the agent to determine if this plugin is interested in changing the specified type at load time. This
+ * is used when the plugin wishes to do some kind of transformation itself before the type is loaded - for example
+ * modify it to record something that will later be used on a reload event.
*
* @param slashedTypeName the type name, slashed form (e.g. java/lang/String)
* @param classLoader the classloader loading the type
@@ -42,7 +44,8 @@ public interface LoadtimeInstrumentationPlugin extends Plugin {
boolean accept(String slashedTypeName, ClassLoader classLoader, ProtectionDomain protectionDomain, byte[] bytes);
/**
- * Once accept has returned true for a type, the modify method will be called to make the actual change to the classfile bytes.
+ * Once accept has returned true for a type, the modify method will be called to make the actual change to the
+ * classfile bytes.
*
* @param slashedClassName the class name, slashed form (e.g. java/lang/String)
* @param classLoader the classloader loading the type
diff --git a/springloaded/src/main/java/org/springsource/loaded/Log.java b/springloaded/src/main/java/org/springsource/loaded/Log.java
index 0cee064..ea094db 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Log.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Log.java
@@ -1,8 +1,9 @@
+
package org.springsource.loaded;
/**
- * Minimal support for logging messages, avoids all dependencies it can because it will be loaded very early by the VM and
- * we don't want to introduce unnecessarily complex classloading.
+ * Minimal support for logging messages, avoids all dependencies it can because it will be loaded very early by the VM
+ * and we don't want to introduce unnecessarily complex classloading.
*
* @author Andy Clement
* @since 1.1.5
@@ -10,7 +11,7 @@ package org.springsource.loaded;
public class Log {
public static void log(String message) {
- System.out.println("SL: "+message);
+ System.out.println("SL: " + message);
}
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java b/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java
index 9f94034..023e314 100644
--- a/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java
+++ b/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.objectweb.asm.Label;
@@ -28,15 +29,21 @@ import org.springsource.loaded.Utils.ReturnType;
class MethodCopier extends MethodVisitor implements Constants {
private boolean isInterface;
+
private String descriptor;
+
private TypeDescriptor typeDescriptor;
+
private String classname;
+
private String suffix;
+
private boolean hasFieldsRequiringAccessors;
- public MethodCopier(MethodVisitor mv, boolean isInterface, String descriptor, TypeDescriptor typeDescriptor, String classname,
+ public MethodCopier(MethodVisitor mv, boolean isInterface, String descriptor, TypeDescriptor typeDescriptor,
+ String classname,
String suffix) {
- super(ASM5,mv);
+ super(ASM5, mv);
this.isInterface = isInterface;
this.descriptor = descriptor;
this.typeDescriptor = typeDescriptor;
@@ -50,7 +57,8 @@ class MethodCopier extends MethodVisitor implements Constants {
// Rename 'this' to 'thiz' in executor otherwise Eclipse debugger will fail (static method with 'this')
if (index == 0 && name.equals("this")) {
super.visitLocalVariable("thiz", desc, signature, start, end, index);
- } else {
+ }
+ else {
super.visitLocalVariable(name, desc, signature, start, end, index);
}
}
@@ -76,9 +84,9 @@ class MethodCopier extends MethodVisitor implements Constants {
}
/**
- * Determine if the supplied type is a supertype of the current type we are modifying. This is used to determine if the owner we
- * have discovered for a field is one of our supertypes (and so, if it is protected, whether it is something that needs
- * redirecting through an accessor).
+ * Determine if the supplied type is a supertype of the current type we are modifying. This is used to determine if
+ * the owner we have discovered for a field is one of our supertypes (and so, if it is protected, whether it is
+ * something that needs redirecting through an accessor).
*
* @param type the type which may be one of this types supertypes
* @return true if it is a supertype
@@ -94,7 +102,7 @@ class MethodCopier extends MethodVisitor implements Constants {
}
return false;
}
-
+
private TypeDescriptor getType(String type) {
TypeDescriptor typeDescriptor = this.typeDescriptor.getTypeRegistry().getDescriptorFor(type);
return typeDescriptor;
@@ -107,18 +115,22 @@ class MethodCopier extends MethodVisitor implements Constants {
FieldMember fm = findFieldIfRequiresAccessorUsage(owner, name);
if (fm != null) {
switch (opcode) {
- case GETFIELD:
- mv.visitMethodInsn(INVOKEVIRTUAL, classname, Utils.getProtectedFieldGetterName(name), "()" + desc, false);
- return;
- case PUTFIELD:
- mv.visitMethodInsn(INVOKEVIRTUAL, classname, Utils.getProtectedFieldSetterName(name), "(" + desc + ")V", false);
- return;
- case GETSTATIC:
- mv.visitMethodInsn(INVOKESTATIC, classname, Utils.getProtectedFieldGetterName(name), "()" + desc, false);
- return;
- case PUTSTATIC:
- mv.visitMethodInsn(INVOKESTATIC, classname, Utils.getProtectedFieldSetterName(name), "(" + desc + ")V", false);
- return;
+ case GETFIELD:
+ mv.visitMethodInsn(INVOKEVIRTUAL, classname, Utils.getProtectedFieldGetterName(name), "()"
+ + desc, false);
+ return;
+ case PUTFIELD:
+ mv.visitMethodInsn(INVOKEVIRTUAL, classname, Utils.getProtectedFieldSetterName(name), "("
+ + desc + ")V", false);
+ return;
+ case GETSTATIC:
+ mv.visitMethodInsn(INVOKESTATIC, classname, Utils.getProtectedFieldGetterName(name), "()"
+ + desc, false);
+ return;
+ case PUTSTATIC:
+ mv.visitMethodInsn(INVOKESTATIC, classname, Utils.getProtectedFieldSetterName(name), "(" + desc
+ + ")V", false);
+ return;
}
}
}
@@ -137,18 +149,20 @@ class MethodCopier extends MethodVisitor implements Constants {
String descriptor = Utils.insertExtraParameter(owner, desc);
super.visitMethodInsn(INVOKESTATIC, Utils.getExecutorName(classname, suffix), name, descriptor, false);
return;
- } else {
+ }
+ else {
// super call
// TODO Check if this is true: we can just call the catcher directly if there was one, there is no need
// for a superdispatcher
// Only need to redirect to the superdispatcher if it was a protected method
TypeDescriptor supertypeDescriptor = getType(owner);
- MethodMember target = supertypeDescriptor.getByNameAndDescriptor(name+desc);
- if (target!=null && target.isProtected()) {
+ MethodMember target = supertypeDescriptor.getByNameAndDescriptor(name + desc);
+ if (target != null && target.isProtected()) {
// A null target means that method is not in the supertype, so didn't get a superdispatcher
- super.visitMethodInsn(INVOKESPECIAL,classname,name+methodSuffixSuperDispatcher,desc, false);
- } else {
+ super.visitMethodInsn(INVOKESPECIAL, classname, name + methodSuffixSuperDispatcher, desc, false);
+ }
+ else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
return;
@@ -183,37 +197,39 @@ class MethodCopier extends MethodVisitor implements Constants {
if (returnType.isVoid()) {
super.visitInsn(RETURN);
super.visitMaxs(1, descriptorSize);
- } else if (returnType.isPrimitive()) {
+ }
+ else if (returnType.isPrimitive()) {
super.visitLdcInsn(0);
switch (returnType.descriptor.charAt(0)) {
- case 'B':
- case 'C':
- case 'I':
- case 'S':
- case 'Z':
- super.visitInsn(IRETURN);
- super.visitMaxs(2, descriptorSize);
- break;
- case 'D':
- super.visitInsn(DRETURN);
- super.visitMaxs(3, descriptorSize);
- break;
- case 'F':
- super.visitInsn(FRETURN);
- super.visitMaxs(2, descriptorSize);
- break;
- case 'J':
- super.visitInsn(LRETURN);
- super.visitMaxs(3, descriptorSize);
- break;
- default:
- throw new IllegalStateException(returnType.descriptor);
+ case 'B':
+ case 'C':
+ case 'I':
+ case 'S':
+ case 'Z':
+ super.visitInsn(IRETURN);
+ super.visitMaxs(2, descriptorSize);
+ break;
+ case 'D':
+ super.visitInsn(DRETURN);
+ super.visitMaxs(3, descriptorSize);
+ break;
+ case 'F':
+ super.visitInsn(FRETURN);
+ super.visitMaxs(2, descriptorSize);
+ break;
+ case 'J':
+ super.visitInsn(LRETURN);
+ super.visitMaxs(3, descriptorSize);
+ break;
+ default:
+ throw new IllegalStateException(returnType.descriptor);
}
- } else {
+ }
+ else {
// reference type
super.visitInsn(ACONST_NULL);
super.visitInsn(ARETURN);
super.visitMaxs(1, descriptorSize);
}
}
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/MethodDelta.java b/springloaded/src/main/java/org/springsource/loaded/MethodDelta.java
index dff2c84..bc0d532 100644
--- a/springloaded/src/main/java/org/springsource/loaded/MethodDelta.java
+++ b/springloaded/src/main/java/org/springsource/loaded/MethodDelta.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.objectweb.asm.tree.AbstractInsnNode;
@@ -24,23 +25,34 @@ import org.objectweb.asm.tree.AbstractInsnNode;
* @since 0.5.0
*/
public class MethodDelta {
+
public int changed;
private final static int CHANGED_INSTRUCTIONS = 0x0001;
+
private final static int CHANGED_ACCESS = 0x0002;
+
private final static int CHANGED_ANNOTATIONS = 0x0004;
+
private final static int CHANGED_INVOKESPECIAL = 0x0008;
+
private final static int CHANGED_CODE = 0x0010;
- private final static int CHANGED_MASK = CHANGED_INSTRUCTIONS | CHANGED_ACCESS | CHANGED_ANNOTATIONS | CHANGED_INVOKESPECIAL
+ private final static int CHANGED_MASK = CHANGED_INSTRUCTIONS | CHANGED_ACCESS | CHANGED_ANNOTATIONS
+ | CHANGED_INVOKESPECIAL
| CHANGED_CODE;
// o = original, n = new
public final String name;
+
public final String desc;
+
String annotationChanges;
+
int oAccess, nAccess;
+
String oInvokespecialDescriptor, nInvokespecialDescriptor;
+
AbstractInsnNode[] oInstructions, nInstructions;
public MethodDelta(String name, String desc) {
@@ -99,4 +111,4 @@ public class MethodDelta {
this.oInstructions = oInstructions;
this.nInstructions = nInstructions;
}
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java b/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java
index afb7fee..275602f 100644
--- a/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.io.BufferedReader;
@@ -45,18 +46,20 @@ import org.springsource.loaded.ri.ReflectiveInterceptor;
/**
- * Rewrite method calls and field accesses. This is not only references to reloadable types but also calls to reflective APIs which
- * must be intercepted in case they refer to reloadable types at runtime.
+ * Rewrite method calls and field accesses. This is not only references to reloadable types but also calls to reflective
+ * APIs which must be intercepted in case they refer to reloadable types at runtime.
*
* The MethodInvokerRewriter actually manages a portion of the .slcache - it keeps track of two things:
*
- *
There is an index file (.index) that records types and whether they were modified on a previous run. A later run can then
- * quickly determine it doesn't need to do anything, or if it should look for a cache file containing the modified form.
- *
There is a file 'per-modified-file' that captures the previously determined woven form of the class. The files are named
- * after the classname suffixed with the length of the bytecode (e.g. java_lang_String_3322.bytes). If one of these files exists, we
- * know it contains the appropriate modified bytecode created on a previous run.
+ *
There is an index file (.index) that records types and whether they were modified on a previous run. A later run
+ * can then quickly determine it doesn't need to do anything, or if it should look for a cache file containing the
+ * modified form.
+ *
There is a file 'per-modified-file' that captures the previously determined woven form of the class. The files
+ * are named after the classname suffixed with the length of the bytecode (e.g. java_lang_String_3322.bytes). If one of
+ * these files exists, we know it contains the appropriate modified bytecode created on a previous run.
*
- * The cache is for types that are *only* getting reflection interception done, not for types touching anything reloadable.
+ * The cache is for types that are *only* getting reflection interception done, not for types touching anything
+ * reloadable.
*
* @author Andy Clement
* @since 0.5.0
@@ -68,11 +71,11 @@ public class MethodInvokerRewriter {
private static boolean anyNecessaryCacheCleanupDone = false;
/**
- * Populated by the file in <cacheDir>/.index - this describes which types were not modified on a previous run and which
- * files were. If a type is not mentioned then it hasn't been seen before. If the type is mentioned then there will likely be
- * the bytecode for the modified form on disk in a file named something like 'com/foo/Bar_22233.bytes'- the number is the length
- * of the unmodified form. The key into this map is the slashed form of the typename suffixed with '_' and the length of the
- * bytes.
+ * Populated by the file in <cacheDir>/.index - this describes which types were not modified on a previous run
+ * and which files were. If a type is not mentioned then it hasn't been seen before. If the type is mentioned then
+ * there will likely be the bytecode for the modified form on disk in a file named something like
+ * 'com/foo/Bar_22233.bytes'- the number is the length of the unmodified form. The key into this map is the slashed
+ * form of the typename suffixed with '_' and the length of the bytes.
*/
private static Map cacheIndex = null;
@@ -102,7 +105,8 @@ public class MethodInvokerRewriter {
boolean b = false;
try {
b = System.getProperty("springloaded.debugcaching", "false").equalsIgnoreCase("true");
- } catch (Exception e) {
+ }
+ catch (Exception e) {
}
DEBUG_CACHING = b;
}
@@ -121,7 +125,8 @@ public class MethodInvokerRewriter {
}
if (b != null) {
if (b.booleanValue()) { // the type was modified on an earlier run, there should be cached code around
- String cacheFileName = new StringBuilder(slashedClassName.replace('/', '_')).append("_").append(bytes.length)
+ String cacheFileName = new StringBuilder(slashedClassName.replace('/', '_')).append("_").append(
+ bytes.length)
.append(".bytes").toString();
File cacheFile = new File(GlobalConfiguration.cacheDir, ".slcache" + File.separator + cacheFileName);
if (DEBUG_CACHING) {
@@ -136,11 +141,13 @@ public class MethodInvokerRewriter {
FileInputStream fis = new FileInputStream(cacheFile);
byte[] cachedBytes = Utils.loadBytesFromStream(fis);
return cachedBytes;
- } catch (IOException ioe) {
+ }
+ catch (IOException ioe) {
ioe.printStackTrace();
}
}
- } else {
+ }
+ else {
if (DEBUG_CACHING) {
System.out.println("returning unmodified bytes, no need to change");
}
@@ -186,17 +193,19 @@ public class MethodInvokerRewriter {
File cacheDir = new File(GlobalConfiguration.cacheDir, ".slcache");
if (cacheDir.exists()) {
recursiveDelete(cacheDir);
- if (cacheIndex!=null) {
+ if (cacheIndex != null) {
cacheIndex.clear();
}
}
- versionInIndex=false;
+ versionInIndex = false;
}
-
- private static final int CACHE_VERSION_1_1_0=1;
- private static final int CACHE_VERSION_1_1_1=2;
+
+ private static final int CACHE_VERSION_1_1_0 = 1;
+
+ private static final int CACHE_VERSION_1_1_1 = 2;
+
private static final int CURRENT_CACHE_VERSION = CACHE_VERSION_1_1_1;
-
+
private static boolean versionInIndex = false;
/**
@@ -209,7 +218,8 @@ public class MethodInvokerRewriter {
if (GlobalConfiguration.cleanCache) {
deleteCacheFiles();
anyNecessaryCacheCleanupDone = true;
- } else {
+ }
+ else {
File cacheDir = new File(GlobalConfiguration.cacheDir, ".slcache");
cacheDir.mkdir();
File cacheIndexFile = new File(cacheDir, ".index");
@@ -226,10 +236,10 @@ public class MethodInvokerRewriter {
if (!handledVersionString) {
if (line.startsWith("Version")) {
int colon = line.indexOf(":");
- if (colon!=-1) {
- cacheVersion = Integer.parseInt(line.substring(colon+1));
+ if (colon != -1) {
+ cacheVersion = Integer.parseInt(line.substring(colon + 1));
}
-
+
handledVersionString = true;
continue;
}
@@ -243,23 +253,27 @@ public class MethodInvokerRewriter {
cacheIndex.put(key, changed);
}
fr.close();
- } catch (IOException ioe) {
+ }
+ catch (IOException ioe) {
ioe.printStackTrace();
}
- if (cacheVersion!=CURRENT_CACHE_VERSION) {
- clearCache=true;
- versionInIndex=false;
- } else {
- versionInIndex=true;
+ if (cacheVersion != CURRENT_CACHE_VERSION) {
+ clearCache = true;
+ versionInIndex = false;
+ }
+ else {
+ versionInIndex = true;
}
if (clearCache) {
if (DEBUG_CACHING) {
- System.out.println("SpringLoaded: cache looks old (version "+cacheVersion+") - clearing it");
+ System.out.println("SpringLoaded: cache looks old (version " + cacheVersion
+ + ") - clearing it");
}
deleteCacheFiles();
- anyNecessaryCacheCleanupDone = true;
+ anyNecessaryCacheCleanupDone = true;
}
- } catch (NoSuchElementException nsme) {
+ }
+ catch (NoSuchElementException nsme) {
// rogue entry in the cache
if (DEBUG_CACHING) {
System.out.println("SpringLoaded: cache corrupt, clearing it");
@@ -303,7 +317,8 @@ public class MethodInvokerRewriter {
if (typeRegistry != null && typeRegistry.isReloadableTypeName(clazz)) {
needsRewriting = true;
break;
- } else if (clazz.length() > 10 && clazz.charAt(0) == 'j'
+ }
+ else if (clazz.length() > 10 && clazz.charAt(0) == 'j'
&& (clazz.startsWith("java/lang/reflect/") || clazz.equals("java/lang/Class"))) {
// Need a closer look
boolean foundMethodCandidate = false;
@@ -330,7 +345,8 @@ public class MethodInvokerRewriter {
RewriteClassAdaptor classAdaptor = new RewriteClassAdaptor(typeRegistry);
try {// TODO always skip frames? or just for javassist things?
fileReader.accept(classAdaptor, ClassReader.SKIP_FRAMES);
- } catch (DontRewriteException drex) {
+ }
+ catch (DontRewriteException drex) {
return bytes;
}
// System.out.println((System.currentTimeMillis() - stime) + " rewrote " + classAdaptor.classname);
@@ -364,7 +380,8 @@ public class MethodInvokerRewriter {
FileOutputStream fos = new FileOutputStream(cacheFile);
fos.write(newbytes, 0, newbytes.length);
fos.close();
- } catch (IOException ioe) {
+ }
+ catch (IOException ioe) {
ioe.printStackTrace();
}
addToCacheIndex(slashedclassname, originalBytes, true);
@@ -382,12 +399,13 @@ public class MethodInvokerRewriter {
BufferedWriter bw = new BufferedWriter(fw);
if (!versionInIndex) {
versionInIndex = true;
- bw.write("Version:"+CURRENT_CACHE_VERSION+"\n");
+ bw.write("Version:" + CURRENT_CACHE_VERSION + "\n");
}
bw.write((changed ? "y" : "n") + ":" + bytes.length + ":" + slashedclassname + "\n");
bw.flush();
fw.close();
- } catch (IOException ioe) {
+ }
+ catch (IOException ioe) {
ioe.printStackTrace();
}
}
@@ -406,10 +424,12 @@ public class MethodInvokerRewriter {
}
if (same) {
System.out.println("same data!!");
- } else {
+ }
+ else {
System.out.println("diff data");
}
- } else {
+ }
+ else {
System.out.println("different");
}
}
@@ -453,100 +473,100 @@ public class MethodInvokerRewriter {
// made about them.
// @formatter:off
static final String[] ignored = new String[] {
- // TODO: [...] the list below is dependent on assumptions about
- // what may / may not be changed
- // when reloading. We should make explicit precisely what the
- // underluing assumptions are
- // (e.g. by implementing checks when reloading types to see if
- // any of the assumptions are
- // being violated.)
+ // TODO: [...] the list below is dependent on assumptions about
+ // what may / may not be changed
+ // when reloading. We should make explicit precisely what the
+ // underluing assumptions are
+ // (e.g. by implementing checks when reloading types to see if
+ // any of the assumptions are
+ // being violated.)
- "Array.",
- "GenericArrayType.",
- "InvocationTargetException.",
- "MalformedParameterizedTypeException.",
- "Modifier.",
- "ParameterizedType.",
- "UndeclaredThrowableException.",
- "WildcardType.",
- "TypeVariable.",
+ "Array.",
+ "GenericArrayType.",
+ "InvocationTargetException.",
+ "MalformedParameterizedTypeException.",
+ "Modifier.",
+ "ParameterizedType.",
+ "UndeclaredThrowableException.",
+ "WildcardType.",
+ "TypeVariable.",
- "AccessibleObject.isAccessible",
- "AccessibleObject.setAccessible",
+ "AccessibleObject.isAccessible",
+ "AccessibleObject.setAccessible",
- "Class.asSubclass",
- "Class.cast",
- "Class.forName",
- "Class.getCanonicalName",
- "Class.getClassLoader",
- "Class.getClasses",
- "Class.getComponentType",
- "Class.getDeclaredClasses",
- "Class.getDeclaringClass",
- "Class.getEnclosingClass",
- "Class.getEnclosingConstructor",
- "Class.getEnclosingMethod",
- "Class.getGenericInterfaces",
- "Class.getGenericSuperclass",
- "Class.desiredAssertionStatus",
- "Class.getEnumConstants",
- "Class.getInterfaces",
- "Class.getModifiers",
- "Class.getName",
- "Class.getPackage",
- "Class.getProtectionDomain",
- "Class.getResourceAsStream",
- "Class.getResource",
- "Class.getSuperclass",
- "Class.getSimpleName",
- "Class.getSigners",
- "Class.getTypeParameters",
- "Class.isArray",
- "Class.isAnonymousClass",
- "Class.isAnnotation",
- "Class.isAssignableFrom",
- "Class.isEnum",
- "Class.isInstance",
- "Class.isInterface",
- "Class.isLocalClass",
- "Class.isMemberClass",
- "Class.isPrimitive",
- "Class.isSynthetic",
- "Class.toString",
+ "Class.asSubclass",
+ "Class.cast",
+ "Class.forName",
+ "Class.getCanonicalName",
+ "Class.getClassLoader",
+ "Class.getClasses",
+ "Class.getComponentType",
+ "Class.getDeclaredClasses",
+ "Class.getDeclaringClass",
+ "Class.getEnclosingClass",
+ "Class.getEnclosingConstructor",
+ "Class.getEnclosingMethod",
+ "Class.getGenericInterfaces",
+ "Class.getGenericSuperclass",
+ "Class.desiredAssertionStatus",
+ "Class.getEnumConstants",
+ "Class.getInterfaces",
+ "Class.getModifiers",
+ "Class.getName",
+ "Class.getPackage",
+ "Class.getProtectionDomain",
+ "Class.getResourceAsStream",
+ "Class.getResource",
+ "Class.getSuperclass",
+ "Class.getSimpleName",
+ "Class.getSigners",
+ "Class.getTypeParameters",
+ "Class.isArray",
+ "Class.isAnonymousClass",
+ "Class.isAnnotation",
+ "Class.isAssignableFrom",
+ "Class.isEnum",
+ "Class.isInstance",
+ "Class.isInterface",
+ "Class.isLocalClass",
+ "Class.isMemberClass",
+ "Class.isPrimitive",
+ "Class.isSynthetic",
+ "Class.toString",
- "Constructor.equals",
- "Constructor.toString",
- "Constructor.hashCode",
- "Constructor.getModifiers",
- "Constructor.getName", // TODO test
- "Constructor.getDeclaringClass", // TODO test
- "Constructor.getParameterTypes", // TODO test
- "Constructor.getTypeParameters", // TODO test
- "Constructor.isSynthetic", // TODO test
- "Constructor.toGenericString", // TODO test
- "Constructor.getExceptionTypes", // TODO test
- "Constructor.getGenericExceptionTypes", // TODO test
- "Constructor.getGenericParameterTypes", // TODO test
- "Constructor.isVarArgs", // TODO test
+ "Constructor.equals",
+ "Constructor.toString",
+ "Constructor.hashCode",
+ "Constructor.getModifiers",
+ "Constructor.getName", // TODO test
+ "Constructor.getDeclaringClass", // TODO test
+ "Constructor.getParameterTypes", // TODO test
+ "Constructor.getTypeParameters", // TODO test
+ "Constructor.isSynthetic", // TODO test
+ "Constructor.toGenericString", // TODO test
+ "Constructor.getExceptionTypes", // TODO test
+ "Constructor.getGenericExceptionTypes", // TODO test
+ "Constructor.getGenericParameterTypes", // TODO test
+ "Constructor.isVarArgs", // TODO test
- "Field.equals", "Field.getDeclaringClass",
- "Field.getGenericType", "Field.getName", "Field.getModifiers",
- "Field.getType", "Field.hashCode", "Field.isEnumConstant",
- "Field.isSynthetic", "Field.toGenericString", "Field.toString",
+ "Field.equals", "Field.getDeclaringClass",
+ "Field.getGenericType", "Field.getName", "Field.getModifiers",
+ "Field.getType", "Field.hashCode", "Field.isEnumConstant",
+ "Field.isSynthetic", "Field.toGenericString", "Field.toString",
- "Member.getDeclaringClass", "Member.getModifiers",
- "Member.getName",
+ "Member.getDeclaringClass", "Member.getModifiers",
+ "Member.getName",
- "Method.equals", "Method.getDeclaringClass",
- "Method.getDefaultValue", "Method.getGenericExceptionTypes",
- "Method.getGenericParameterTypes",
- "Method.getGenericReturnType", "Method.getExceptionTypes",
- "Method.getModifiers", "Method.getName",
- "Method.getParameterTypes", "Method.getReturnType",
- "Method.getTypeParameters", "Method.hashCode",
- "Method.isAccessible", "Method.isBridge", "Method.isSynthetic",
- "Method isVarArgs", "Method.setAccessible",
- "Method toGenericString", "Method.toString",
+ "Method.equals", "Method.getDeclaringClass",
+ "Method.getDefaultValue", "Method.getGenericExceptionTypes",
+ "Method.getGenericParameterTypes",
+ "Method.getGenericReturnType", "Method.getExceptionTypes",
+ "Method.getModifiers", "Method.getName",
+ "Method.getParameterTypes", "Method.getReturnType",
+ "Method.getTypeParameters", "Method.hashCode",
+ "Method.isAccessible", "Method.isBridge", "Method.isSynthetic",
+ "Method isVarArgs", "Method.setAccessible",
+ "Method toGenericString", "Method.toString",
};
@@ -641,12 +661,12 @@ public class MethodInvokerRewriter {
// @formatter:on
/**
- * Call this method to declare that a certain method is 'interceptable'. An interceptable method should have a corresponding
- * interceptor method in {@link ReflectiveInterceptor}. The name and signature of the interceptor will be derived from the
- * interceptable method.
+ * Call this method to declare that a certain method is 'interceptable'. An interceptable method should have a
+ * corresponding interceptor method in {@link ReflectiveInterceptor}. The name and signature of the interceptor
+ * will be derived from the interceptable method.
*
- * For example, java.lang.Class.getMethod(Class[] params) ==> ReflectiveInterceptor.jlClassGetMethod(Class thiz, Class[]
- * params)
+ * For example, java.lang.Class.getMethod(Class[] params) ==> ReflectiveInterceptor.jlClassGetMethod(Class thiz,
+ * Class[] params)
*
* @param owner Slashed class name of the declaring type.
* @param methodName Name of the interceptable method.
@@ -660,7 +680,9 @@ public class MethodInvokerRewriter {
}
public boolean rewroteReflection = false;
+
public boolean rewroteOtherKindOfOperation = false;
+
public boolean thisClassIsReloadable = false;
private static boolean isInterceptable(String owner, String methodName) {
@@ -668,14 +690,18 @@ public class MethodInvokerRewriter {
}
protected TypeRegistry typeRegistry;
+
boolean isEnum = false;
+
private boolean isGroovyClosure = false;
+
int fieldcount = 0;
+
private ReloadableType rtype; // Can be null if rewriting in a non reloadable type
public RewriteClassAdaptor(TypeRegistry typeRegistry, ClassVisitor classWriter) {
// TODO should it also compute frames?
- super(ASM5,classWriter);
+ super(ASM5, classWriter);
cw = cv;
this.typeRegistry = typeRegistry;
}
@@ -692,7 +718,7 @@ public class MethodInvokerRewriter {
public ClassVisitor getClassVisitor() {
return cv;
}
-
+
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
@@ -705,7 +731,8 @@ public class MethodInvokerRewriter {
}
if (superName.equals("java/lang/Enum")) {
this.isEnum = true;
- } else if (superName.equals("groovy/lang/Closure")) {
+ }
+ else if (superName.equals("groovy/lang/Closure")) {
this.isGroovyClosure = true;
}
}
@@ -717,7 +744,8 @@ public class MethodInvokerRewriter {
}
@Override
- public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
+ public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
+ String[] exceptions) {
MethodVisitor mv = super.visitMethod(flags, name, descriptor, signature, exceptions);
return new RewritingMethodAdapter(mv, name);
}
@@ -729,10 +757,11 @@ public class MethodInvokerRewriter {
private int max = 0;
private String methodname; // method being rewritten
+
private boolean isClinitOrEnumInit = false;
public RewritingMethodAdapter(MethodVisitor mv, String methodname) {
- super(ASM5,mv);
+ super(ASM5, mv);
this.methodname = methodname;
if (isEnum) {
isClinitOrEnumInit = this.methodname.length() > 2 && this.methodname.charAt(0) == '<'
@@ -748,10 +777,12 @@ public class MethodInvokerRewriter {
if (var > max) {
if (opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE) {
max = var + 1;
- } else {
+ }
+ else {
max = var;
}
- } else if (var == max) {
+ }
+ else if (var == max) {
if (opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE) {
max = var + 1;
}
@@ -764,9 +795,11 @@ public class MethodInvokerRewriter {
if (!GlobalConfiguration.fieldRewriting) {
super.visitFieldInsn(opcode, owner, name, desc);
return;
- } else {
+ }
+ else {
boolean isReloadable = typeRegistry != null
- && (owner.equals(slashedclassname) ? thisClassIsReloadable : typeRegistry.isReloadableTypeName(owner));
+ && (owner.equals(slashedclassname) ? thisClassIsReloadable
+ : typeRegistry.isReloadableTypeName(owner));
// boolean isReloadable = typeRegistry != null &&
// typeRegistry.isReloadableTypeName(owner);
if (!isReloadable) {
@@ -783,15 +816,18 @@ public class MethodInvokerRewriter {
return;
}
rewriteGETSTATIC(opcode, owner, name, desc);
- } else if (opcode == PUTSTATIC) {
+ }
+ else if (opcode == PUTSTATIC) {
if (isEnum && isClinitOrEnumInit && fieldcount > GlobalConfiguration.enumLimit) {
super.visitFieldInsn(opcode, owner, name, desc);
return;
}
rewritePUTSTATIC(opcode, owner, name, desc);
- } else if (opcode == GETFIELD) {
+ }
+ else if (opcode == GETFIELD) {
rewriteGETFIELD(opcode, owner, name, desc);
- } else if (opcode == PUTFIELD) {
+ }
+ else if (opcode == PUTFIELD) {
rewritePUTFIELD(opcode, owner, name, desc);
}
rewroteOtherKindOfOperation = true;
@@ -818,7 +854,8 @@ public class MethodInvokerRewriter {
// Make a call to check if this field operation must be intercepted:
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
mv.visitLdcInsn(name);
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mInstanceFieldInterceptionRequired, "(ILjava/lang/String;)Z", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mInstanceFieldInterceptionRequired,
+ "(ILjava/lang/String;)Z", false);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1); // IF (false) GOTO l1
Utils.insertBoxInsns(mv, desc); // box the value if necessary
@@ -847,7 +884,8 @@ public class MethodInvokerRewriter {
// intercepted
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
mv.visitLdcInsn(name);
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mInstanceFieldInterceptionRequired, "(ILjava/lang/String;)Z", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mInstanceFieldInterceptionRequired,
+ "(ILjava/lang/String;)Z", false);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1); // IF (false) GOTO l1
mv.visitInsn(DUP);
@@ -858,7 +896,8 @@ public class MethodInvokerRewriter {
if (!desc.equals(jlObject)) {
mv.visitTypeInsn(CHECKCAST, toDescriptor(desc));
}
- } else {
+ }
+ else {
Utils.insertUnboxInsns(mv, desc.charAt(0), true);
}
Label l2 = new Label();
@@ -874,13 +913,15 @@ public class MethodInvokerRewriter {
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
// Make a call to check if this field operation must be intercepted:
mv.visitLdcInsn(name);
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mStaticFieldInterceptionRequired, "(ILjava/lang/String;)Z", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mStaticFieldInterceptionRequired,
+ "(ILjava/lang/String;)Z", false);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1); // IF (false) GOTO l1
// top of heap will be the new value
Utils.insertBoxInsns(mv, desc);
mv.visitLdcInsn(name);
- mv.visitMethodInsn(INVOKESTATIC, owner, mStaticFieldSetterName, "(Ljava/lang/Object;Ljava/lang/String;)V", false);
+ mv.visitMethodInsn(INVOKESTATIC, owner, mStaticFieldSetterName,
+ "(Ljava/lang/Object;Ljava/lang/String;)V", false);
Label l2 = new Label();
mv.visitJumpInsn(GOTO, l2);
mv.visitLabel(l1);
@@ -893,17 +934,20 @@ public class MethodInvokerRewriter {
// Make a call to check if this field operation must be intercepted:
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
mv.visitLdcInsn(name);
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mStaticFieldInterceptionRequired, "(ILjava/lang/String;)Z", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mStaticFieldInterceptionRequired,
+ "(ILjava/lang/String;)Z", false);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1); // IF (false) GOTO l1
// top of heap will be the new value
mv.visitLdcInsn(name);
- mv.visitMethodInsn(INVOKESTATIC, owner, mStaticFieldGetterName, "(Ljava/lang/String;)Ljava/lang/Object;", false);
+ mv.visitMethodInsn(INVOKESTATIC, owner, mStaticFieldGetterName,
+ "(Ljava/lang/String;)Ljava/lang/Object;", false);
if (desc.length() != 1) {
if (!desc.equals(jlObject)) {
mv.visitTypeInsn(CHECKCAST, toDescriptor(desc));
}
- } else {
+ }
+ else {
Utils.insertUnboxInsnsIfNecessary(mv, desc, true);
}
Label l2 = new Label();
@@ -921,8 +965,8 @@ public class MethodInvokerRewriter {
}
/**
- * The big method for intercepting reflection. It is passed what the original code is trying to do (which method it is
- * calling) and decides:
+ * The big method for intercepting reflection. It is passed what the original code is trying to do (which
+ * method it is calling) and decides:
*
*
whether to rewrite it
*
what method should be called instead
@@ -953,14 +997,15 @@ public class MethodInvokerRewriter {
}
private String toString(Handle handle) {
- return "handle(tag="+handle.getTag()+",name="+handle.getName()+",desc="+handle.getDesc()+",owner="+handle.getOwner();
+ return "handle(tag=" + handle.getTag() + ",name=" + handle.getName() + ",desc=" + handle.getDesc()
+ + ",owner=" + handle.getOwner();
}
-
+
private String toString(Object[] oa) {
StringBuilder buf = new StringBuilder();
buf.append("[");
- if (oa!=null) {
- for (Object o:oa) {
+ if (oa != null) {
+ for (Object o : oa) {
buf.append(" ");
buf.append(o);
}
@@ -968,13 +1013,13 @@ public class MethodInvokerRewriter {
buf.append("]");
return buf.toString();
}
-
+
boolean hasParams(String descriptor) {
- return descriptor.charAt(1)!=')';
+ return descriptor.charAt(1) != ')';
}
-
+
/**
- * Generate bytecode to convert parameters on the stack into an array (based on the descriptor). If the
+ * Generate bytecode to convert parameters on the stack into an array (based on the descriptor). If the
* descriptor shows there are no parameters then null is stacked.
*
* @param descriptor MethodType descriptor showing parameters and return value
@@ -988,18 +1033,19 @@ public class MethodInvokerRewriter {
mv.visitInsn(ACONST_NULL);
}
}
-
+
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
// TODO *shudder* what about invoke dynamic calls that target reflective APIs
int classId = typeRegistry.getTypeIdFor(slashedclassname, true);
- if (classId==-1) {
- throw new IllegalStateException("Unable to find classId for "+slashedclassname+" referenced from invokedynamic in "+this.methodname+"()");
+ if (classId == -1) {
+ throw new IllegalStateException("Unable to find classId for " + slashedclassname
+ + " referenced from invokedynamic in " + this.methodname + "()");
}
// Initially only rewriting use of INVOKEDYNAMIC to support Lambda execution
// TODO support the more general invokedynamic usage
-
+
// Example data at this point:
// name=m
// desc=()Lbasic/LambdaA2$Foo;
@@ -1008,33 +1054,37 @@ public class MethodInvokerRewriter {
// desc=(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
// owner=java/lang/invoke/LambdaMetafactory
// bsmArgs=[ ()I basic/LambdaA2.lambda$run$1()I (6) ()I]
- if (bsm.getTag()==H_INVOKESTATIC && bsm.getName().equals("metafactory") && bsm.getOwner().equals("java/lang/invoke/LambdaMetafactory")) {
+ if (bsm.getTag() == H_INVOKESTATIC && bsm.getName().equals("metafactory")
+ && bsm.getOwner().equals("java/lang/invoke/LambdaMetafactory")) {
// System.out.println("InvokeDynamic(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs));
// Only when the BSM is LambdaMetafactory.metafactory are we rewriting the invokedynamic. Since LambdaMetafactory will not
// be getting reloaded, we can avoid a bunch of complexity. When the bsm points to a reloadable type we'll have to
// do more hoop jumping.
-
+
// Check on reloading having happened
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeDynamicName, "()Ljava/lang/Object;", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeDynamicName,
+ "()Ljava/lang/Object;", false);
// mv.visitInsn(DUP);
Label nochange = new Label();
mv.visitJumpInsn(IFNULL, nochange);
-
-// // 9. do what we were going to do
-// mv.visitLabel(l1);
-
+
+ // // 9. do what we were going to do
+ // mv.visitLabel(l1);
+
stackParameters(desc);
- int bsmReferenceId = typeRegistry.recordBootstrapMethod(slashedclassname,bsm,bsmArgs);
- // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
+ int bsmReferenceId = typeRegistry.recordBootstrapMethod(slashedclassname, bsm, bsmArgs);
+ // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
mv.visitLdcInsn(typeRegistry.getId());
mv.visitLdcInsn(classId);
- mv.visitMethodInsn(INVOKESTATIC,"java/lang/invoke/MethodHandles","lookup","()Ljava/lang/invoke/MethodHandles$Lookup;", false);
- mv.visitLdcInsn(name+desc); // Ljava/lang/String;
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup",
+ "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
+ mv.visitLdcInsn(name + desc); // Ljava/lang/String;
mv.visitLdcInsn(bsmReferenceId); // I
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mPerformInvokeDynamicName, "([Ljava/lang/Object;IILjava/lang/Object;Ljava/lang/String;I)Ljava/lang/Object;", false);
-
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mPerformInvokeDynamicName,
+ "([Ljava/lang/Object;IILjava/lang/Object;Ljava/lang/String;I)Ljava/lang/Object;", false);
+
Label gotolabel = new Label();
mv.visitJumpInsn(GOTO, gotolabel);
mv.visitLabel(nochange);
@@ -1043,14 +1093,17 @@ public class MethodInvokerRewriter {
}
else {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.WARNING)) {
- log.warning("[current limitation] not rewriting invokedynamic usage in type '"+slashedclassname+"'. InvokeDynamic(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs));
+ log.warning("[current limitation] not rewriting invokedynamic usage in type '"
+ + slashedclassname + "'. InvokeDynamic(name=" + name + ",desc=" + desc + ",bsm="
+ + toString(bsm) + ",bsmArgs=" + toString(bsmArgs));
}
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
}
-
+
@Override
- public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) {
+ public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc,
+ final boolean itf) {
if (GlobalConfiguration.interceptReflection && rewriteReflectiveCall(opcode, owner, name, desc)) {
return;
}
@@ -1068,7 +1121,8 @@ public class MethodInvokerRewriter {
// return;
// }
boolean isReloadable = typeRegistry != null
- && (owner.equals(slashedclassname) ? thisClassIsReloadable : typeRegistry.isReloadableTypeName(owner));
+ && (owner.equals(slashedclassname) ? thisClassIsReloadable
+ : typeRegistry.isReloadableTypeName(owner));
if (!isReloadable) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
return;
@@ -1080,14 +1134,19 @@ public class MethodInvokerRewriter {
int classId = typeRegistry.getTypeIdFor(owner, true);
if (opcode == INVOKESTATIC) {
rewriteINVOKESTATIC(opcode, owner, name, desc, hasParams, returnType, classId, itf);
- } else if (opcode == INVOKEINTERFACE) {
+ }
+ else if (opcode == INVOKEINTERFACE) {
rewriteINVOKEINTERFACE(opcode, owner, name, desc, hasParams, returnType, classId, itf);
- } else if (opcode == INVOKEVIRTUAL) {
+ }
+ else if (opcode == INVOKEVIRTUAL) {
rewriteINVOKEVIRTUAL(opcode, owner, name, desc, hasParams, returnType, classId, itf);
- } else if (opcode == INVOKESPECIAL) {
+ }
+ else if (opcode == INVOKESPECIAL) {
rewriteINVOKESPECIAL(opcode, owner, name, desc, hasParams, returnType, classId, itf);
- } else {
- Utils.logAndThrow(log, "Failed to rewrite instruction " + Utils.toOpcodeString(opcode) + " in method "
+ }
+ else {
+ Utils.logAndThrow(log, "Failed to rewrite instruction " + Utils.toOpcodeString(opcode)
+ + " in method "
+ this.methodname);
}
}
@@ -1123,7 +1182,8 @@ public class MethodInvokerRewriter {
/**
* Rewrite an INVOKESTATIC instruction.
*/
- private void rewriteINVOKESTATIC(final int opcode, final String owner, final String name, final String desc,
+ private void rewriteINVOKESTATIC(final int opcode, final String owner, final String name,
+ final String desc,
boolean hasParams, ReturnType returnType, int classId, boolean itf) {
// 1. call istcheck(classId|methodId,
// methodName+methodDescriptor)
@@ -1159,7 +1219,8 @@ public class MethodInvokerRewriter {
mv.visitVarInsn(ALOAD, max + 1); // dispatcher instance
mv.visitInsn(ACONST_NULL); // no parameters
mv.visitInsn(ACONST_NULL); // no instance, static method invocation
- } else {
+ }
+ else {
mv.visitVarInsn(ALOAD, max + 1); // dispatcher instance
mv.visitInsn(SWAP); // swap with that params array
mv.visitInsn(ACONST_NULL); // no instance, static method invocation
@@ -1169,7 +1230,8 @@ public class MethodInvokerRewriter {
mv.visitLdcInsn(name + desc);
// 7. calling __execute(params array,this,name+desc)
- mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(owner), mDynamicDispatchName, mDynamicDispatchDescriptor, true);
+ mv.visitMethodInsn(INVOKEINTERFACE, Utils.getInterfaceName(owner), mDynamicDispatchName,
+ mDynamicDispatchDescriptor, true);
insertAppropriateReturn(returnType);
// 8. jump over the original call
@@ -1183,17 +1245,19 @@ public class MethodInvokerRewriter {
}
/**
- * Based on the return type, insert the right return instructions. There will be an object on the stack when this method
- * is called - the object must be either discarded (void), unboxed (primitive) or cast (reference) depending on the
- * return type.
+ * Based on the return type, insert the right return instructions. There will be an object on the stack when
+ * this method is called - the object must be either discarded (void), unboxed (primitive) or cast
+ * (reference) depending on the return type.
*/
private void insertAppropriateReturn(ReturnType returnType) {
if (returnType.isVoid()) {
mv.visitInsn(POP); // throw the result away (it was null)
- } else {
+ }
+ else {
if (returnType.isPrimitive()) {
Utils.insertUnboxInsnsIfNecessary(mv, returnType.descriptor, true);
- } else {
+ }
+ else {
mv.visitTypeInsn(CHECKCAST, returnType.descriptor);
}
}
@@ -1202,17 +1266,19 @@ public class MethodInvokerRewriter {
/**
* All we need to do is know if the INVOKEINTERFACE that is about to run is OK to execute.
*
- * Invokeinterface rewriting is done by calling the type registry to see if what we are about to do is OK. The method we
- * call returns a boolean indicating whether it can be called directly or if we must direct it through the dynamic
- * dispatch method.
+ * Invokeinterface rewriting is done by calling the type registry to see if what we are about to do is OK.
+ * The method we call returns a boolean indicating whether it can be called directly or if we must direct it
+ * through the dynamic dispatch method.
*
*/
- private void rewriteINVOKEINTERFACE(final int opcode, final String owner, final String name, final String desc,
+ private void rewriteINVOKEINTERFACE(final int opcode, final String owner, final String name,
+ final String desc,
boolean hasParams, ReturnType returnType, int classId, final boolean itf) {
// 1. call 'boolean iicheck(classId|methodId, methodName+methodDescriptor)' to see if this needs interception
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
mv.visitLdcInsn(name + desc);
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeInterfaceName, "(ILjava/lang/String;)Z", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeInterfaceName,
+ "(ILjava/lang/String;)Z", false);
// 3. if false, do what was going to be done anyway
Label l1 = new Label();
@@ -1229,7 +1295,8 @@ public class MethodInvokerRewriter {
mv.visitInsn(DUP);
mv.visitInsn(ACONST_NULL); // no parameters
mv.visitInsn(SWAP); // [targetInstance NULL targetInstance]
- } else {
+ }
+ else {
// [targetInstance paramArray]
mv.visitInsn(SWAP);
mv.visitInsn(DUP_X1); // [targetInstance paramArray targetInstance]
@@ -1240,7 +1307,12 @@ public class MethodInvokerRewriter {
if (GlobalConfiguration.isJava18orHigher) {
// if the target is a generated lambda callsite object then calling __execute isn't going to work as those
// types don't have the method in them!
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, "iiIntercept", "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;", false);
+ mv.visitMethodInsn(
+ INVOKESTATIC,
+ tRegistryType,
+ "iiIntercept",
+ "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;",
+ false);
}
else {
// calling __execute(params array, this, name+desc)
@@ -1256,13 +1328,15 @@ public class MethodInvokerRewriter {
mv.visitLabel(gotolabel);
}
- private void rewriteINVOKEVIRTUAL(final int opcode, final String owner, final String name, final String desc,
+ private void rewriteINVOKEVIRTUAL(final int opcode, final String owner, final String name,
+ final String desc,
boolean hasParams, ReturnType returnType, int classId, final boolean itf) {
// 1. call icheck(classId|methodId, methodName+methodDescriptor)
// to see if this needs interception
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
mv.visitLdcInsn(name + desc);
- mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeVirtualName, "(ILjava/lang/String;)Z", false);
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeVirtualName, "(ILjava/lang/String;)Z",
+ false);
// Return value is the extracted interface to call if there is a
// change and it can't be called directly
@@ -1290,7 +1364,8 @@ public class MethodInvokerRewriter {
mv.visitInsn(DUP);
mv.visitInsn(ACONST_NULL); // no parameters
mv.visitInsn(SWAP); // [targetInstance NULL targetInstance]
- } else {
+ }
+ else {
// [targetInstance paramArray]
mv.visitInsn(SWAP);
mv.visitInsn(DUP_X1); // [targetInstance paramArray
@@ -1316,11 +1391,12 @@ public class MethodInvokerRewriter {
/**
* Rewrite an INVOKESPECIAL that has been encountered in the code.
*
- * The basic premise is then simple: call the TypeRegistry to check whether we can make the call we want to make. If we
- * can then just do it, if we can't then that method will return a dispatcher instance that can handle the method so
- * package up our parameters and invoke it.
+ * The basic premise is then simple: call the TypeRegistry to check whether we can make the call we want to
+ * make. If we can then just do it, if we can't then that method will return a dispatcher instance that can
+ * handle the method so package up our parameters and invoke it.
*/
- private void rewriteINVOKESPECIAL(final int opcode, final String owner, final String name, final String desc,
+ private void rewriteINVOKESPECIAL(final int opcode, final String owner, final String name,
+ final String desc,
boolean hasParams, ReturnType returnType, int classId, final boolean itf) {
if (unitializedObjectsCount == -1 && name.charAt(0) == '<') {
super.visitMethodInsn(opcode, owner, name, desc, itf);
@@ -1336,7 +1412,8 @@ public class MethodInvokerRewriter {
if (name.charAt(0) == '<') {
// constructor
- if (isEnum && isClinitOrEnumInit && fieldcount > GlobalConfiguration.enumLimit && owner.equals(slashedclassname)) {
+ if (isEnum && isClinitOrEnumInit && fieldcount > GlobalConfiguration.enumLimit
+ && owner.equals(slashedclassname)) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
@@ -1397,8 +1474,10 @@ public class MethodInvokerRewriter {
Utils.insertUnboxInsns(mv, 'I', true);
mv.visitInsn(ACONST_NULL);
- mv.visitMethodInsn(INVOKESPECIAL, owner, "", "(Ljava/lang/String;ILorg/springsource/loaded/C;)V", itf);
- } else if (owner.contains("_closure")) { // TODO need more robust way to identify when target is a closure?
+ mv.visitMethodInsn(INVOKESPECIAL, owner, "",
+ "(Ljava/lang/String;ILorg/springsource/loaded/C;)V", itf);
+ }
+ else if (owner.contains("_closure")) { // TODO need more robust way to identify when target is a closure?
mv.visitInsn(SWAP); // now params array on top instance underneath
mv.visitInsn(DUP_X1); // give us an array instance to retrieve 1 from
mv.visitInsn(DUP); // give us an array instance to retrieve 0 from
@@ -1410,7 +1489,8 @@ public class MethodInvokerRewriter {
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKESPECIAL, owner, "",
"(Ljava/lang/Object;Ljava/lang/Object;Lorg/springsource/loaded/C;)V", itf);
- } else {
+ }
+ else {
mv.visitInsn(ACONST_NULL);
mv.visitMethodInsn(INVOKESPECIAL, owner, "", "(Lorg/springsource/loaded/C;)V", itf);
}
@@ -1425,7 +1505,8 @@ public class MethodInvokerRewriter {
// stack is now the dispatcher instance then the params then the instance
mv.visitLdcInsn(name + desc);
// stack is now the dispatcher instance, the params, the instance and the name+desc!
- } else {
+ }
+ else {
// stack is now the two instances
mv.visitInsn(DUP);
mv.visitInsn(ACONST_NULL);
@@ -1440,7 +1521,8 @@ public class MethodInvokerRewriter {
mv.visitLdcInsn(name + desc);
// stack is now the dispatcher instance, null, the instance and the name+desc!
}
- mv.visitMethodInsn(INVOKEINTERFACE, "org/springsource/loaded/__DynamicallyDispatchable", mDynamicDispatchName,
+ mv.visitMethodInsn(INVOKEINTERFACE, "org/springsource/loaded/__DynamicallyDispatchable",
+ mDynamicDispatchName,
mDynamicDispatchDescriptor, true);
mv.visitInsn(POP);
// mv.visitMethodInsn(INVOKESPECIAL, "ctors/Callee", "", "()V");
@@ -1461,7 +1543,8 @@ public class MethodInvokerRewriter {
super.visitMethodInsn(opcode, owner, name, desc, itf);
mv.visitLabel(gotolabel);
- } else {
+ }
+ else {
// 1. call ispcheck(classId|methodId, methodName+methodDescriptor) to see if this needs interception
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
@@ -1498,7 +1581,8 @@ public class MethodInvokerRewriter {
mv.visitVarInsn(ALOAD, max + 1); // dispatcher instance
mv.visitInsn(ACONST_NULL); // no parameters
mv.visitVarInsn(ALOAD, max + 2); // instance
- } else {
+ }
+ else {
mv.visitVarInsn(ALOAD, max + 1); // dispatcher instance
mv.visitInsn(SWAP); // swap with that params array
mv.visitVarInsn(ALOAD, max + 2); // instance
@@ -1506,7 +1590,8 @@ public class MethodInvokerRewriter {
mv.visitLdcInsn(name + desc);
- mv.visitMethodInsn(INVOKEINTERFACE, "org/springsource/loaded/__DynamicallyDispatchable", mDynamicDispatchName,
+ mv.visitMethodInsn(INVOKEINTERFACE, "org/springsource/loaded/__DynamicallyDispatchable",
+ mDynamicDispatchName,
mDynamicDispatchDescriptor, true);
insertAppropriateReturn(returnType);
@@ -1564,8 +1649,10 @@ public class MethodInvokerRewriter {
// methodName.append(pieces[pieces.length - 1]);
// methodName.append(Character.toUpperCase(name.charAt(0)));
// methodName.append(name.substring(1));
- StringBuilder newDescriptor = new StringBuilder("(L").append(owner).append(";").append(desc, 1, desc.length());
- mv.visitMethodInsn(INVOKESTATIC, "org/springsource/loaded/ri/ReflectiveInterceptor", methodName.toString(),
+ StringBuilder newDescriptor = new StringBuilder("(L").append(owner).append(";").append(desc, 1,
+ desc.length());
+ mv.visitMethodInsn(INVOKESTATIC, "org/springsource/loaded/ri/ReflectiveInterceptor",
+ methodName.toString(),
newDescriptor.toString());
rewroteReflection = true;
diff --git a/springloaded/src/main/java/org/springsource/loaded/MethodMember.java b/springloaded/src/main/java/org/springsource/loaded/MethodMember.java
index bd74fcd..9b7466b 100644
--- a/springloaded/src/main/java/org/springsource/loaded/MethodMember.java
+++ b/springloaded/src/main/java/org/springsource/loaded/MethodMember.java
@@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
- * Representation of a Method. Some of the bitflags and state are only set for 'incremental' methods - those found in a secondary
- * type descriptor representing a type reload.
+ * Representation of a Method. Some of the bitflags and state are only set for 'incremental' methods - those found in a
+ * secondary type descriptor representing a type reload.
*
* @author Andy Clement
* @since 0.5.0
@@ -35,17 +36,25 @@ public class MethodMember extends AbstractMember {
// computed up front:
public final static int BIT_CATCHER = 0x001;
+
public final static int BIT_CLASH = 0x0002;
+
// identifies a catcher method placed into an abstract class (where a method from a super interface hasn't been implemented)
public final static int BIT_CATCHER_INTERFACE = 0x004;
+
public final static int BIT_SUPERDISPATCHER = 0x0008;
// computed on incremental members to indicate what changed:
public final static int MADE_STATIC = 0x0010;
+
public final static int MADE_NON_STATIC = 0x0020;
+
public final static int VISIBILITY_CHANGE = 0x0040;
+
public final static int IS_NEW = 0x0080;
+
public final static int WAS_DELETED = 0x0100;
+
public final static int EXCEPTIONS_CHANGE = 0x0200;
// For MethodMembers in an incremental type descriptor, this tracks the method in the original type descriptor (if there was one)
@@ -155,12 +164,14 @@ public class MethodMember extends AbstractMember {
int newModifiers = modifiers & ~Modifier.NATIVE;
if (name.equals("clone") && (modifiers & Modifier.NATIVE) != 0) {
newModifiers = Modifier.PUBLIC;
- } else if ((modifiers & Modifier.PROTECTED) != 0) {
+ }
+ else if ((modifiers & Modifier.PROTECTED) != 0) {
// promote to public
// The reason for this is that the executor may try and call these things and as it is not in the hierarchy
// it cannot. The necessary knock on effect is that subtypes get their methods promoted to public too...
newModifiers = Modifier.PUBLIC;
- } else if ((modifiers & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
+ }
+ else if ((modifiers & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
// promote to public from default
// The reason for this is that the executor may try and call these things and as it is not in the hierarchy
// it cannot. The necessary knock on effect is that subtypes get their methods promoted to public too...
@@ -170,23 +181,26 @@ public class MethodMember extends AbstractMember {
copy.bits |= MethodMember.BIT_CATCHER;
return copy;
}
-
+
public MethodMember superDispatcherFor() {
int newModifiers = modifiers & ~Modifier.NATIVE;
if (name.equals("clone") && (modifiers & Modifier.NATIVE) != 0) {
newModifiers = Modifier.PUBLIC;
- } else if ((modifiers & Modifier.PROTECTED) != 0) {
+ }
+ else if ((modifiers & Modifier.PROTECTED) != 0) {
// promote to public
// The reason for this is that the executor may try and call these things and as it is not in the hierarchy
// it cannot. The necessary knock on effect is that subtypes get their methods promoted to public too...
newModifiers = Modifier.PUBLIC;
- } else if ((modifiers & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
+ }
+ else if ((modifiers & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
// promote to public from default
// The reason for this is that the executor may try and call these things and as it is not in the hierarchy
// it cannot. The necessary knock on effect is that subtypes get their methods promoted to public too...
newModifiers = Modifier.PUBLIC;
}
- MethodMember copy = new MethodMember(newModifiers, name+"_$superdispatcher$", descriptor, signature, exceptions);
+ MethodMember copy = new MethodMember(newModifiers, name + "_$superdispatcher$", descriptor, signature,
+ exceptions);
copy.bits |= MethodMember.BIT_SUPERDISPATCHER;
return copy;
}
@@ -195,12 +209,14 @@ public class MethodMember extends AbstractMember {
int newModifiers = modifiers & ~(Modifier.NATIVE | Modifier.ABSTRACT);
if (name.equals("clone") && (modifiers & Modifier.NATIVE) != 0) {
newModifiers = Modifier.PUBLIC;
- } else if ((modifiers & Modifier.PROTECTED) != 0) {
+ }
+ else if ((modifiers & Modifier.PROTECTED) != 0) {
// promote to public
// The reason for this is that the executor may try and call these things and as it is not in the hierarchy
// it cannot. The necessary knock on effect is that subtypes get their methods promoted to public too...
newModifiers = Modifier.PUBLIC;
- } else if ((modifiers & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
+ }
+ else if ((modifiers & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
// promote to public from default
// The reason for this is that the executor may try and call these things and as it is not in the hierarchy
// it cannot. The necessary knock on effect is that subtypes get their methods promoted to public too...
@@ -241,7 +257,7 @@ public class MethodMember extends AbstractMember {
public static boolean isClash(MethodMember method) {
return (method.bits & MethodMember.BIT_CLASH) != 0;
}
-
+
public static boolean isSuperDispatcher(MethodMember method) {
return (method.bits & BIT_SUPERDISPATCHER) != 0;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/NameRegistry.java b/springloaded/src/main/java/org/springsource/loaded/NameRegistry.java
index 57da77c..56aa9d0 100644
--- a/springloaded/src/main/java/org/springsource/loaded/NameRegistry.java
+++ b/springloaded/src/main/java/org/springsource/loaded/NameRegistry.java
@@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
- * Manages a mapping of names to numbers. The same number anywhere means the same name. This means that if some type a/b/C has been
- * loaded in two places (by different classloaders), it will have the same number in both. Only one of those a/b/C types will be
- * visible at the location in question, the tricky part could be working out which one (if classloaders are being naughty) but in
- * theory the first time we get confused (due to finding the name twice), we can work out which one is right and use that mapping
- * from then on.
+ * Manages a mapping of names to numbers. The same number anywhere means the same name. This means that if some type
+ * a/b/C has been loaded in two places (by different classloaders), it will have the same number in both. Only one of
+ * those a/b/C types will be visible at the location in question, the tricky part could be working out which one (if
+ * classloaders are being naughty) but in theory the first time we get confused (due to finding the name twice), we can
+ * work out which one is right and use that mapping from then on.
*
* @author Andy Clement
* @since 0.8.1
@@ -28,7 +29,9 @@ package org.springsource.loaded;
public class NameRegistry {
private static int nextTypeId = 0;
+
private static int size = 10;
+
private static String[] allocatedIds = new String[size];
private NameRegistry() {
@@ -44,8 +47,8 @@ public class NameRegistry {
}
/**
- * Return the id for a particular type. This method will not allocate a new id if the type is unknown, it will return -1
- * instead.
+ * Return the id for a particular type. This method will not allocate a new id if the type is unknown, it will
+ * return -1 instead.
*
* @param slashedClassName a type name like java/lang/String
* @return the allocated ID for that type or -1 if unknown
@@ -61,8 +64,8 @@ public class NameRegistry {
}
/**
- * Return the id for a particular type. This method will not allocate a new id if the type is unknown, it will return -1
- * instead.
+ * Return the id for a particular type. This method will not allocate a new id if the type is unknown, it will
+ * return -1 instead.
*
* @param slashedClassName a type name like java/lang/String
* @return the allocated ID for that type or -1 if unknown
diff --git a/springloaded/src/main/java/org/springsource/loaded/Plugin.java b/springloaded/src/main/java/org/springsource/loaded/Plugin.java
index 0730ec7..134452a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Plugin.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Plugin.java
@@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
- * Top level interface for Spring-Loaded plugins. Plugins are allowed to participate in class loading and modify code as it is
- * loaded and can be notified as types are reloaded.
+ * Top level interface for Spring-Loaded plugins. Plugins are allowed to participate in class loading and modify code as
+ * it is loaded and can be notified as types are reloaded.
*
- * Implementations should be registered by creating a META-INF/services/org.springsource.reloading.agent.Plugins file that lists
- * (one per line) the plugin classes, for example: org.springsource.loaded.ReloadEventProcessorPluginImpl
+ * Implementations should be registered by creating a META-INF/services/org.springsource.reloading.agent.Plugins file
+ * that lists (one per line) the plugin classes, for example: org.springsource.loaded.ReloadEventProcessorPluginImpl
*
* @author Andy Clement
* @since 0.5.0
diff --git a/springloaded/src/main/java/org/springsource/loaded/Plugins.java b/springloaded/src/main/java/org/springsource/loaded/Plugins.java
index 4503463..52bbba3 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Plugins.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Plugins.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.springsource.loaded.agent.SpringLoadedPreProcessor;
@@ -27,6 +28,7 @@ import org.springsource.loaded.agent.SpringLoadedPreProcessor;
* @since 0.7.2
*/
public class Plugins {
+
public static void registerGlobalPlugin(Plugin instance) {
SpringLoadedPreProcessor.registerGlobalPlugin(instance);
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/PrefixTypePattern.java b/springloaded/src/main/java/org/springsource/loaded/PrefixTypePattern.java
index eab6425..b01e758 100644
--- a/springloaded/src/main/java/org/springsource/loaded/PrefixTypePattern.java
+++ b/springloaded/src/main/java/org/springsource/loaded/PrefixTypePattern.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
diff --git a/springloaded/src/main/java/org/springsource/loaded/QuickVisitor.java b/springloaded/src/main/java/org/springsource/loaded/QuickVisitor.java
index dcdc5ac..a4bd2ce 100644
--- a/springloaded/src/main/java/org/springsource/loaded/QuickVisitor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/QuickVisitor.java
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import org.objectweb.asm.ClassReader;
/**
- * Can be used to take a quick look in the bytecode for something. The various static get* methods are the things that the quick
- * visitor can discover.
+ * Can be used to take a quick look in the bytecode for something. The various static get* methods are the things that
+ * the quick visitor can discover.
*
* @author Andy Clement
* @since 0.7.3
@@ -31,12 +32,14 @@ public class QuickVisitor {
QuickVisitor1 qv = new QuickVisitor1();
try {
fileReader.accept(qv, ClassReader.SKIP_FRAMES);// TODO more flags to skip other things?
- } catch (EarlyExitException eee) {
+ }
+ catch (EarlyExitException eee) {
}
return qv.interfaces;
}
static class QuickVisitor1 extends EmptyClassVisitor {
+
String[] interfaces;
@Override
@@ -150,4 +153,4 @@ public class QuickVisitor {
//
// }
// }
-}
\ No newline at end of file
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/RTH.java b/springloaded/src/main/java/org/springsource/loaded/RTH.java
index 94c67f2..2008d35 100644
--- a/springloaded/src/main/java/org/springsource/loaded/RTH.java
+++ b/springloaded/src/main/java/org/springsource/loaded/RTH.java
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
// TODO many more to add here - would drastically reduce amount of generated bytecode but the cost would be debugging confusion
// TODO can these methods be made synthetic (at load time) so they don't interfere with the debugger?
// TODO can we check on whether debugging is going to happen and then choose whether to use these helper methods at startup?
/**
- * Runtime Helper Class. Provides utility methods called by generated code to perform common functions. Using these does reduce the
- * amount of generated bytecode but it introduces extra paths into the code (calls) that a debugger might step into.
+ * Runtime Helper Class. Provides utility methods called by generated code to perform common functions. Using these does
+ * reduce the amount of generated bytecode but it introduces extra paths into the code (calls) that a debugger might
+ * step into.
*
*
* @author Andy Clement
diff --git a/springloaded/src/main/java/org/springsource/loaded/ReflectionFieldReaderWriter.java b/springloaded/src/main/java/org/springsource/loaded/ReflectionFieldReaderWriter.java
index 5d549cd..aba354a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ReflectionFieldReaderWriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ReflectionFieldReaderWriter.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.reflect.Field;
@@ -34,7 +35,8 @@ public class ReflectionFieldReaderWriter extends FieldReaderWriter {
}
@Override
- public Object getStaticFieldValue(Class> type, SSMgr fieldAccessor) throws IllegalAccessException, IllegalArgumentException {
+ public Object getStaticFieldValue(Class> type, SSMgr fieldAccessor) throws IllegalAccessException,
+ IllegalArgumentException {
field.setAccessible(true);
return field.get(null);
}
@@ -52,7 +54,8 @@ public class ReflectionFieldReaderWriter extends FieldReaderWriter {
}
@Override
- public Object getValue(Object instance, ISMgr fieldAccessor) throws IllegalAccessException, IllegalArgumentException {
+ public Object getValue(Object instance, ISMgr fieldAccessor) throws IllegalAccessException,
+ IllegalArgumentException {
field.setAccessible(true);
return field.get(instance);
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadEventProcessorPlugin.java b/springloaded/src/main/java/org/springsource/loaded/ReloadEventProcessorPlugin.java
index 4ec9eae..f57295f 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ReloadEventProcessorPlugin.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ReloadEventProcessorPlugin.java
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
- * ReloadEventProcessor Plugins are called when a type is reloading. For information on registering them, see {@link Plugin}
+ * ReloadEventProcessor Plugins are called when a type is reloading. For information on registering them, see
+ * {@link Plugin}
*
* @author Andy Clement
* @since 0.5.0
@@ -24,8 +26,8 @@ package org.springsource.loaded;
public interface ReloadEventProcessorPlugin extends Plugin {
/**
- * Called when a type has been reloaded, allows the plugin to decide if the static initializer should be re-run for the reloaded
- * type. If the reloaded type has a different static initializer, the new one is the one that will run.
+ * Called when a type has been reloaded, allows the plugin to decide if the static initializer should be re-run for
+ * the reloaded type. If the reloaded type has a different static initializer, the new one is the one that will run.
*
* @param typename the (dotted) type name, for example java.lang.String
* @param clazz the Class object that has been reloaded
@@ -37,10 +39,10 @@ public interface ReloadEventProcessorPlugin extends Plugin {
// TODO expose detailed delta for changes in the type? (i.e. what new fields/methods/etc)
// TODO expose instances when they are being tracked?
/**
- * Called when a type has been reloaded. Note, the class is only truly defined to the VM once, and so the Class object (clazz
- * parameter) is always the same for the same type (ignoring multiple classloader situations). It is passed here so that plugins
- * processing events can clear any cached state related to it. The encodedTimestamp is an encoding of the ID that the agent has
- * assigned to this reloaded version of this type.
+ * Called when a type has been reloaded. Note, the class is only truly defined to the VM once, and so the Class
+ * object (clazz parameter) is always the same for the same type (ignoring multiple classloader situations). It is
+ * passed here so that plugins processing events can clear any cached state related to it. The encodedTimestamp is
+ * an encoding of the ID that the agent has assigned to this reloaded version of this type.
*
* @param typename the (dotted) type name, for example java.lang.String
* @param clazz the Class object that has been reloaded
diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadException.java b/springloaded/src/main/java/org/springsource/loaded/ReloadException.java
index cf3c315..f3b10c0 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ReloadException.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ReloadException.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
/**
diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
index 93423ae..511b435 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springsource.loaded;
import java.lang.ref.Reference;
@@ -95,15 +96,19 @@ public class ReloadableType {
private Class> superclazz;
private ReloadableType superRtype;
+
private ReloadableType[] interfaceRtypes;
List> associatedSubtypes = null;
- /** Caches Method objects for this reloadable type. This cache should be invalidated (set to null) when a type is reloaded! */
+ /**
+ * Caches Method objects for this reloadable type. This cache should be invalidated (set to null) when a type is
+ * reloaded!
+ */
private JavaMethodCache javaMethodCache;
private final static int IS_RESOLVED = 0x0001;
-
+
// Indicates that this type or one in its hierarchy (super/sub) has been reloaded
private final static int IMPACTED_BY_RELOAD = 0x0002;
@@ -111,9 +116,12 @@ public class ReloadableType {
/** Cache of the invokers used to answer getDeclaredMethods() call made on this type */
public List invokersCache_getDeclaredMethods = null;
+
// TODO clear these out on reload
public Collection invokersCache_getMethods = null;
+
public Map> invokerCache_getMethod = new HashMap>();
+
public Map> invokerCache_getDeclaredMethod = new HashMap>();
public Class> getClazz() {
@@ -122,8 +130,10 @@ public class ReloadableType {
// classloader has defined it.
try {
clazz = Class.forName(dottedtypename, false, typeRegistry.getClassLoader());
- } catch (ClassNotFoundException cnfe) {
- throw new ReloadException("Unexpectedly unable to find class " + this.dottedtypename + ". Asked classloader "
+ }
+ catch (ClassNotFoundException cnfe) {
+ throw new ReloadException("Unexpectedly unable to find class " + this.dottedtypename
+ + ". Asked classloader "
+ typeRegistry.getClassLoader(), cnfe);
}
}
@@ -150,12 +160,13 @@ public class ReloadableType {
}
this.id = id;
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
- log.info("New reloadable type: "+dottedtypename+ " (allocatedId="+id+") "+typeRegistry.toString());
+ log.info("New reloadable type: " + dottedtypename + " (allocatedId=" + id + ") " + typeRegistry.toString());
}
this.typeRegistry = typeRegistry;
this.dottedtypename = dottedtypename;
this.slashedtypename = dottedtypename.replace('.', '/');
- this.typedescriptor = (typeDescriptor != null ? typeDescriptor : typeRegistry.getExtractor().extract(initialBytes, true));
+ this.typedescriptor = (typeDescriptor != null ? typeDescriptor : typeRegistry.getExtractor().extract(
+ initialBytes, true));
this.interfaceBytes = InterfaceExtractor.extract(initialBytes, typeRegistry, this.typedescriptor);
this.bytesInitial = initialBytes;
rewriteCallSitesAndDefine();
@@ -168,6 +179,7 @@ public class ReloadableType {
}
public final static ReloadableType NOT_RELOADABLE_TYPE = new ReloadableType();
+
public final static WeakReference NOT_RELOADABLE_TYPE_REF = new WeakReference(
NOT_RELOADABLE_TYPE);
@@ -176,8 +188,8 @@ public class ReloadableType {
}
/**
- * Gets the 'orignal' method corresponding to given name and method descriptor. This only considers methods that exist in the
- * first (non-reloaded) version of the type.
+ * Gets the 'orignal' method corresponding to given name and method descriptor. This only considers methods that
+ * exist in the first (non-reloaded) version of the type.
*
* @param name method name
* @param descriptor method descriptor (e.g (Ljava/lang/String;)I)
@@ -190,7 +202,8 @@ public class ReloadableType {
return method;
}
}
- throw new IllegalStateException("Unable to find member '" + name + descriptor + "' on type " + this.dottedtypename);
+ throw new IllegalStateException("Unable to find member '" + name + descriptor + "' on type "
+ + this.dottedtypename);
}
public MethodMember getConstructor(String descriptor) {
@@ -199,13 +212,15 @@ public class ReloadableType {
return ctor;
}
}
- throw new IllegalStateException("Unable to find constructor '" + descriptor + "' on type " + this.dottedtypename);
+ throw new IllegalStateException("Unable to find constructor '" + descriptor + "' on type "
+ + this.dottedtypename);
}
// TODO what about 'regular' spring load time weaving?
/**
- * This method will attempt to apply any pre-existing transforms to the provided bytecode, if it is thought to be necessary.
- * Currently 'necessary' is determined by finding ourselves running under tcServer and Spring Insight being turned on.
+ * This method will attempt to apply any pre-existing transforms to the provided bytecode, if it is thought to be
+ * necessary. Currently 'necessary' is determined by finding ourselves running under tcServer and Spring Insight
+ * being turned on.
*
* @param bytes the new bytes to be possibly transformed.
* @return either the original bytes or a transformed set of bytes
@@ -231,7 +246,8 @@ public class ReloadableType {
log.info("Determining if retransform necessary, result = " + retransformNecessary);
}
}
- } catch (Exception e) {
+ }
+ catch (Exception e) {
log.log(Level.SEVERE, "Unexpected exception when determining if Spring Insight enabled", e);
retransformNecessary = false;
}
@@ -247,7 +263,8 @@ public class ReloadableType {
log.info("retransform was attempted, oldsize=" + bytes.length + " newsize=" + newdata.length);
}
return newdata;
- } catch (Exception e) {
+ }
+ catch (Exception e) {
if (GlobalConfiguration.isRuntimeLogging) {
log.log(Level.SEVERE, "Unexpected exception when trying to run other weaving transformers", e);
}
@@ -257,8 +274,11 @@ public class ReloadableType {
}
private boolean determinedNeedToRetransform = false;
+
private boolean retransformNecessary = false;
+
private Object retransformWeavingTransformer;
+
private java.lang.reflect.Method retransformWeavingTransformMethod;
// for lazy tests that are only loading one new version, fill in the versionsuffix for them
@@ -288,7 +308,8 @@ public class ReloadableType {
public boolean loadNewVersion(String versionsuffix, byte[] newbytedata) {
javaMethodCache = null;
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
- log.info("Loading new version of "+slashedtypename+", identifying suffix "+versionsuffix+", new data length is "+newbytedata.length+"bytes");
+ log.info("Loading new version of " + slashedtypename + ", identifying suffix " + versionsuffix
+ + ", new data length is " + newbytedata.length + "bytes");
}
// If we find our parent classloader has a weavingTransformer
@@ -307,13 +328,15 @@ public class ReloadableType {
// Not allowed to change the type
reload = false;
- s = new StringBuilder("Spring Loaded: Cannot reload new version of ").append(this.dottedtypename).append("\n");
+ s = new StringBuilder("Spring Loaded: Cannot reload new version of ").append(this.dottedtypename).append(
+ "\n");
if (td.hasTypeAccessChanged()) {
s.append(" Reason: Type modifiers changed\n");
cantReload = true;
}
if (td.hasTypeSupertypeChanged()) {
- s.append(" Reason: Supertype changed from ").append(td.oSuperName).append(" to ").append(td.nSuperName)
+ s.append(" Reason: Supertype changed from ").append(td.oSuperName).append(" to ").append(
+ td.nSuperName)
.append("\n");
cantReload = true;
}
@@ -388,7 +411,8 @@ public class ReloadableType {
invokersCache_getDeclaredMethods = null; // will no longer use this cache
if (GlobalConfiguration.reloadMessages) {
// Only put out the message when running in limit mode (under tc Server)
- System.out.println("Reloading: Loading new version of " + this.dottedtypename + " [" + versionsuffix + "]");
+ System.out.println("Reloading: Loading new version of " + this.dottedtypename + " [" + versionsuffix
+ + "]");
}
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("Reloading: Loading new version of " + this.dottedtypename + " [" + versionsuffix + "]");
@@ -405,7 +429,8 @@ public class ReloadableType {
if (typeRegistry.shouldRerunStaticInitializer(this, versionsuffix) || typedescriptor.isEnum()) {
liveVersion.staticInitializedNeedsRerunningOnDefine = true;
liveVersion.runStaticInitializer();
- } else {
+ }
+ else {
liveVersion.staticInitializedNeedsRerunningOnDefine = false;
}
// For performance:
@@ -425,7 +450,7 @@ public class ReloadableType {
private void tagSupertypesAsAffectedByReload() {
ReloadableType superRtype = getSuperRtype();
- if (superRtype!=null) {
+ if (superRtype != null) {
superRtype.tagAsAffectedByReload();
// need to recurse up with the tagging
superRtype.tagSupertypesAsAffectedByReload();
@@ -433,8 +458,8 @@ public class ReloadableType {
// need to recurse through super interfaces too
ReloadableType[] superinterfaceRtypes = getInterfacesRtypes();
- if (superinterfaceRtypes!=null) {
- for (ReloadableType superinterfaceRtype: superinterfaceRtypes) {
+ if (superinterfaceRtypes != null) {
+ for (ReloadableType superinterfaceRtype : superinterfaceRtypes) {
superinterfaceRtype.tagAsAffectedByReload();
superinterfaceRtype.tagSupertypesAsAffectedByReload();
}
@@ -443,8 +468,8 @@ public class ReloadableType {
// TODO who is clearing up dead entries?
private void tagSubtypesAsAffectedByReload() {
- if (associatedSubtypes !=null) {
- for (Reference ref: associatedSubtypes) {
+ if (associatedSubtypes != null) {
+ for (Reference ref : associatedSubtypes) {
ReloadableType rsubtype = ref.get();
if (rsubtype != null) {
rsubtype.tagAsAffectedByReload();
@@ -453,18 +478,17 @@ public class ReloadableType {
}
}
}
-
+
private void tagAsAffectedByReload() {
bits |= IMPACTED_BY_RELOAD;
invokersCache_getMethods = null;
invokersCache_getDeclaredMethods = null;
}
-
+
public boolean isAffectedByReload() {
- return (bits&IMPACTED_BY_RELOAD)!=0;
+ return (bits & IMPACTED_BY_RELOAD) != 0;
}
-
-
+
// TODO cache these field objects to avoid digging for them every time?
/**
@@ -479,14 +503,16 @@ public class ReloadableType {
Field f = clazz.getClass().getDeclaredField("enumConstants");
f.setAccessible(true);
f.set(clazz, null);
- } catch (Exception e) {
+ }
+ catch (Exception e) {
e.printStackTrace();
}
try {
Field f = clazz.getClass().getDeclaredField("enumConstantDirectory");
f.setAccessible(true);
f.set(clazz, null);
- } catch (Exception e) {
+ }
+ catch (Exception e) {
e.printStackTrace();
}
}
@@ -507,10 +533,11 @@ public class ReloadableType {
// TODO Subclassloader lookups (via subregistries) when the cglib proxies are being loaded below this registry
// TODO caching discovered Method objects
/**
- * Go through proxies we know about in this registry and see if any of them are for the type we have just reloaded. If they are,
- * regenerate them and reload them.
+ * Go through proxies we know about in this registry and see if any of them are for the type we have just reloaded.
+ * If they are, regenerate them and reload them.
*
- * @param versionsuffix the suffix to use when reloading the proxies (it matches what is being used to reload the type)
+ * @param versionsuffix the suffix to use when reloading the proxies (it matches what is being used to reload the
+ * type)
*/
private void reloadProxiesIfNecessary(String versionsuffix) {
ReloadableType proxy = typeRegistry.cglibProxies.get(this.slashedtypename);
@@ -537,7 +564,8 @@ public class ReloadableType {
byte[] bs = (byte[]) found.invoke(a, b);
proxy.loadNewVersion(versionsuffix, bs);
proxy.runStaticInitializer();
- } catch (Throwable t) {
+ }
+ catch (Throwable t) {
t.printStackTrace();
}
}
@@ -564,7 +592,8 @@ public class ReloadableType {
byte[] bs = (byte[]) found.invoke(a, b);
proxy.loadNewVersion(versionsuffix, bs);
proxy.runStaticInitializer();
- } catch (Throwable t) {
+ }
+ catch (Throwable t) {
t.printStackTrace();
}
}
@@ -578,14 +607,16 @@ public class ReloadableType {
for (ReloadableType relevantProxy : relevantProxies) {
Class>[] interfacesImplementedByProxy = relevantProxy.getClazz().getInterfaces();
// check slashedname correct
-// @SuppressWarnings("restriction")
- byte[] newProxyBytes = sun.misc.ProxyGenerator.generateProxyClass(relevantProxy.getSlashedName(),
+ // @SuppressWarnings("restriction")
+ byte[] newProxyBytes = sun.misc.ProxyGenerator.generateProxyClass(
+ relevantProxy.getSlashedName(),
interfacesImplementedByProxy);
relevantProxy.loadNewVersion(versionsuffix, newProxyBytes, true);
}
}
}
- } catch (Throwable t) {
+ }
+ catch (Throwable t) {
new RuntimeException("Unexpected problem trying to reload proxy for interface " + this.dottedtypename, t)
.printStackTrace();
}
@@ -594,14 +625,16 @@ public class ReloadableType {
Object[] reflectiveTargets;
private final static int INDEX_SWAPINIT_METHOD = 0;
+
private final static int INDEX_CALLSITEARRAY_FIELD = 1;
+
private final static int INDEX_METACLASS_FIELD = 2;
/**
* Groovy types need some extra fixup:
*
- *
they contain a callsite array that caches destinations for calls. It needs clearing (it will be reinitialized when
- * required)
+ *
they contain a callsite array that caches destinations for calls. It needs clearing (it will be reinitialized
+ * when required)
*
not quite sure about the two: $staticClassInfo and GroovySystem removeMetaClass
*
@@ -613,22 +646,26 @@ public class ReloadableType {
reflectiveTargets = new Object[5];
try {
reflectiveTargets[INDEX_SWAPINIT_METHOD] = clazz.getDeclaredMethod("__$swapInit");
- } catch (Exception e) {
+ }
+ catch (Exception e) {
s.append("cannot discover __$swapInit " + e.toString() + " -- ");
}
try {
reflectiveTargets[INDEX_CALLSITEARRAY_FIELD] = clazz.getDeclaredField("$callSiteArray");
- } catch (Exception e) {
+ }
+ catch (Exception e) {
s.append("cannot discover $callSiteArray " + e.toString() + " -- ");
}
try {
reflectiveTargets[INDEX_METACLASS_FIELD] = clazz.getDeclaredField("$class$groovy$lang$MetaClass");
- } catch (Exception e) {
+ }
+ catch (Exception e) {
s.append("cannot discover $class$groovy$lang$MetaClass " + e.toString() + " -- ");
}
try {
reflectiveTargets[3] = clazz.getDeclaredField("$staticClassInfo");
- } catch (Exception e) {
+ }
+ catch (Exception e) {
s.append("cannot discover $staticClassInfo " + e.toString() + " -- ");
}
}
@@ -658,7 +695,8 @@ public class ReloadableType {
f.setAccessible(true);
f.set(null, null);
}
- } catch (Exception e) {
+ }
+ catch (Exception e) {
s.append("cannot reset state" + e.toString() + " -- ");
// new RuntimeException("Unable to fix up groovy state for " + this.dottedtypename, e);
}
@@ -668,11 +706,14 @@ public class ReloadableType {
Field metaClassRegistryField = clazz.getDeclaredField("META_CLASS_REGISTRY");
metaClassRegistryField.setAccessible(true);
Object metaClassRegistry = metaClassRegistryField.get(null);
- Method metaClassRegistryMethod = metaClassRegistry.getClass().getDeclaredMethod("removeMetaClass", Class.class);
+ Method metaClassRegistryMethod = metaClassRegistry.getClass().getDeclaredMethod("removeMetaClass",
+ Class.class);
metaClassRegistryMethod.setAccessible(true);
metaClassRegistryMethod.invoke(metaClassRegistry, getClazz());
- } catch (Exception e) {
- s.append("Unable to remove meta class for groovy type " + this.dottedtypename + ": " + e.toString() + " -- ");
+ }
+ catch (Exception e) {
+ s.append("Unable to remove meta class for groovy type " + this.dottedtypename + ": " + e.toString()
+ + " -- ");
// new RuntimeException("Unable to remove meta class for groovy type " + this.dottedtypename, e)
// .printStackTrace(System.err);
}
@@ -688,8 +729,10 @@ public class ReloadableType {
// java.lang.NoSuchMethodException: org.codehaus.groovy.reflection.ClassInfo$LazyCachedClassRef.clear()
Method clearMethod = lazyReferenceClass.getMethod("clear");//DeclaredMethod("clear");
clearMethod.invoke(cachedClassRefObject);
- } catch (Exception e) {
- s.append("1 Unable to clear ClassInfo CachedClass data for groovy type " + this.dottedtypename + ": " + e.toString()
+ }
+ catch (Exception e) {
+ s.append("1 Unable to clear ClassInfo CachedClass data for groovy type " + this.dottedtypename + ": "
+ + e.toString()
+ " -- ");
// new RuntimeException("Unable to clear ClassInfo CachedClass data for groovy type " + this.dottedtypename, e)
// .printStackTrace(System.err);
@@ -714,8 +757,10 @@ public class ReloadableType {
// // java.lang.NoSuchMethodException: org.codehaus.groovy.reflection.ClassInfo$LazyCachedClassRef.clear()
// Method clearMethod = lazyReferenceClass.getMethod("clear");//DeclaredMethod("clear");
// clearMethod.invoke(cachedClassRefObject);
- } catch (Exception e) {
- s.append("2 Unable to clear ClassInfo CachedClass data for groovy type " + this.dottedtypename + ": " + e.toString()
+ }
+ catch (Exception e) {
+ s.append("2 Unable to clear ClassInfo CachedClass data for groovy type " + this.dottedtypename + ": "
+ + e.toString()
+ " -- ");
// new RuntimeException("Unable to clear ClassInfo CachedClass data for groovy type " + this.dottedtypename, e)
// .printStackTrace(System.err);
@@ -731,7 +776,8 @@ public class ReloadableType {
deadInstances = new HashSet>();
}
deadInstances.add(instance);
- } else {
+ }
+ else {
f.setAccessible(true);
f.set(o, null);
}
@@ -739,8 +785,10 @@ public class ReloadableType {
if (deadInstances != null) {
liveInstances.removeAll(deadInstances);
}
- } catch (Exception e) {
- s.append("2 Unable to clear metaClass for groovy object instance (class=" + this.dottedtypename + ") " + e.toString()
+ }
+ catch (Exception e) {
+ s.append("2 Unable to clear metaClass for groovy object instance (class=" + this.dottedtypename + ") "
+ + e.toString()
+ " -- ");
// new RuntimeException("Unable to clear metaClass for groovy object instance (class=" + this.dottedtypename + ")", e)
// .printStackTrace(System.err);
@@ -771,7 +819,8 @@ public class ReloadableType {
loadNewVersion("0", bytesInitial);
}
return liveVersion.dispatcherInstance;
- } else {
+ }
+ else {
// Same as getLatestDispatcherInstance()
return (liveVersion == null ? null : liveVersion.dispatcherInstance);
}
@@ -802,18 +851,19 @@ public class ReloadableType {
}
/**
- * Gets the method corresponding to given name and descriptor, taking into consideration changes that have happened by
- * reloading.
+ * Gets the method corresponding to given name and descriptor, taking into consideration changes that have happened
+ * by reloading.
*
* @param name the member name
* @param descriptor the member descriptor (e.g. (Ljava/lang/String;)I)
- * @return the MethodMember for that name and descriptor. Null if not found on a live version, or an exception if there is no live version and
- * it cannot be found.
+ * @return the MethodMember for that name and descriptor. Null if not found on a live version, or an exception if
+ * there is no live version and it cannot be found.
*/
public MethodMember getCurrentMethod(String name, String descriptor) {
if (liveVersion == null) {
return getMethod(name, descriptor);
- } else {
+ }
+ else {
return liveVersion.getReloadableMethod(name, descriptor);
}
}
@@ -885,8 +935,8 @@ public class ReloadableType {
}
if (typeRegistry.shouldDefineClasses()) {
/**
- * Define the actual class. This is a separate call because it doesn't need doing when the ReloadableType is built
- * during agent processing, because that agent will define the class.
+ * Define the actual class. This is a separate call because it doesn't need doing when the ReloadableType is
+ * built during agent processing, because that agent will define the class.
*/
// ClassPrinter.print(bytesLoaded);
clazz = typeRegistry.defineClass(dottedtypename, bytesLoaded, true);
@@ -898,13 +948,15 @@ public class ReloadableType {
* This merges the two steps: method invocation rewriting and type rewriting
*/
static class MergedRewrite {
+
public static byte[] rewrite(ReloadableType rtype, byte[] bytes) {
try {
ClassReader fileReader = new ClassReader(bytes);
ChainedAdapters classAdaptor = new ChainedAdapters(rtype);
fileReader.accept(classAdaptor, 0);
return classAdaptor.getBytes();
- } catch (DontRewriteException drex) {
+ }
+ catch (DontRewriteException drex) {
return bytes;
}
}
@@ -912,8 +964,9 @@ public class ReloadableType {
static class ChainedAdapters extends ClassVisitor implements Constants {
public ChainedAdapters(ReloadableType rtype) {
- super(ASM5,new RewriteClassAdaptor(rtype.typeRegistry, new TypeRewriter.RewriteClassAdaptor(rtype, new ClassWriter(
- ClassWriter.COMPUTE_MAXS))));
+ super(ASM5, new RewriteClassAdaptor(rtype.typeRegistry, new TypeRewriter.RewriteClassAdaptor(rtype,
+ new ClassWriter(
+ ClassWriter.COMPUTE_MAXS))));
}
public byte[] getBytes() {
@@ -977,13 +1030,15 @@ public class ReloadableType {
* Check if the specified method is different to the original form from the type as loaded.
*
* @param methodId the ID of the method currently executing
- * @return 0 if the method cannot have changed. 1 if the method has changed. 2 if the method has been deleted in a new version.
+ * @return 0 if the method cannot have changed. 1 if the method has changed. 2 if the method has been deleted in a
+ * new version.
*/
@UsedByGeneratedCode
public int changed(int methodId) {
if (liveVersion == null) {
return 0;
- } else {
+ }
+ else {
int retval = 0;
// First check if a new version of the type was loaded:
if (liveVersion != null) {
@@ -998,7 +1053,8 @@ public class ReloadableType {
boolean b = liveVersion.incrementalTypeDescriptor.hasBeenDeleted(methodId);
if (b) {
retval = 2;
- } else {
+ }
+ else {
retval = liveVersion.incrementalTypeDescriptor.mustUseExecutorForThisMethod(methodId) ? 1 : 0;
}
}
@@ -1038,7 +1094,7 @@ public class ReloadableType {
public String getSlashedSupertypeName() {
return getTypeDescriptor().getSupertypeName();
}
-
+
public String[] getSlashedSuperinterfacesName() {
return getTypeDescriptor().getSuperinterfacesName();
}
@@ -1058,8 +1114,8 @@ public class ReloadableType {
}
/**
- * Intended to handle dynamic dispatch. This will determine the right type to handle the specified method and return a
- * dispatcher that can handle it.
+ * Intended to handle dynamic dispatch. This will determine the right type to handle the specified method and return
+ * a dispatcher that can handle it.
*
* @param instance the target instance for the invocation
* @param nameAndDescriptor an encoded method name and descriptor, e.g. foo(Ljava/langString;)V
@@ -1080,7 +1136,8 @@ public class ReloadableType {
// iterate up the hierarchy finding the first person that can satisfy that method from a virtual dispatch perspective
ReloadableType rtype = typeRegistry.getReloadableType(dynamicTypeName.replace('.', '/'));
if (rtype == null) {
- throw new ReloadException("ReloadableType.determineDispatcher(): expected " + dynamicTypeName + " to be reloadable");
+ throw new ReloadException("ReloadableType.determineDispatcher(): expected " + dynamicTypeName
+ + " to be reloadable");
}
boolean found = false;
while (rtype != null && !found) {
@@ -1101,12 +1158,14 @@ public class ReloadableType {
break;
}
}
- } else {
+ }
+ else {
// Did the type originally define it:
MethodMember[] mms = rtype.getTypeDescriptor().getMethods();
for (MethodMember mm : mms) {
// TODO don't need superdispatcher check, name won't match will it...
- if (mm.getNameAndDescriptor().equals(nameAndDescriptor) && !MethodMember.isCatcher(mm) && !MethodMember.isSuperDispatcher(mm)) {
+ if (mm.getNameAndDescriptor().equals(nameAndDescriptor) && !MethodMember.isCatcher(mm)
+ && !MethodMember.isSuperDispatcher(mm)) {
// the original version does implement it
found = true;
break;
@@ -1141,7 +1200,8 @@ public class ReloadableType {
int dotIndex = dottedtypename.lastIndexOf(".");
if (dotIndex == -1) {
return dottedtypename;
- } else {
+ }
+ else {
return dottedtypename.substring(dotIndex + 1);
}
}
@@ -1149,7 +1209,8 @@ public class ReloadableType {
public TypeDescriptor getLatestTypeDescriptor() {
if (liveVersion == null) {
return typedescriptor;
- } else {
+ }
+ else {
return liveVersion.incrementalTypeDescriptor.getLatestTypeDescriptor();
}
}
@@ -1190,9 +1251,10 @@ public class ReloadableType {
}
/**
- * Find the named instance field either on this reloadable type or on a reloadable supertype - it will not go into the
- * non-reloadable types. This method also avoids interfaces because it is looking for instance fields. This is slightly naughty
- * but if we assume the code we are reloading is valid code, it should never be referring to interface fields.
+ * Find the named instance field either on this reloadable type or on a reloadable supertype - it will not go into
+ * the non-reloadable types. This method also avoids interfaces because it is looking for instance fields. This is
+ * slightly naughty but if we assume the code we are reloading is valid code, it should never be referring to
+ * interface fields.
*
* @param name the name of the field to locate
* @return the FieldMember or null if the field is not found
@@ -1218,9 +1280,9 @@ public class ReloadableType {
}
/**
- * Search for a static field from this type upwards, as far as the topmost reloadable types. This is searching for a field, it
- * is not checking the result. It is up to the caller to check they have not ended up with an instance field and throw the
- * appropriate exception.
+ * Search for a static field from this type upwards, as far as the topmost reloadable types. This is searching for a
+ * field, it is not checking the result. It is up to the caller to check they have not ended up with an instance
+ * field and throw the appropriate exception.
*
* @param name the name of the field to look for
* @return a FieldMember for the named field or null if not found
@@ -1265,33 +1327,40 @@ public class ReloadableType {
* @param newValue the new value to put into the field
* @throws IllegalAccessException if there is a problem setting the field value
*/
- public void setField(Object instance, String fieldname, boolean isStatic, Object newValue) throws IllegalAccessException {
+ public void setField(Object instance, String fieldname, boolean isStatic, Object newValue)
+ throws IllegalAccessException {
FieldReaderWriter fieldReaderWriter = locateField(fieldname);
if (isStatic && !fieldReaderWriter.isStatic()) {
- throw new IncompatibleClassChangeError("Expected static field " + fieldReaderWriter.theField.getDeclaringTypeName()
+ throw new IncompatibleClassChangeError("Expected static field "
+ + fieldReaderWriter.theField.getDeclaringTypeName()
+ "." + fieldReaderWriter.theField.getName());
- } else if (!isStatic && fieldReaderWriter.isStatic()) {
- throw new IncompatibleClassChangeError("Expected non-static field " + fieldReaderWriter.theField.getDeclaringTypeName()
+ }
+ else if (!isStatic && fieldReaderWriter.isStatic()) {
+ throw new IncompatibleClassChangeError("Expected non-static field "
+ + fieldReaderWriter.theField.getDeclaringTypeName()
+ "." + fieldReaderWriter.theField.getName());
}
if (fieldReaderWriter.isStatic()) {
fieldReaderWriter.setStaticFieldValue(getClazz(), newValue, null);
- } else {
+ }
+ else {
fieldReaderWriter.setValue(instance, newValue, null);
}
}
private Set> liveInstances = Collections.synchronizedSet(new HashSet>());
+
private ReferenceQueue