commit 089e0cf20857c811a77abcf668b81eb8aaa58c8c Author: decatur Date: Tue Oct 4 22:27:20 2011 +0200 first commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..b8f9892 --- /dev/null +++ b/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b510112 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +git.txt +pom.xml +src/main/resources/META-INF/* +src/site/**/* +src/test/**/* +launchConfig/**/* +target/**/* \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..6e60f53 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + j2js-compiler + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..d253ed8 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,271 @@ +#Wed Sep 03 00:07:36 CEST 2008 +eclipse.preferences.version=1 +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 +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.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +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_enum_constant=16 +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_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_selector_in_method_invocation=16 +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.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=0 +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=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +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_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=false +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=true +org.eclipse.jdt.core.formatter.comment.format_comments=true +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=true +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=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +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=2 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +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=false +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_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=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_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not 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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not 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_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_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_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_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_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_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_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.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=80 +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=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..b1ff5e6 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,6 @@ +#Wed Sep 03 00:07:35 CEST 2008 +eclipse.preferences.version=1 +formatter_profile=_MyProfile +formatter_settings_version=11 +internal.default.compliance=default +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/.settings/org.eclipse.wst.validation.prefs b/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 0000000..40588f9 --- /dev/null +++ b/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,6 @@ +#Wed Oct 24 00:47:12 CEST 2007 +DELEGATES_PREFERENCE=delegateValidatorListorg.eclipse.wst.wsdl.validation.internal.eclipse.WSDLDelegatingValidator\=org.eclipse.wst.wsdl.validation.internal.eclipse.Validator;org.eclipse.wst.xsd.core.internal.validation.eclipse.XSDDelegatingValidator\=org.eclipse.wst.xsd.core.internal.validation.eclipse.Validator; +USER_BUILD_PREFERENCE=enabledBuildValidatorListorg.eclipse.wst.dtd.core.internal.validation.eclipse.Validator;org.eclipse.wst.xsd.core.internal.validation.eclipse.XSDDelegatingValidator;org.eclipse.wst.html.internal.validation.HTMLValidator;org.eclipse.jst.jsp.core.internal.validation.JSPBatchValidator;org.eclipse.wst.xml.core.internal.validation.eclipse.Validator;org.eclipse.wst.wsi.ui.internal.WSIMessageValidator;org.eclipse.wst.wsdl.validation.internal.eclipse.WSDLDelegatingValidator; +USER_MANUAL_PREFERENCE=enabledManualValidatorListorg.eclipse.wst.dtd.core.internal.validation.eclipse.Validator;org.eclipse.wst.xsd.core.internal.validation.eclipse.XSDDelegatingValidator;org.eclipse.wst.html.internal.validation.HTMLValidator;org.eclipse.jst.jsp.core.internal.validation.JSPBatchValidator;org.eclipse.wst.xml.core.internal.validation.eclipse.Validator;org.eclipse.wst.wsi.ui.internal.WSIMessageValidator;org.eclipse.wst.wsdl.validation.internal.eclipse.WSDLDelegatingValidator; +USER_PREFERENCE=overrideGlobalPreferencesfalse +eclipse.preferences.version=1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..662b4d3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +A FreeBSD Licence + +Copyright(c) 2011, the j2js Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD +PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..72b09d4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +#j2js-compiler + +A Java Bytecode to JavaScript Cross-Compiler. diff --git a/libs/bcel-5.1.jar b/libs/bcel-5.1.jar new file mode 100644 index 0000000..524e375 Binary files /dev/null and b/libs/bcel-5.1.jar differ diff --git a/libs/commons-io-1.4.jar b/libs/commons-io-1.4.jar new file mode 100644 index 0000000..133dc6c Binary files /dev/null and b/libs/commons-io-1.4.jar differ diff --git a/src/main/java/Checker.java b/src/main/java/Checker.java new file mode 100644 index 0000000..d35b3d4 --- /dev/null +++ b/src/main/java/Checker.java @@ -0,0 +1,40 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.j2js.Const; + +/** + * Copyright by Wolfgang Kuehn 2005 + * Created on Feb 27, 2005 + */ +public class Checker { + public static void main(String argv[]) throws Exception { + BufferedReader reader = new BufferedReader(new FileReader(new File("src/com/j2js/Pass1.java"))); + Pattern p = Pattern.compile(".*case\\s+Const\\.(\\w*)\\s*:.*"); + HashSet set = new HashSet(); + + String line; + while ((line=reader.readLine())!=null) { + Matcher m = p.matcher(line); + if (m.matches()) { + String name = m.group(1); + //System.out.println("Adding " + name); + set.add(name); + } + } + + int missingCount = 0; + for (int i=0; i 1) { + head += " (" + i + ")"; + } + line += head + " Format: " + it.getName(); + for (int j=0; j { + + public ASTNodeStack() { + super(); + } + + public ASTNodeStack(ASTNodeStack other) { + super(other); + } + + public ASTNodeStack(Expression expression) { + super(); + push(expression); + } + + public Expression pop() { + if (size() == 0) throw new RuntimeException("Cannot pop empty stack"); + return (Expression) remove(size() - 1); + } + + public void push(ASTNode node) { + add(node); + } + + public void rotate(int offset) { + if (offset == 0) return; + if (offset > size()) throw new IndexOutOfBoundsException(); + ASTNode node = pop(); + add(size()-offset, node); + } + + public Expression peek() { + return peek(0); + } + + private static Object safeCast(Object object, Class clazz) { + if (!clazz.isInstance(object)) { + throw new RuntimeException("Expected " + clazz + ", but was " + object); + } + return object; + } + + public Expression peek(int offset) { + if (size()-1 < offset) return null; + return (Expression) ASTNodeStack.safeCast(get(size()-1-offset), Expression.class); + } +} diff --git a/src/main/java/com/j2js/AnnotationAttribute.java b/src/main/java/com/j2js/AnnotationAttribute.java new file mode 100644 index 0000000..19493b7 --- /dev/null +++ b/src/main/java/com/j2js/AnnotationAttribute.java @@ -0,0 +1,28 @@ +package com.j2js; + +import java.util.Map; + +import org.apache.bcel.classfile.Attribute; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.Visitor; + +public class AnnotationAttribute extends Attribute { + + Map[] annotations; + + protected AnnotationAttribute(byte tag, int nameIndex, int length, ConstantPool constantPool) { + super(tag, nameIndex, length, constantPool); + } + + @Override + public void accept(Visitor arg0) { + // TODO Auto-generated method stub + + } + + @Override + public Attribute copy(ConstantPool arg0) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/main/java/com/j2js/AnnotationReader.java b/src/main/java/com/j2js/AnnotationReader.java new file mode 100644 index 0000000..9f7d8c5 --- /dev/null +++ b/src/main/java/com/j2js/AnnotationReader.java @@ -0,0 +1,58 @@ +package com.j2js; + +import java.io.DataInputStream; +import java.util.HashMap; +import java.util.Map; + +import org.apache.bcel.classfile.Attribute; +import org.apache.bcel.classfile.AttributeReader; +import org.apache.bcel.classfile.Constant; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.ConstantUtf8; + +import com.j2js.assembly.ClassUnit; +import com.j2js.assembly.Project; +import com.j2js.assembly.Signature; + +public class AnnotationReader implements AttributeReader{ + + private ClassUnit classUnit; + + public AnnotationReader(ClassUnit theClassUnit) { + classUnit = theClassUnit; + } + + public Attribute createAttribute(int name_index, int length, DataInputStream file, ConstantPool constantPool) { + Map[] annotations = null; + // TODO: Annotation + try { + int attCount = file.readUnsignedShort(); + annotations = new HashMap[attCount]; + for (int j=0; j map = new HashMap(); + annotations[j] = map; + int nameIndex = file.readUnsignedShort(); + Constant constant = constantPool.getConstant(nameIndex); + Signature signature = Project.getSingleton().getSignature(((ConstantUtf8) constant).getBytes()); + map.put("$signature", signature.toString()); + int fieldCount = file.readUnsignedShort(); + for (int i=0; i entryPoints = new ArrayList(); + + private final static String DECLARECLASS = "dcC"; + + private transient Log logger; + + private transient String entryPointClassName; + + private Project project; + private Set taintedSignatures = new HashSet(); + private Set unprocessedTaintedSignatures = new HashSet(); + String[] patterns; + private Collection resolvedTypes = new ArrayList(); + private transient File targetLocation; + + public Assembly() { + patterns = Utils.getProperty("j2js.taintIfInstantiated").split(";"); + for (int i=0; i 0) { + processSingle(popSignature()); + if (unprocessedTaintedSignatures.size() == 0) { + processOverWrittenMembers(); + } + } + } + + public void processSingle(Signature signature) { + ClassUnit clazz = resolve(signature.className()); + String methodPart = signature.relativeSignature(); + boolean found = false; + for (MemberUnit member : clazz.getMembers(methodPart)) { + taint(member); + found = true; + } + + if (!found) { + //logger.severe("No such method: " + signature); + throw new RuntimeException("No such method: " + signature); + } + +// MemberUnit member = clazz.getMember(methodPart); +// if (member != null) { +// taint(member); +// } else { +// logger.severe("No such method: " + signature); +// } + } + + private ClassUnit resolve(String className) { + ClassUnit clazz = project.getOrCreateClassUnit(className); + + if (className.startsWith("[")) { + project.resolve(clazz); + } else { + project.resolve(clazz); + taint(className + "#()void"); + } + + resolvedTypes.add(clazz); + + return clazz; + } + + /** + * Taint the signatures of methods matching a pattern. + */ + private void taintImplicitelyAccessedMembers(ClassUnit clazz) { + // TODO: This method is called multiple times for each clazz. This + // should not be the case! +// System.out.println(clazz.toString()); + for (MemberUnit member : clazz.getDeclaredMembers()) { + for (int i=0; i classIterator = resolvedTypes.iterator(); + while (classIterator.hasNext()) { + ClassUnit clazz = classIterator.next(); + if (clazz.isConstructorTainted) taintIfSuperTainted(clazz);; + } + } + + public void taint(String signature) { + signature = signature.replaceAll("\\s", ""); +// if (signature.indexOf('*') != -1) { +// if (!signature.endsWith(".*")) { +// throw new RuntimeException("Package signature must end with '.*'"); +// } +// signature = signature.substring(0, signature.length()-2); +// File[] files = Utils.resolvePackage(J2JSCompiler.compiler.getClassPath(), signature); +// for (int i=0; i iter = unprocessedTaintedSignatures.iterator(); + Signature signature = iter.next(); + iter.remove(); + return signature; + } + + public void taint(Signature signature) { + if (!signature.toString().contains("#")) { + throw new IllegalArgumentException("Signature must be field or method: " + signature); + } + + if (taintedSignatures.contains(signature)) return; + + taintedSignatures.add(signature); + unprocessedTaintedSignatures.add(signature); + } + + public void taint(MemberUnit member) { + member.setTainted(); + // TODO: Tainting super types in neccesary for generating only? + member.getDeclaringClass().setSuperTainted(); + if (member instanceof ProcedureUnit) { + taintTargetSignatures((ProcedureUnit) member); + if (member instanceof ConstructorUnit) { + member.getDeclaringClass().isConstructorTainted = true; + taintImplicitelyAccessedMembers(member.getDeclaringClass()); + } + } + } + + /** + * @see #getProject() + */ + public void setProject(Project project) { + this.project = project; + } + + public Project getProject() { + return project; + } + + /** + * @return Returns the entryPointClassName. + */ + public String getEntryPointClassName() { + return entryPointClassName; + } + + /** + *

+ * The static main(String[])void method of the specified class is executed + * after the assembly is loaded by the script engine. + *

+ *

+ * In web mode, the values of the URL query string are passed to this method. + *

+ * + * @param entryPointClassName (required) the class signature + */ + public Assembly setEntryPointClassName(String entryPointClassName) { + this.entryPointClassName = entryPointClassName; + return this; + } + + /** + * @return Returns the targetLocation. + */ + public File getTargetLocation() { + return targetLocation; + } + + /** + * The assembly is written to the specified directory + * + * @param targetLocation (required) the directory the assembly is written to + */ + public void setTargetLocation(File targetLocation) { + this.targetLocation = targetLocation; + } + + /** + * Signature of a field or method to include in the assembly. + *

+ * Note: Normally you will only add those entrypoints which + * the static code analyser cannot detect, for example methods called through + * reflection. + *

+ * + * @param memberSignature the member to include + */ + public void addEntryPoint(String memberSignature) { + entryPoints.add(memberSignature); + } + +} diff --git a/src/main/java/com/j2js/Const.java b/src/main/java/com/j2js/Const.java new file mode 100644 index 0000000..194edb8 --- /dev/null +++ b/src/main/java/com/j2js/Const.java @@ -0,0 +1,2480 @@ + + package com.j2js; + public class Const { + + public static InstructionType[] instructionTypes = new InstructionType[202]; + + public static final int AALOAD = 50; + + public static final int AASTORE = 83; + + public static final int ACONST_NULL = 1; + + public static final int ALOAD = 25; + + public static final int ALOAD_0 = 42; + + public static final int ALOAD_1 = 43; + + public static final int ALOAD_2 = 44; + + public static final int ALOAD_3 = 45; + + public static final int ANEWARRAY = 189; + + public static final int ARETURN = 176; + + public static final int ARRAYLENGTH = 190; + + public static final int ASTORE = 58; + + public static final int ASTORE_0 = 75; + + public static final int ASTORE_1 = 76; + + public static final int ASTORE_2 = 77; + + public static final int ASTORE_3 = 78; + + public static final int ATHROW = 191; + + public static final int BALOAD = 51; + + public static final int BASTORE = 84; + + public static final int BIPUSH = 16; + + public static final int NEW = 187; + + public static final int NEWARRAY = 188; + + public static final int NOP = 0; + + public static final int POP = 87; + + public static final int POP2 = 88; + + public static final int PUTFIELD = 181; + + public static final int PUTSTATIC = 179; + + public static final int RET = 169; + + public static final int RETURN = 177; + + public static final int SALOAD = 53; + + public static final int SASTORE = 86; + + public static final int SIPUSH = 17; + + public static final int SWAP = 95; + + public static final int TABLESWITCH = 170; + + public static final int WIDE = 196; + + public static final int CALOAD = 52; + + public static final int CASTORE = 85; + + public static final int CHECKCAST = 192; + + public static final int D2F = 144; + + public static final int D2I = 142; + + public static final int D2L = 143; + + public static final int DADD = 99; + + public static final int DALOAD = 49; + + public static final int DASTORE = 82; + + public static final int DCMPL = 151; + + public static final int DCMPG = 152; + + public static final int DCONST_0 = 14; + + public static final int DCONST_1 = 15; + + public static final int DDIV = 111; + + public static final int DLOAD = 24; + + public static final int DLOAD_0 = 38; + + public static final int DLOAD_1 = 39; + + public static final int DLOAD_2 = 40; + + public static final int DLOAD_3 = 41; + + public static final int DMUL = 107; + + public static final int DNEG = 119; + + public static final int DREM = 115; + + public static final int DRETURN = 175; + + public static final int DSTORE = 57; + + public static final int DSTORE_0 = 71; + + public static final int DSTORE_1 = 72; + + public static final int DSTORE_2 = 73; + + public static final int DSTORE_3 = 74; + + public static final int DSUB = 103; + + public static final int DUP = 89; + + public static final int DUP_X1 = 90; + + public static final int DUP_X2 = 91; + + public static final int DUP2 = 92; + + public static final int DUP2_X1 = 93; + + public static final int DUP2_X2 = 94; + + public static final int F2D = 141; + + public static final int F2I = 139; + + public static final int F2L = 140; + + public static final int FADD = 98; + + public static final int FALOAD = 48; + + public static final int FASTORE = 81; + + public static final int FCMPL = 149; + + public static final int FCMPG = 150; + + public static final int FCONST_0 = 11; + + public static final int FCONST_1 = 12; + + public static final int FCONST_2 = 13; + + public static final int FDIV = 110; + + public static final int FLOAD = 23; + + public static final int FLOAD_0 = 34; + + public static final int FLOAD_1 = 35; + + public static final int FLOAD_2 = 36; + + public static final int FLOAD_3 = 37; + + public static final int FMUL = 106; + + public static final int FNEG = 118; + + public static final int FREM = 114; + + public static final int FRETURN = 174; + + public static final int FSTORE = 56; + + public static final int FSTORE_0 = 67; + + public static final int FSTORE_1 = 68; + + public static final int FSTORE_2 = 69; + + public static final int FSTORE_3 = 70; + + public static final int FSUB = 102; + + public static final int GETFIELD = 180; + + public static final int GETSTATIC = 178; + + public static final int GOTO = 167; + + public static final int GOTO_W = 200; + + public static final int I2B = 145; + + public static final int I2C = 146; + + public static final int I2D = 135; + + public static final int I2F = 134; + + public static final int I2L = 133; + + public static final int I2S = 147; + + public static final int IADD = 96; + + public static final int IALOAD = 46; + + public static final int IAND = 126; + + public static final int IASTORE = 79; + + public static final int ICONST_M1 = 2; + + public static final int ICONST_0 = 3; + + public static final int ICONST_1 = 4; + + public static final int ICONST_2 = 5; + + public static final int ICONST_3 = 6; + + public static final int ICONST_4 = 7; + + public static final int ICONST_5 = 8; + + public static final int IDIV = 108; + + public static final int IF_ACMPEQ = 165; + + public static final int IF_ACMPNE = 166; + + public static final int IF_ICMPEQ = 159; + + public static final int IF_ICMPNE = 160; + + public static final int IF_ICMPLT = 161; + + public static final int IF_ICMPGE = 162; + + public static final int IF_ICMPGT = 163; + + public static final int IF_ICMPLE = 164; + + public static final int IFEQ = 153; + + public static final int IFNE = 154; + + public static final int IFLT = 155; + + public static final int IFGE = 156; + + public static final int IFGT = 157; + + public static final int IFLE = 158; + + public static final int IFNONNULL = 199; + + public static final int IFNULL = 198; + + public static final int IINC = 132; + + public static final int ILOAD = 21; + + public static final int ILOAD_0 = 26; + + public static final int ILOAD_1 = 27; + + public static final int ILOAD_2 = 28; + + public static final int ILOAD_3 = 29; + + public static final int IMUL = 104; + + public static final int INEG = 116; + + public static final int INSTANCEOF = 193; + + public static final int INVOKEINTERFACE = 185; + + public static final int INVOKESPECIAL = 183; + + public static final int INVOKESTATIC = 184; + + public static final int INVOKEVIRTUAL = 182; + + public static final int IOR = 128; + + public static final int IREM = 112; + + public static final int IRETURN = 172; + + public static final int ISHL = 120; + + public static final int ISHR = 122; + + public static final int ISTORE = 54; + + public static final int ISTORE_0 = 59; + + public static final int ISTORE_1 = 60; + + public static final int ISTORE_2 = 61; + + public static final int ISTORE_3 = 62; + + public static final int ISUB = 100; + + public static final int IUSHR = 124; + + public static final int IXOR = 130; + + public static final int JSR = 168; + + public static final int JSR_W = 201; + + public static final int L2D = 138; + + public static final int L2F = 137; + + public static final int L2I = 136; + + public static final int LADD = 97; + + public static final int LALOAD = 47; + + public static final int LAND = 127; + + public static final int LASTORE = 80; + + public static final int LCMP = 148; + + public static final int LCONST_0 = 9; + + public static final int LCONST_1 = 10; + + public static final int LDC = 18; + + public static final int LDC_W = 19; + + public static final int LDC2_W = 20; + + public static final int LDIV = 109; + + public static final int LLOAD = 22; + + public static final int LLOAD_0 = 30; + + public static final int LLOAD_1 = 31; + + public static final int LLOAD_2 = 32; + + public static final int LLOAD_3 = 33; + + public static final int LMUL = 105; + + public static final int LNEG = 117; + + public static final int LOOKUPSWITCH = 171; + + public static final int LOR = 129; + + public static final int LREM = 113; + + public static final int LRETURN = 173; + + public static final int LSHL = 121; + + public static final int LSHR = 123; + + public static final int LSTORE = 55; + + public static final int LSTORE_0 = 63; + + public static final int LSTORE_1 = 64; + + public static final int LSTORE_2 = 65; + + public static final int LSTORE_3 = 66; + + public static final int LSUB = 101; + + public static final int LUSHR = 125; + + public static final int LXOR = 131; + + public static final int MONITORENTER = 194; + + public static final int MONITOREXIT = 195; + + public static final int MULTIANEWARRAY = 197; + + public static final int XXXUNUSEDXXX = 186; + + + static { + InstructionType i; + Form f; + + i = new InstructionType((short)0, "nop", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[0] = i; + + i = new InstructionType((short)1, "aconst_null", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","null"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[1] = i; + + i = new InstructionType((short)2, "iconst_m1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[2] = i; + + i = new InstructionType((short)3, "iconst_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[3] = i; + + i = new InstructionType((short)4, "iconst_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[4] = i; + + i = new InstructionType((short)5, "iconst_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[5] = i; + + i = new InstructionType((short)6, "iconst_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[6] = i; + + i = new InstructionType((short)7, "iconst_4", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[7] = i; + + i = new InstructionType((short)8, "iconst_5", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","i"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[8] = i; + + i = new InstructionType((short)9, "lconst_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","l"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[9] = i; + + i = new InstructionType((short)10, "lconst_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","l"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[10] = i; + + i = new InstructionType((short)11, "fconst_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","f"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[11] = i; + + i = new InstructionType((short)12, "fconst_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","f"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[12] = i; + + i = new InstructionType((short)13, "fconst_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","f"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[13] = i; + + i = new InstructionType((short)14, "dconst_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","d"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[14] = i; + + i = new InstructionType((short)15, "dconst_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","d"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[15] = i; + + i = new InstructionType((short)16, "bipush", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","byte"), }); + i.setForm(f, 0); + + instructionTypes[16] = i; + + i = new InstructionType((short)17, "sipush", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("short","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[17] = i; + + i = new InstructionType((short)18, "ldc", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[18] = i; + + i = new InstructionType((short)19, "ldc_w", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[19] = i; + + i = new InstructionType((short)20, "ldc2_w", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[20] = i; + + i = new InstructionType((short)21, "iload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[21] = i; + + i = new InstructionType((short)22, "lload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[22] = i; + + i = new InstructionType((short)23, "fload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[23] = i; + + i = new InstructionType((short)24, "dload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[24] = i; + + i = new InstructionType((short)25, "aload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[25] = i; + + i = new InstructionType((short)26, "iload_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[26] = i; + + i = new InstructionType((short)27, "iload_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[27] = i; + + i = new InstructionType((short)28, "iload_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[28] = i; + + i = new InstructionType((short)29, "iload_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[29] = i; + + i = new InstructionType((short)30, "lload_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[30] = i; + + i = new InstructionType((short)31, "lload_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[31] = i; + + i = new InstructionType((short)32, "lload_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[32] = i; + + i = new InstructionType((short)33, "lload_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("long","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[33] = i; + + i = new InstructionType((short)34, "fload_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[34] = i; + + i = new InstructionType((short)35, "fload_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[35] = i; + + i = new InstructionType((short)36, "fload_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[36] = i; + + i = new InstructionType((short)37, "fload_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("float","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[37] = i; + + i = new InstructionType((short)38, "dload_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[38] = i; + + i = new InstructionType((short)39, "dload_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[39] = i; + + i = new InstructionType((short)40, "dload_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[40] = i; + + i = new InstructionType((short)41, "dload_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("double","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[41] = i; + + i = new InstructionType((short)42, "aload_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[42] = i; + + i = new InstructionType((short)43, "aload_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[43] = i; + + i = new InstructionType((short)44, "aload_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[44] = i; + + i = new InstructionType((short)45, "aload_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[45] = i; + + i = new InstructionType((short)46, "iaload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[46] = i; + + i = new InstructionType((short)47, "laload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[47] = i; + + i = new InstructionType((short)48, "faload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[48] = i; + + i = new InstructionType((short)49, "daload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[49] = i; + + i = new InstructionType((short)50, "aaload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[50] = i; + + i = new InstructionType((short)51, "baload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[51] = i; + + i = new InstructionType((short)52, "caload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[52] = i; + + i = new InstructionType((short)53, "saload", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), }); + f.setOuts(new Form.Value[]{new Form.Value("short","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[53] = i; + + i = new InstructionType((short)54, "istore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[54] = i; + + i = new InstructionType((short)55, "lstore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[55] = i; + + i = new InstructionType((short)56, "fstore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[56] = i; + + i = new InstructionType((short)57, "dstore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[57] = i; + + i = new InstructionType((short)58, "astore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[58] = i; + + i = new InstructionType((short)59, "istore_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[59] = i; + + i = new InstructionType((short)60, "istore_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[60] = i; + + i = new InstructionType((short)61, "istore_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[61] = i; + + i = new InstructionType((short)62, "istore_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[62] = i; + + i = new InstructionType((short)63, "lstore_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[63] = i; + + i = new InstructionType((short)64, "lstore_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[64] = i; + + i = new InstructionType((short)65, "lstore_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[65] = i; + + i = new InstructionType((short)66, "lstore_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[66] = i; + + i = new InstructionType((short)67, "fstore_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("float","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[67] = i; + + i = new InstructionType((short)68, "fstore_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("float","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[68] = i; + + i = new InstructionType((short)69, "fstore_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("float","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[69] = i; + + i = new InstructionType((short)70, "fstore_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("float","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[70] = i; + + i = new InstructionType((short)71, "dstore_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[71] = i; + + i = new InstructionType((short)72, "dstore_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[72] = i; + + i = new InstructionType((short)73, "dstore_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[73] = i; + + i = new InstructionType((short)74, "dstore_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[74] = i; + + i = new InstructionType((short)75, "astore_0", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[75] = i; + + i = new InstructionType((short)76, "astore_1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[76] = i; + + i = new InstructionType((short)77, "astore_2", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[77] = i; + + i = new InstructionType((short)78, "astore_3", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[78] = i; + + i = new InstructionType((short)79, "iastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[79] = i; + + i = new InstructionType((short)80, "lastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[80] = i; + + i = new InstructionType((short)81, "fastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[81] = i; + + i = new InstructionType((short)82, "dastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[82] = i; + + i = new InstructionType((short)83, "aastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[83] = i; + + i = new InstructionType((short)84, "bastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[84] = i; + + i = new InstructionType((short)85, "castore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[85] = i; + + i = new InstructionType((short)86, "sastore", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","array"), new Form.Value("","index"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[86] = i; + + i = new InstructionType((short)87, "pop", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[87] = i; + + i = new InstructionType((short)88, "pop2", 2); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat2","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 1); + + instructionTypes[88] = i; + + i = new InstructionType((short)89, "dup", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value"), new Form.Value("cat1","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[89] = i; + + i = new InstructionType((short)90, "dup_x1", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value1"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[90] = i; + + i = new InstructionType((short)91, "dup_x2", 2); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value1"), new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat2","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value1"), new Form.Value("cat2","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 1); + + instructionTypes[91] = i; + + i = new InstructionType((short)92, "dup2", 2); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat2","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat2","value"), new Form.Value("cat2","value"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 1); + + instructionTypes[92] = i; + + i = new InstructionType((short)93, "dup2_x1", 2); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat2","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat2","value1"), new Form.Value("cat1","value2"), new Form.Value("cat2","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 1); + + instructionTypes[93] = i; + + i = new InstructionType((short)94, "dup2_x2", 4); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value4"), new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), new Form.Value("cat1","value4"), new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat2","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat2","value1"), new Form.Value("cat1","value3"), new Form.Value("cat1","value2"), new Form.Value("cat2","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat2","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), new Form.Value("cat2","value3"), new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 2); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat2","value2"), new Form.Value("cat2","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat2","value1"), new Form.Value("cat2","value2"), new Form.Value("cat2","value1"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 3); + + instructionTypes[94] = i; + + i = new InstructionType((short)95, "swap", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("cat1","value2"), new Form.Value("cat1","value1"), }); + f.setOuts(new Form.Value[]{new Form.Value("cat1","value1"), new Form.Value("cat1","value2"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[95] = i; + + i = new InstructionType((short)96, "iadd", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[96] = i; + + i = new InstructionType((short)97, "ladd", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[97] = i; + + i = new InstructionType((short)98, "fadd", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[98] = i; + + i = new InstructionType((short)99, "dadd", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value1"), new Form.Value("double","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[99] = i; + + i = new InstructionType((short)100, "isub", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[100] = i; + + i = new InstructionType((short)101, "lsub", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[101] = i; + + i = new InstructionType((short)102, "fsub", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[102] = i; + + i = new InstructionType((short)103, "dsub", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value1"), new Form.Value("double","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[103] = i; + + i = new InstructionType((short)104, "imul", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[104] = i; + + i = new InstructionType((short)105, "lmul", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[105] = i; + + i = new InstructionType((short)106, "fmul", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[106] = i; + + i = new InstructionType((short)107, "dmul", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[107] = i; + + i = new InstructionType((short)108, "idiv", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[108] = i; + + i = new InstructionType((short)109, "ldiv", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[109] = i; + + i = new InstructionType((short)110, "fdiv", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[110] = i; + + i = new InstructionType((short)111, "ddiv", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[111] = i; + + i = new InstructionType((short)112, "irem", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[112] = i; + + i = new InstructionType((short)113, "lrem", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[113] = i; + + i = new InstructionType((short)114, "frem", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[114] = i; + + i = new InstructionType((short)115, "drem", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[115] = i; + + i = new InstructionType((short)116, "ineg", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[116] = i; + + i = new InstructionType((short)117, "lneg", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[117] = i; + + i = new InstructionType((short)118, "fneg", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[118] = i; + + i = new InstructionType((short)119, "dneg", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[119] = i; + + i = new InstructionType((short)120, "ishl", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[120] = i; + + i = new InstructionType((short)121, "lshl", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[121] = i; + + i = new InstructionType((short)122, "ishr", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[122] = i; + + i = new InstructionType((short)123, "lshr", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[123] = i; + + i = new InstructionType((short)124, "iushr", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[124] = i; + + i = new InstructionType((short)125, "lushr", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[125] = i; + + i = new InstructionType((short)126, "iand", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[126] = i; + + i = new InstructionType((short)127, "land", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[127] = i; + + i = new InstructionType((short)128, "ior", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[128] = i; + + i = new InstructionType((short)129, "lor", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[129] = i; + + i = new InstructionType((short)130, "ixor", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[130] = i; + + i = new InstructionType((short)131, "lxor", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[131] = i; + + i = new InstructionType((short)132, "iinc", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), new Form.Value("int","const"), }); + i.setForm(f, 0); + + instructionTypes[132] = i; + + i = new InstructionType((short)133, "i2l", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[133] = i; + + i = new InstructionType((short)134, "i2f", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[134] = i; + + i = new InstructionType((short)135, "i2d", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[135] = i; + + i = new InstructionType((short)136, "l2i", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[136] = i; + + i = new InstructionType((short)137, "l2f", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[137] = i; + + i = new InstructionType((short)138, "l2d", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[138] = i; + + i = new InstructionType((short)139, "f2i", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("float","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[139] = i; + + i = new InstructionType((short)140, "f2l", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[140] = i; + + i = new InstructionType((short)141, "f2d", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("double","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[141] = i; + + i = new InstructionType((short)142, "d2i", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[142] = i; + + i = new InstructionType((short)143, "d2l", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("long","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[143] = i; + + i = new InstructionType((short)144, "d2f", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("float","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[144] = i; + + i = new InstructionType((short)145, "i2b", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("byte","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[145] = i; + + i = new InstructionType((short)146, "i2c", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("byte","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[146] = i; + + i = new InstructionType((short)147, "i2s", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{new Form.Value("short","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[147] = i; + + i = new InstructionType((short)148, "lcmp", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[148] = i; + + i = new InstructionType((short)149, "fcmpl", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[149] = i; + + i = new InstructionType((short)150, "fcmpg", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[150] = i; + + i = new InstructionType((short)151, "dcmpl", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[151] = i; + + i = new InstructionType((short)152, "dcmpg", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[152] = i; + + i = new InstructionType((short)153, "ifeq", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[153] = i; + + i = new InstructionType((short)154, "ifne", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[154] = i; + + i = new InstructionType((short)155, "iflt", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[155] = i; + + i = new InstructionType((short)156, "ifge", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[156] = i; + + i = new InstructionType((short)157, "ifgt", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[157] = i; + + i = new InstructionType((short)158, "ifle", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[158] = i; + + i = new InstructionType((short)159, "if_icmpeq", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[159] = i; + + i = new InstructionType((short)160, "if_icmpne", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[160] = i; + + i = new InstructionType((short)161, "if_icmplt", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[161] = i; + + i = new InstructionType((short)162, "if_icmpge", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[162] = i; + + i = new InstructionType((short)163, "if_icmpgt", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[163] = i; + + i = new InstructionType((short)164, "if_icmple", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[164] = i; + + i = new InstructionType((short)165, "if_acmpeq", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[165] = i; + + i = new InstructionType((short)166, "if_acmpne", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value1"), new Form.Value("","value2"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[166] = i; + + i = new InstructionType((short)167, "goto", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[167] = i; + + i = new InstructionType((short)168, "jsr", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("returnAddress","address"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[168] = i; + + i = new InstructionType((short)169, "ret", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","index"), }); + i.setForm(f, 0); + + instructionTypes[169] = i; + + i = new InstructionType((short)170, "tableswitch", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("int","index"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","padding"), new Form.Value("byte","padding"), new Form.Value("byte","padding"), new Form.Value("int","default"), new Form.Value("int","low"), new Form.Value("int","high"), }); + i.setForm(f, 0); + + instructionTypes[170] = i; + + i = new InstructionType((short)171, "lookupswitch", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","key"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("byte","padding"), new Form.Value("byte","padding"), new Form.Value("byte","padding"), new Form.Value("int","default"), new Form.Value("int","npairs"), }); + i.setForm(f, 0); + + instructionTypes[171] = i; + + i = new InstructionType((short)172, "ireturn", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[172] = i; + + i = new InstructionType((short)173, "lreturn", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[173] = i; + + i = new InstructionType((short)174, "freturn", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[174] = i; + + i = new InstructionType((short)175, "dreturn", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("double","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[175] = i; + + i = new InstructionType((short)176, "areturn", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[176] = i; + + i = new InstructionType((short)177, "return", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[177] = i; + + i = new InstructionType((short)178, "getstatic", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[178] = i; + + i = new InstructionType((short)179, "putstatic", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[179] = i; + + i = new InstructionType((short)180, "getfield", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{new Form.Value("","value"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[180] = i; + + i = new InstructionType((short)181, "putfield", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[181] = i; + + i = new InstructionType((short)182, "invokevirtual", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), new Form.Value("","arg1"), new Form.Value("","..."), new Form.Value("","argN"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[182] = i; + + i = new InstructionType((short)183, "invokespecial", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), new Form.Value("","arg1"), new Form.Value("","..."), new Form.Value("","argN"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[183] = i; + + i = new InstructionType((short)184, "invokestatic", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arg1"), new Form.Value("","..."), new Form.Value("","argN"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[184] = i; + + i = new InstructionType((short)185, "invokeinterface", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), new Form.Value("","arg1"), new Form.Value("","..."), new Form.Value("","argN"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), new Form.Value("byte","count"), new Form.Value("byte","0"), }); + i.setForm(f, 0); + + instructionTypes[185] = i; + + i = new InstructionType((short)186, "xxxunusedxxx", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[186] = i; + + i = new InstructionType((short)187, "new", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[187] = i; + + i = new InstructionType((short)188, "newarray", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("int","count"), }); + f.setOuts(new Form.Value[]{new Form.Value("object","arrayref"), }); + f.setOperands(new Form.Value[]{new Form.Value("byte","atype"), }); + i.setForm(f, 0); + + instructionTypes[188] = i; + + i = new InstructionType((short)189, "anewarray", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("int","count"), }); + f.setOuts(new Form.Value[]{new Form.Value("object","arrayref"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[189] = i; + + i = new InstructionType((short)190, "arraylength", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","arrayref"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","length"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[190] = i; + + i = new InstructionType((short)191, "athrow", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[191] = i; + + i = new InstructionType((short)192, "checkcast", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{new Form.Value("object","objectref"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[192] = i; + + i = new InstructionType((short)193, "instanceof", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{new Form.Value("int","result"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), }); + i.setForm(f, 0); + + instructionTypes[193] = i; + + i = new InstructionType((short)194, "monitorenter", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[194] = i; + + i = new InstructionType((short)195, "monitorexit", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","objectref"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[195] = i; + + i = new InstructionType((short)196, "wide", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, 0); + + instructionTypes[196] = i; + + i = new InstructionType((short)197, "multianewarray", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","count1"), new Form.Value("","..."), new Form.Value("","countN"), }); + f.setOuts(new Form.Value[]{new Form.Value("object","arrayref"), }); + f.setOperands(new Form.Value[]{new Form.Value("short","index"), new Form.Value("byte","dimension N"), }); + i.setForm(f, 0); + + instructionTypes[197] = i; + + i = new InstructionType((short)198, "ifnull", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[198] = i; + + i = new InstructionType((short)199, "ifnonnull", 1); + + f = new Form(); + f.setIns(new Form.Value[]{new Form.Value("","value"), }); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("short","branch"), }); + i.setForm(f, 0); + + instructionTypes[199] = i; + + i = new InstructionType((short)200, "goto_w", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{new Form.Value("int","branch"), }); + i.setForm(f, 0); + + instructionTypes[200] = i; + + i = new InstructionType((short)201, "jsr_w", 1); + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{new Form.Value("returnAddress","address"), }); + f.setOperands(new Form.Value[]{new Form.Value("int","branch"), }); + i.setForm(f, 0); + + instructionTypes[201] = i; + + } + } + \ No newline at end of file diff --git a/src/main/java/com/j2js/ConstGenerator.java b/src/main/java/com/j2js/ConstGenerator.java new file mode 100644 index 0000000..0b1a496 --- /dev/null +++ b/src/main/java/com/j2js/ConstGenerator.java @@ -0,0 +1,29 @@ +/* + * Created on 26.12.2005 + * Copyright Wolfgang Kuehn 2005 + */ +package com.j2js; + +import java.io.File; + +import javax.xml.transform.Result; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +/** + * @author kuehn + */ +public class ConstGenerator { + + public static void main(String[] args) throws Exception { + TransformerFactory factory = TransformerFactory.newInstance(); + StreamSource xslSource = new StreamSource(new File("src/com/j2js/instructions.xsl")); + Transformer xltTransformer = factory.newTransformer(xslSource); + + StreamSource xmlSource = new StreamSource(new File("src/com/j2js/instructions.xml")); + Result result = new StreamResult(new File("src/com/j2js/Const.java")); + xltTransformer.transform(xmlSource, result); + } +} diff --git a/src/main/java/com/j2js/Diff.java b/src/main/java/com/j2js/Diff.java new file mode 100644 index 0000000..9f03483 --- /dev/null +++ b/src/main/java/com/j2js/Diff.java @@ -0,0 +1,105 @@ +package com.j2js; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.Reader; + +/** + * Copyright by Wolfgang Kuehn 2005 + * Created on Feb 26, 2005 + */ +public class Diff { + + private int lookAheadLines = 5; + private LineNumberReader in1; + private LineNumberReader in2; + private int skipLineCount = 0; + + public Diff(Reader r1, Reader r2) { + in1 = new LineNumberReader(r1); + in2 = new LineNumberReader(r2); + } + + public Diff(File f1, File f2) throws FileNotFoundException { + this(new FileReader(f1), new FileReader(f2)); + } + + public boolean apply() throws IOException { + boolean equal = true; + int lineCount = 0; + + while (true) { + lineCount++; + String l1 = in1.readLine(); + String l2 = in2.readLine(); + if (l1==null || l2==null) { + break; + } else if (l1==null && l2!=null) { + equal = false; + System.out.println("File 2 extends File 1 at line " + in2.getLineNumber()); + break; + } else if (l1!=null && l2==null) { + equal = false; + System.out.println("File 1 extends File 2 at line " + in1.getLineNumber()); + break; + } + + if (lineCount > skipLineCount && !l1.equals(l2)) { + equal = false; + System.out.println("Mismatch"); + System.out.println("File 1 Line " + in1.getLineNumber() + ": " + l1); + System.out.println("File 2 Line " + in2.getLineNumber() + ": " + l2); + + if (!sync()) { + break; + } + } + } + + in1.close(); + in2.close(); + + return equal; + } + + private boolean sync() throws IOException { + int maxCharactersInLine = 200; + int readAheadLimit = lookAheadLines * maxCharactersInLine; + + in2.mark(readAheadLimit); + in1.mark(readAheadLimit); + + for (int i=0; i { + + public ExceptionHandlers(Code code) { + + // Join all contiguous CodeExceptions with equal handler PC. + // This is to eliminate multi-entrant execption handlers. + CodeException previousCodeException = null; + for (CodeException codeException : code.getExceptionTable()) { + if (previousCodeException != null && + previousCodeException.getHandlerPC() == codeException.getHandlerPC()) { + previousCodeException.setEndPC(codeException.getEndPC()); + } else { + add(new ExceptionHandler(codeException)); + } + previousCodeException = codeException; + } + } +} diff --git a/src/main/java/com/j2js/FileManager.java b/src/main/java/com/j2js/FileManager.java new file mode 100644 index 0000000..60bb0da --- /dev/null +++ b/src/main/java/com/j2js/FileManager.java @@ -0,0 +1,76 @@ +package com.j2js; + +import com.j2js.J2JSCompiler; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * A FileManager can resolve relative file names against a class path. + *

+ * The file names "java/lang.package-info", "java/lang/package-info" and + * "java.lang.package-info" are valid and equivalent. + *

+ * + * @author wolle + */ +public class FileManager { + + private List path = new ArrayList(); + + /** + * Create a new FileManager. + * + * @param classPath list of file system directories or jar files. + */ + public FileManager(List classPath) { + Log.getLogger().info("Resolving class path " + classPath); + + // Replace all jar files on classPath by instances of JarFile. + // Non-existing files are sorted out. + for (File file : classPath) { + if (!file.exists()) { + J2JSCompiler.errorCount++; + Log.getLogger().error("Cannot find resource on class path: " + file.getAbsolutePath()); + continue; + } + + if (file.getName().endsWith(".jar")) { + try { + path.add(new JarFile(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + path.add(file); + } + } + } + + /** + * Resolves a file given by name along the class path. + */ + public FileObject getFileForInput(String relativeName) { + for (Object o : path) { + if (o instanceof JarFile) { + JarFile jarFile = (JarFile) o; + JarEntry entry = jarFile.getJarEntry(relativeName); + if (entry != null) { + return new FileObject(jarFile, entry); + } + } else { + File file = new File(((File) o), relativeName); + if (file.exists()) { + return new FileObject(file); + } + } + } + + throw new RuntimeException("Could not find " + relativeName + " on class path"); + } + +} diff --git a/src/main/java/com/j2js/FileObject.java b/src/main/java/com/j2js/FileObject.java new file mode 100644 index 0000000..3807271 --- /dev/null +++ b/src/main/java/com/j2js/FileObject.java @@ -0,0 +1,54 @@ +package com.j2js; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * File abstraction for tools. In this context, file means an abstraction of regular + * files and other sources of data. + * + * @author wolle + */ +public class FileObject { + + private long lastModified; + private InputStream in; + + FileObject(JarFile jarFile, JarEntry entry) { + try { + in = jarFile.getInputStream(entry); + } catch (IOException e) { + throw new RuntimeException(e); + } + lastModified = entry.getTime(); + } + + FileObject(File file) { + try { + in = new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + lastModified = file.lastModified(); + } + + /** + * Gets an InputStream for this file object. + */ + public InputStream openInputStream() throws IOException { + return in; + } + + /** + * @return Returns the lastModified. + */ + public long getLastModified() { + return lastModified; + } + +} diff --git a/src/main/java/com/j2js/Form.java b/src/main/java/com/j2js/Form.java new file mode 100644 index 0000000..7b93814 --- /dev/null +++ b/src/main/java/com/j2js/Form.java @@ -0,0 +1,109 @@ +package com.j2js; + +import org.apache.bcel.generic.Type; + +/** + * @author wolfgang + */ +public class Form { + + public static int CATEGORY1 = 1; + public static int CATEGORY2 = 2; + + public static class Value { + public String type; + public String name; + + public Value(String theType, String theName) { + type = theType; + name = theName; + } + + public int getCategory() { + return type.equals("cat2") || type.equals("long") || type.equals("double")?CATEGORY2:CATEGORY1; + } + } + + private int index; + private Form.Value[] ins; + private Form.Value[] outs; + private Form.Value[] operands; + private Type type; + + /** + * @return Returns the ins. + */ + public Form.Value[] getIns() { + return ins; + } + + /** + * @param theIns The ins to set. + */ + public void setIns(Form.Value[] theIns) { + ins = theIns; + } + + /** + * @return Returns the operands. + */ + public Form.Value[] getOperands() { + return operands; + } + + /** + * @param theOperands The operands to set. + */ + public void setOperands(Form.Value[] theOperands) { + operands = theOperands; + } + + /** + * @return Returns the outs. + */ + public Form.Value[] getOuts() { + return outs; + } + + /** + * @param theOuts The outs to set. + */ + public void setOuts(Form.Value[] theOuts) { + outs = theOuts; + + if (theOuts.length != 1) return; + + String s = theOuts[0].type; + if (s.equals("object")) type = Type.OBJECT; + else if (s.equals("int")) type = Type.INT; + else if (s.equals("short")) type = Type.SHORT; + else if (s.equals("byte")) type = Type.SHORT; + else if (s.equals("long")) type = Type.LONG; + else if (s.equals("float")) type = Type.FLOAT; + else if (s.equals("double")) type = Type.DOUBLE; + else if (!s.equals("cat1") && !s.equals("returnAddress") && !s.equals("")) + throw new RuntimeException("Unhandled type: " + s); + } + + public int getOpStackDelta() { + return getOuts().length - getIns().length; + } + + public Type getResultType() { + if (type == null) throw new RuntimeException("Result type is not available for " + this); + + return type; + } + /** + * @return Returns the index. + */ + public int getIndex() { + return index; + } + /** + * @param theIndex The index to set. + */ + public void setIndex(int theIndex) { + index = theIndex; + } +} diff --git a/src/main/java/com/j2js/InstructionType.java b/src/main/java/com/j2js/InstructionType.java new file mode 100644 index 0000000..ecc2d3e --- /dev/null +++ b/src/main/java/com/j2js/InstructionType.java @@ -0,0 +1,59 @@ +package com.j2js; + +/** + * @author wolfgang + */ +public class InstructionType { + + private short code; + private String name; + private Form[] forms; + + public InstructionType(short theCode, String theName, int formCount) { + code = theCode; + name = theName; + forms = new Form[formCount]; + } + + public int getFormCount() { + return forms.length; + } + + public void setForm(Form form, int index) { + forms[index] = form; + form.setIndex(index); + } + + public Form getForm(int index) { + return forms[index]; + } + + /** + * @return Returns the name. + */ + public String getName() { + return name; + } + + /** + * @param theName The name to set. + */ + public void setName(String theName) { + name = theName; + } + + /** + * @return Returns the code. + */ + public short getCode() { + return code; + } + + /** + * @param theCode The code to set. + */ + public void setCode(short theCode) { + code = theCode; + } + +} diff --git a/src/main/java/com/j2js/J2JSCompiler.java b/src/main/java/com/j2js/J2JSCompiler.java new file mode 100644 index 0000000..3c6ba65 --- /dev/null +++ b/src/main/java/com/j2js/J2JSCompiler.java @@ -0,0 +1,357 @@ +// Copyright 2011 The j2js Authors. All Rights Reserved. +// +// This file is part of j2js. +// +// j2js is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// j2js is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with j2js. If not, see . + +package com.j2js; + +import java.io.File; +import java.io.IOException; + +import java.util.ArrayList; +import java.util.List; + +import com.j2js.FileManager; +import com.j2js.Log; +import com.j2js.Utils; +import com.j2js.assembly.Project; +import com.j2js.visitors.JavaScriptGenerator; + +/** + * The cross-compiler translates Java class files into JavaScript code and assembles all reachable code + * into assemblies. + *

+ * For details please refer to the plugin description. + * + * @author Wolfgang Kuehn + */ +public class J2JSCompiler { + + public static J2JSCompiler compiler; + + public static int errorCount = 0; + + private File basedir; + private File cacheFile; + + List assemblies = new ArrayList(); + + private List classpath = new ArrayList(); + + public FileManager fileManager; + + public boolean optimize = true; + public boolean failOnError = true; + private boolean compression = true; + + private String singleEntryPoint; + private String targetPlatform; + public int reductionLevel = 5; + + private int junkSizeInKiloBytes = Integer.MAX_VALUE; + + private boolean generateLineNumbers = false; + + public int compileCount = 0; + public JavaScriptGenerator generator; + + private Log logger; + + // Begin main + public static void main(String argv[]) throws Exception { + if ( argv == null || argv.length != 4 ) { + StringBuffer sb = new StringBuffer(); + sb.append("Usage: java "); + sb.append(J2JSCompiler.class.getName()); + sb.append(" "); + System.out.print(sb.toString()); + return; + } + + File basedir = new File(argv[0]); + String classpathElements = argv[1]; + String entryPointClassName = argv[2]; + + Assembly assembly = new Assembly(); + assembly.setEntryPointClassName(entryPointClassName); + + assembly.setTargetLocation(new File(basedir, argv[3])); + + J2JSCompiler compiler = new J2JSCompiler(); + compiler.setBasedir(basedir); + compiler.addClasspathElements(classpathElements); + compiler.addAssembly(assembly); + compiler.setGenerateLineNumbers(true); + compiler.execute(); + } + // End main + + /** + * Create new compiler with the current directory as basedir. + */ + public J2JSCompiler() { + setBasedir(new File(System.getProperty("user.dir"))); + setTargetPlatform("web"); + } + + public void execute() throws Exception { + if (logger == null) { + setLogger(new Log()); + } + + for (Assembly assembly : assemblies) { + execute(assembly); + } + } + + private boolean isMavenExecution() { + return System.getProperty("localRepository") != null; + } + + public void execute(Assembly assembly) throws Exception { + J2JSCompiler.compiler = this; + + logger.info("Entry point is " + assembly.getEntryPointClassName() + "#main(java.lang.String[])void"); + + if (classpath == null) { + throw new RuntimeException("Field classPath must be set"); + } + + if (assembly.getEntryPointClassName() == null) { + throw new RuntimeException("Field assembly.entryPointClassName must be set"); + } + + if (cacheFile == null) { + setCacheFile(new File(basedir, "target/j2js.cache")); + } + + if (assembly.getTargetLocation() == null) { + throw new RuntimeException("Field assembly.targetLocation must be set"); + //assembly.setTargetLocation(new File(basedir, "target/classes/assemblies/" + assembly.getEntryPointClassName().replaceAll("\\.", "/"))); + } + + logger.info("Creating assembly " + assembly.getTargetLocation()); + + fileManager = new FileManager(classpath); + Project project = Project.createSingleton(getCacheFile()); + assembly.setProject(project); + generator = new JavaScriptGenerator(project); + + errorCount = 0; + + assembly.addEntryPoint(assembly.getEntryPointClassName() + "#main(java.lang.String[])void"); + + for (String memberSignature : assembly.entryPoints) { + assembly.taint(memberSignature); + } + + long startTime = System.currentTimeMillis(); + + // Used by the JavaScript JVM. The static code analyser would miss these. + + String[] signatures = Utils.getProperty("j2js.preTaintedSignatures").split(";"); + for (int i=0; i 0) { + logger.error("There " + timesName("was|were", errorCount, "error|errors") + "."); + } + } + + private String timesName(String verb, int count, String noun) { + String[] verbs = verb.split("\\|"); + String[] nouns = noun.split("\\|"); + int index = (count==1?0:1); + return verbs[index] + " " + count + " " + nouns[index]; + } + + public void setCompression(boolean isCompression) { + this.compression = isCompression; + } + + public boolean isCompression() { + return compression; + } + + + /** + * For debugging. Internal use only. + */ + public void setSingleEntryPoint(String signature) { + singleEntryPoint = signature; + } + + /** + * @see #setSingleEntryPoint(String) + */ + public String getSingleEntryPoint() { + return singleEntryPoint; + } + + /** + * Sets one of the target platforms "web" or "javascript". + * + * @param targetPlatform optional; default is "web" + */ + public void setTargetPlatform(String targetPlatform) { + targetPlatform = targetPlatform.toLowerCase(); + if ("web".equals(targetPlatform) || "javascript".equals(targetPlatform)) { + this.targetPlatform = targetPlatform; + } else { + throw new IllegalArgumentException("Target platform must be web or javascript"); + } + } + + /** + * @see #setTargetPlatform(String) + */ + public String getTargetPlatform() { + return targetPlatform; + } + + public List getClasspath() { + return classpath; + } + + /** + * @param classpathElements (optional) additional class path elements + */ + public void addClasspathElements(List classpathElements) { + classpath.addAll(classpathElements); + } + + /** + * @param classpathElement (optional) additional class path element + */ + public void addClasspathElement(File classpathElement) { + classpath.add(classpathElement); + } + + /** + * Semicolon- or whitespace-separated list of class path elements. + * + * @param classPathElements (optional) additional class path elements + * @see #setClasspathElements(List) + */ + public void addClasspathElements(String classPathElements) { + String[] array = classPathElements.split("(;|,)"); + for (String path : array) { + path = path.trim(); + if (path.length() > 0) { + addClasspathElement(Utils.resolve(basedir, path)); + } + } + } + + public void setClasspathElements(List classpathElements) { + for (Object part : classpathElements) { + addClasspathElements((String) part); + } + } + + public void setFailOnError(boolean flag) { + failOnError = flag; + } + + public boolean isFailOnError() { + return failOnError; + } + + public File getCacheFile() { + return cacheFile; + } + + public void setCacheFile(File theCacheFile) { + cacheFile = theCacheFile; + } + + public List getAssemblies() { + return assemblies; + } + + public void setAssemlies(List assemblies) { + this.assemblies = assemblies; + } + + public void setGenerateLineNumbers(boolean theGenerateLineNumbers) { + generateLineNumbers = theGenerateLineNumbers; + } + + public boolean isGenerateLineNumbers() { + return generateLineNumbers; + } + + public void setJunkSizeInKiloBytes(int junkSizeInKiloBytes) { + if (junkSizeInKiloBytes < 1) { + throw new RuntimeException("Junk size must be greater than zero."); + } + this.junkSizeInKiloBytes = junkSizeInKiloBytes; + } + + public int getJunkSizeInKiloBytes() { + return junkSizeInKiloBytes; + } + + /** + * @return Returns the logger. + */ + public Log getLogger() { + return logger; + } + + /** + * Sets the logger. + */ + public void setLogger(Log logger) { + this.logger = logger; + Log.logger = logger; + } + + public void setBasedir(File basedir) { + this.basedir = basedir; + } + + public File getBasedir() { + return basedir; + } + + public void addAssembly(Assembly assembly) { + assemblies.add(assembly); + } + +} diff --git a/src/main/java/com/j2js/LineNumberCursor.java b/src/main/java/com/j2js/LineNumberCursor.java new file mode 100644 index 0000000..465736a --- /dev/null +++ b/src/main/java/com/j2js/LineNumberCursor.java @@ -0,0 +1,76 @@ +package com.j2js; + +import java.util.Arrays; +import java.util.Comparator; + +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.LineNumber; +import org.apache.bcel.classfile.LineNumberTable; + +import com.j2js.dom.ASTNode; + +public class LineNumberCursor { + + private class LineNumberComparator implements Comparator { + + public int compare(LineNumber arg0, LineNumber arg1) { + return arg0.getStartPC() - arg1.getStartPC(); + } + + } + + private LineNumber[] lineNumbers = null; + private int index = 0; + private int markedLineNumber = -1; + private int length; + + public LineNumberCursor(Code code) { + if (code == null) return; + LineNumberTable table = code.getLineNumberTable(); + if (table == null) return; + lineNumbers = table.getLineNumberTable(); + length = lineNumbers.length; + // TODO: Is table already ordered? + Arrays.sort(lineNumbers, new LineNumberComparator()); + } + + public boolean hasLineNumbers() { + return lineNumbers != null; + } + + public int getLineNumber(ASTNode node) { + if (!hasLineNumbers()) return -1; + + while (node.getBeginIndex() == Integer.MAX_VALUE && node.getPreviousSibling() != null) { + node = node.getPreviousSibling(); + } + + int pc = node.getBeginIndex(); + L: if (pc > lineNumbers[index].getStartPC()) { + do { + if (index+1 == length) break L; + index ++; + } while (pc >= lineNumbers[index].getStartPC()); + index --; + } else if (pc < lineNumbers[index].getStartPC()) { + do { + if (index == 0) break L; + index --; + } while (pc <= lineNumbers[index].getStartPC()); + index ++; + } + + return lineNumbers[index].getLineNumber(); + } + + public int getAndMarkLineNumber(ASTNode node) { + if (!hasLineNumbers()) return -1; + + int lineNumber = getLineNumber(node); + if (lineNumber == markedLineNumber) return -1; + + markedLineNumber = lineNumber; + return lineNumber; + } + +} diff --git a/src/main/java/com/j2js/Log.java b/src/main/java/com/j2js/Log.java new file mode 100644 index 0000000..4d54886 --- /dev/null +++ b/src/main/java/com/j2js/Log.java @@ -0,0 +1,132 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 04.10.2005 + */ +package com.j2js; + + +/** + * @author wolfgang + */ +public class Log { + + public static Log logger; + + public static Log getLogger() { + return logger; + } + + private int state = INFO; + + /** + * @return Returns the state. + */ + public int getState() { + return state; + } + + /** + * @param state The state to set. + */ + public void setState(int state) { + this.state = state; + } + + public static final int DEBUG = 3; + public static final int INFO = 2; + public static final int WARN = 1; + public static final int ERROR = 0; + + public void debug(CharSequence arg0, Throwable arg1) { + if (isDebugEnabled()) { + System.out.println("[DEBUG] " + arg0); + arg1.printStackTrace(); + } + } + + public void debug(CharSequence arg0) { + if (isDebugEnabled()) { + System.out.println("[DEBUG] " + arg0); + } + } + + public void debug(Throwable arg0) { + if (isDebugEnabled()) { + arg0.printStackTrace(); + } + } + + public void error(CharSequence arg0, Throwable arg1) { + if (isErrorEnabled()) { + System.out.println("[ERROR] " + arg0); + arg1.printStackTrace(); + } + } + + public void error(CharSequence arg0) { + if (isErrorEnabled()) { + System.out.println("[ERROR] " + arg0); + } + } + + public void error(Throwable arg0) { + if (isErrorEnabled()) { + arg0.printStackTrace(); + } + } + + public void info(CharSequence arg0, Throwable arg1) { + if (isInfoEnabled()) { + System.out.println("[INFO] " + arg0); + arg1.printStackTrace(); + } + } + + public void info(CharSequence arg0) { + if (isInfoEnabled()) { + System.out.println("[INFO] " + arg0); + } + } + + public void info(Throwable arg0) { + if (isInfoEnabled()) { + arg0.printStackTrace(); + } + } + + public boolean isDebugEnabled() { + return state >= DEBUG; + } + + public boolean isErrorEnabled() { + return state >= ERROR; + } + + public boolean isInfoEnabled() { + return state >= INFO; + } + + public boolean isWarnEnabled() { + return state >= WARN; + } + + public void warn(CharSequence arg0, Throwable arg1) { + if (isWarnEnabled()) { + System.out.println("[WARNING] " + arg0); + arg1.printStackTrace(); + } + } + + public void warn(CharSequence arg0) { + if (isWarnEnabled()) { + System.out.println("[WARNING] " + arg0); + } + } + + public void warn(Throwable arg0) { + if (isWarnEnabled()) { + arg0.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/j2js/OperandState.java b/src/main/java/com/j2js/OperandState.java new file mode 100644 index 0000000..8e248ed --- /dev/null +++ b/src/main/java/com/j2js/OperandState.java @@ -0,0 +1,38 @@ +/* + * Created on 20.10.2004 + * + * To change the template for this generated file go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ +package com.j2js; + +import com.j2js.dom.ASTNode; + +/** + * @author kuehn + * + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ +public class OperandState { + + int code; + int beginIndex; + int endIndex; + + ASTNode stmt; + + OperandState(int theCode) { + code = theCode; + } + + OperandState(int theCode, int theBeginIndex, ASTNode theStmt) { + code = theCode; + beginIndex = theBeginIndex; + stmt = theStmt; + } + + public String toString() { + return "State: " + code + " " + stmt; + } +} diff --git a/src/main/java/com/j2js/Optimizer.java b/src/main/java/com/j2js/Optimizer.java new file mode 100644 index 0000000..b34ec3e --- /dev/null +++ b/src/main/java/com/j2js/Optimizer.java @@ -0,0 +1,324 @@ +package com.j2js; + +import java.util.Iterator; +import java.util.List; + +import org.apache.bcel.generic.Type; + +import com.j2js.dom.ASTNode; +import com.j2js.dom.Assignable; +import com.j2js.dom.Assignment; +import com.j2js.dom.Block; +import com.j2js.dom.Expression; +import com.j2js.dom.FieldAccess; +import com.j2js.dom.InfixExpression; +import com.j2js.dom.MethodDeclaration; +import com.j2js.dom.NumberLiteral; +import com.j2js.dom.PStarExpression; +import com.j2js.dom.PostfixExpression; +import com.j2js.dom.PrefixExpression; +import com.j2js.dom.VariableBinding; +import com.j2js.dom.VariableDeclaration; + +public class Optimizer { + + private MethodDeclaration methodDecl; + private List tempDecls; + + public Optimizer(MethodDeclaration theMethodDecl, List theTempDecls) { + methodDecl = theMethodDecl; + tempDecls = theTempDecls; + } + + public static Expression negate(Expression expr) { + PrefixExpression pe = new PrefixExpression(); + pe.setOperator(PrefixExpression.NOT); + pe.setOperand(expr); + return pe; + } + + /** + * Simplifies the (possibly negated) expression. + */ + public static Expression simplifyBooleanExpression(Expression expr, boolean negate) { + if (expr instanceof PrefixExpression) { + PrefixExpression pe = (PrefixExpression) expr; + if (pe.getOperator() != PrefixExpression.NOT) return expr; + return simplifyBooleanExpression((Expression) pe.getOperand(), !negate); + } + + if (expr instanceof InfixExpression && expr.getTypeBinding() == Type.BOOLEAN) { + InfixExpression in = (InfixExpression) expr; + InfixExpression.Operator op = in.getOperator(); + if (negate) { + op = op.getComplement(); + if (op != InfixExpression.Operator.CONDITIONAL_AND && op != InfixExpression.Operator.CONDITIONAL_OR) + negate = false; + } + InfixExpression out = new InfixExpression(op); + out.widen(in); + out.setOperands(simplifyBooleanExpression(in.getLeftOperand(), negate), simplifyBooleanExpression(in.getRightOperand(), negate)); + return out; + } + + if (negate) { + PrefixExpression pe = new PrefixExpression(); + pe.setOperator(PrefixExpression.NOT); + pe.setOperand(expr); + return pe; + } + + return expr; + } + + private boolean representSameAssignables(Assignable a, Assignable b) { + if (!(a instanceof FieldAccess && b instanceof FieldAccess)) return false; + FieldAccess faa = (FieldAccess) a; + FieldAccess fab = (FieldAccess) b; + if (!faa.getName().equals(fab.getName())) return false; + if (faa.getExpression() instanceof VariableBinding && fab.getExpression() instanceof VariableBinding ) { + VariableBinding vba = (VariableBinding) faa.getExpression(); + VariableBinding vbb = (VariableBinding) fab.getExpression(); + return vba.getVariableDeclaration() == vbb.getVariableDeclaration(); + } + return false; + } + + /** + * Reduces occurences of + * temp = x; + * y = temp; + * to + * y = x; + */ +// private ASTNode bar1(ASTNode child, ASTNode next) { +// if (child instanceof Assignment && next instanceof Assignment) { +// Assignment a1 = (Assignment) child; +// Assignment a2 = (Assignment) next; +// if (a1.getLeftHandSide().equals(a2.getRightHandSide())) { +// a2.setRightHandSide(a1.getRightHandSide()); +// child.getParentBlock().removeChild(child); +// return next; +// } +// } +// return child; +// } + + private VariableBinding fetchVariableBinding(Expression expr, VariableDeclaration decl) { + ASTNode child = expr.getFirstChild(); + while (child != null) { + if (child instanceof VariableBinding && ((VariableBinding) child).getVariableDeclaration() == decl) + return (VariableBinding) child; + VariableBinding vb = fetchVariableBinding((Expression) child, decl); + if (vb != null) return vb; + child = child.getNextSibling(); + } + return null; + } + + /** + * If the specified expression corresponds to + * xy + 1 or xy - (-1), + * returns the INCREMENT operator. If the specified expression corresponds to + * xy - 1 or xy + (-1), + * returns the DECREMENT operator. Otherwise, null is returned. + * @param expr + * @return + */ + private PStarExpression.Operator getOp(InfixExpression expr) { + NumberLiteral nl; + if (expr.getRightOperand() instanceof NumberLiteral) { + nl = (NumberLiteral) expr.getRightOperand(); + } else return null; + + PStarExpression.Operator op; + if (expr.getOperator() == InfixExpression.Operator.PLUS) { + op = PStarExpression.INCREMENT; + } else if (expr.getOperator() == InfixExpression.Operator.MINUS) { + op = PStarExpression.DECREMENT; + } else { + return null; + } + + if (NumberLiteral.isOne(nl)) { + // We are ok. + } else if (NumberLiteral.isMinusOne(nl)) { + op = op.complement(); + } else { + return null; + } + return op; + } + + /** + * Reduces + * vb = x.y; (1) + * x.y = vb + 1; (2) + * ... + * expr(vb); + * to incremental form + * expr(x.y++); + * Likewise decrement. + */ + private boolean reduceXCrement(VariableDeclaration decl) { + Assignment a1 = null; + Assignment a2 = null; + VariableBinding vb1 = null; + VariableBinding vb2 = null; + + Assignable fa1 = null; + Assignable fa2 = null; + + InfixExpression sum = null; + + + Iterator iter = decl.vbs.iterator(); + + while (iter.hasNext()) { + VariableBinding vb = (VariableBinding) iter.next(); + + if (vb.getParentNode() instanceof Assignment) { + Assignment a = (Assignment) vb.getParentNode(); + if (a.getLeftHandSide() == vb && a.getRightHandSide() instanceof Assignable) { + vb1 = vb; + a1 = a; + fa1 = (Assignable) a.getRightHandSide(); + continue; + } + } + + if (vb.getParentNode() instanceof InfixExpression) { + InfixExpression infix = (InfixExpression) vb.getParentNode(); + if (infix.getParentNode() instanceof Assignment) { + Assignment a = (Assignment) infix.getParentNode(); + if (a.getLeftHandSide() instanceof Assignable) { + vb2 = vb; + fa2 = (Assignable) a.getLeftHandSide(); + a2 = a; + sum = infix; + continue; + } + } + } + } + + if (a1 == null || a2 == null) return false; + if (!fa1.isSame(fa2)) return false; + + PStarExpression.Operator operator = getOp(sum); + if (operator == null) return false; + + PStarExpression p = new PostfixExpression(); + p.setOperand((Expression) fa1); + p.setOperator(operator); + + decl.vbs.remove(vb1); + decl.vbs.remove(vb2); + VariableBinding vb = decl.vbs.get(0); + vb.getParentBlock().replaceChild(p, vb); + + Block b = a1.getParentBlock(); + b.removeChild(a1); + b.removeChild(a2); + return true; + } + + /** + * Reduces + * vb = x.y op z; (3) + * x.y = vb; (4) + * ... + * expr(vb); + * to incremental form + * expr(x.y op= z) or expr(++x.y); + * Likewise decrement. + */ + private boolean reduceYCrement(VariableDeclaration decl) { + Assignment a1 = null; + Assignment a2 = null; + VariableBinding vb1 = null; + VariableBinding vb2 = null; + + Assignable fa1 = null; + Assignable fa2 = null; + + InfixExpression infixExpr = null; + + Iterator iter = decl.vbs.iterator(); + + while (iter.hasNext()) { + VariableBinding vb = (VariableBinding) iter.next(); + + if (!(vb.getParentNode() instanceof Assignment)) continue; + + Assignment a = (Assignment) vb.getParentNode(); + if (a.getRightHandSide() == vb && a.getLeftHandSide() instanceof Assignable) { + vb2 = vb; + a2 = a; + fa2 = (Assignable) a.getLeftHandSide(); + continue; + } + + if (a.getLeftHandSide() == vb && a.getRightHandSide() instanceof InfixExpression) { + InfixExpression infix = (InfixExpression) a.getRightHandSide(); + if (!(infix.getLeftOperand() instanceof Assignable)) continue; + vb1 = vb; + a1 = a; + fa1 = (Assignable) infix.getLeftOperand(); + infixExpr = infix; + continue; + } + } + + if (a1 == null || a2 == null) return false; + if (!fa1.isSame(fa2)) return false; + + decl.vbs.remove(vb1); + decl.vbs.remove(vb2); + VariableBinding vb = decl.vbs.get(0); + Expression replacement = null; + + PStarExpression.Operator operator = getOp(infixExpr); + if (operator != null) { + PrefixExpression p = new PrefixExpression(); + p.setOperand((Expression) fa1); + p.setOperator(operator); + replacement = p; + } else { + InfixExpression.Operator op = infixExpr.getOperator(); + Assignment.Operator opp = Assignment.Operator.lookup(op.toString() + '='); + Assignment a = new Assignment(opp); + a.setLeftHandSide((Expression) fa2); + a.setRightHandSide(infixExpr.getRightOperand()); + replacement = a; + } + + vb.getParentBlock().replaceChild(replacement, vb); + + Block b = a1.getParentBlock(); + b.removeChild(a1); + b.removeChild(a2); + return true; + } + + public void optimize() { + if (false) return; + // Review this code. For example j2js.fa(obj, name)++ is illegal! + + Iterator iter = tempDecls.iterator(); + while (iter.hasNext()) { + VariableDeclaration decl = (VariableDeclaration) iter.next(); + int count = decl.vbs.size(); + if (count == 3) { + if (reduceXCrement(decl)) { + iter.remove(); + methodDecl.removeLocalVariable(decl.getName()); + } else if (reduceYCrement(decl)) { + iter.remove(); + methodDecl.removeLocalVariable(decl.getName()); + } + } + } + } + +} diff --git a/src/main/java/com/j2js/ParseException.java b/src/main/java/com/j2js/ParseException.java new file mode 100644 index 0000000..e039d00 --- /dev/null +++ b/src/main/java/com/j2js/ParseException.java @@ -0,0 +1,25 @@ +package com.j2js; + +import com.j2js.dom.ASTNode; + +/** + * @author kuehn + */ +public class ParseException extends RuntimeException { + + private ASTNode astNode; + + public ParseException(String msg, ASTNode node) { + super(msg); + astNode = node; + } + + public ParseException(Throwable cause, ASTNode node) { + super(cause); + astNode = node; + } + + public ASTNode getAstNode() { + return astNode; + } +} diff --git a/src/main/java/com/j2js/Parser.java b/src/main/java/com/j2js/Parser.java new file mode 100644 index 0000000..7f2e6b4 --- /dev/null +++ b/src/main/java/com/j2js/Parser.java @@ -0,0 +1,204 @@ +package com.j2js; + +import com.j2js.J2JSCompiler; + +import java.io.IOException; +import java.lang.reflect.Modifier; + +import com.j2js.Pass1; +import com.j2js.ParseException; + +import org.apache.bcel.classfile.Attribute; +import org.apache.bcel.classfile.AttributeReader; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.classfile.Field; +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.Type; + +import com.j2js.assembly.ClassUnit; +import com.j2js.assembly.Project; +import com.j2js.assembly.Signature; +import com.j2js.dom.*; + +/** + * @author wolfgang + */ +public class Parser { + + public static String getResourcePath(String name) { + name = name.replace('.', '/') + ".class"; + java.net.URL url = Parser.class.getClassLoader().getResource(name); + if (url == null) throw new RuntimeException("Resource not found: " + name); + return url.getPath(); + } + + private JavaClass jc; + private ClassUnit fileUnit; + + public Parser(ClassUnit theFileUnit) { + fileUnit = theFileUnit; + fileUnit.annotations = null; + + AttributeReader r = new AnnotationReader(fileUnit); + Attribute.addAttributeReader("RuntimeVisibleAnnotations", r); + + try { + ClassParser cp = new ClassParser(fileUnit.getClassFile().openInputStream(), fileUnit.getName()); + jc = cp.parse(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public TypeDeclaration parse() { + +// Attribute[] attributes = jc.getAttributes(); +// for (int i=0; i", "(java/lang/String)V;"); + ClassInstanceCreation cic = new ClassInstanceCreation(methodDecl, binding); + cic.addArgument(new StringLiteral("Unresolved decompilation problem")); + throwStmt.setExpression(cic); + body.appendChild(throwStmt); + methodDecl.setBody(body); + + } + + // Remove from body last expressionless return statement. + if (J2JSCompiler.compiler.optimize && methodDecl.getBody().getLastChild() instanceof ReturnStatement) { + ReturnStatement ret = (ReturnStatement) methodDecl.getBody().getLastChild(); + if (ret.getExpression() == null) { + methodDecl.getBody().removeChild(ret); + } + } + + Pass1.dump(methodDecl.getBody(), "Body of " + methodDecl.toString()); + +// if (typeDecl.getClassName().equals("java.lang.String")) { +// if (methodDecl.isInstanceConstructor()) { +// +// } +// } + + return; + } + + public ConstantPool getConstantPool() { + return jc.getConstantPool(); + } + + public String toString() { + return jc.getClassName(); + } + +} diff --git a/src/main/java/com/j2js/Pass1.java b/src/main/java/com/j2js/Pass1.java new file mode 100644 index 0000000..d658164 --- /dev/null +++ b/src/main/java/com/j2js/Pass1.java @@ -0,0 +1,2233 @@ +package com.j2js; + +import org.apache.bcel.Constants; +//import org.apache.bcel.classfile.*; +import org.apache.bcel.classfile.ClassFormatException; +import org.apache.bcel.classfile.CodeException; +import org.apache.bcel.classfile.Constant; +import org.apache.bcel.classfile.ConstantClass; +import org.apache.bcel.classfile.ConstantCP; +import org.apache.bcel.classfile.ConstantDouble; +import org.apache.bcel.classfile.ConstantFieldref; +import org.apache.bcel.classfile.ConstantInteger; +import org.apache.bcel.classfile.ConstantFloat; +import org.apache.bcel.classfile.ConstantLong; +import org.apache.bcel.classfile.ConstantNameAndType; +import org.apache.bcel.classfile.ConstantString; +import org.apache.bcel.classfile.ConstantUtf8; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.Utility; + +import org.apache.bcel.generic.BasicType; +import org.apache.bcel.generic.InstructionHandle; +import org.apache.bcel.generic.InstructionTargeter; +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.Type; +import org.apache.bcel.util.ByteSequence; + +import com.j2js.cfg.ControlFlowGraph; +import com.j2js.cfg.Node; +import com.j2js.cfg.SwitchEdge; +import com.j2js.cfg.TryHeaderNode; +import com.j2js.dom.*; +import com.j2js.visitors.SimpleGenerator; +import com.j2js.assembly.Project; +import com.j2js.assembly.Signature; + +import org.apache.bcel.generic.InstructionList; + +import com.j2js.J2JSCompiler; + +import java.io.*; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +public class Pass1 { + + private ConstantPool constantPool; + + private ByteSequence bytes; + private static ASTNode currentNode; + + private ASTNodeStack stack; + + private Code code; + private MethodDeclaration methodDecl; + // The currently parsed method. + private Method method; + + private List tryStatements = new ArrayList(); + private ControlFlowGraph graph = new ControlFlowGraph(tryStatements); + + // Not used anymore. + private int depth; + + private static Log logger = Log.getLogger(); + + + /* + * The `WIDE' instruction is used in the byte code to allow 16-bit wide + * indices for local variables. This opcode may precede one of + * iload, fload, aload, lload, dload, istore, fstore, astore, lstore, dstore, ret or iinc. + * The opcode immediately following takes an extra byte which is combined with + * the following byte to form a 16-bit value. + * In case of iinc, the default two 8 bit operants are widened to two 16 bit aperants accordingly. + */ + private boolean wide = false; + + public static ASTNode getCurrentNode() { + return currentNode; + } + + public Pass1(JavaClass jc) { + constantPool = jc.getConstantPool(); + } + + private CatchClause createCatchClause(TryStatement tryStmt, ExceptionHandler handle) { + CatchClause cStmt = new CatchClause(handle.getHandlerPC()); + VariableDeclaration decl = new VariableDeclaration(VariableDeclaration.LOCAL_PARAMETER); + decl.setName("_EX_"); + decl.setType(handle.getCatchType(constantPool)); + cStmt.setException(decl); + tryStmt.addCatchStatement(cStmt); + return cStmt; + } + + private void makeTryFrames() { + for (int i=0; i handleIterator = handlers.iterator(); + + ExceptionHandler handle = handleIterator.hasNext()?handleIterator.next():null; + while (handle != null) { + boolean hasFinally = false; + int start = handle.getStartPC(); + int end = handle.getEndPC(); + + TryStatement tryStmt = new TryStatement(); + tryStmt.header = (TryHeaderNode) graph.createNode(TryHeaderNode.class); + tryStmt.header.tryStmt = tryStmt; + + Block tryBlock = new Block(start, end); + tryStmt.setTryBlock(tryBlock); + + //tryStmt.setBeginIndex(start); + + tryStatements.add(tryStmt); + + CatchClause cStmt = null; + + // Collect all non-default handlers. The range of each handler is from the 'store'-instruction to the beginning of the next handler. + while (handle != null && !handle.isDefault() && handle.getStartPC()==start && handle.getEndPC()==end) { + if (cStmt!=null) { + cStmt.setEndIndex(handle.getHandlerPC()-1); + } + cStmt = createCatchClause(tryStmt, handle); + handle = handleIterator.hasNext()?handleIterator.next():null; + } + + int foo = -1; + if (handle != null && handle.isDefault() && handle.getStartPC()==start) { + // Collect first default handler. + hasFinally = true; + if (cStmt!=null) { + cStmt.setEndIndex(handle.getHandlerPC()-1); + tryStmt.setEndIndex(handle.getHandlerPC()-1); + // Warning: We only set a lower bound for the end index. The correct index is set later + // when the finally statement is analysed. + } + cStmt = createCatchClause(tryStmt, handle); + foo = handle.getHandlerPC(); + handle = handleIterator.hasNext()?handleIterator.next():null; + } + + // Last catch stmt has no endIndex, yet! + + while (handle != null && handle.isDefault() && (handle.getHandlerPC()==foo)) { + // Skip all remaining default handlers. + throw new RuntimeException("remaining default handlers"); + //handle = handleIterator.hasNext()?handleIterator.next():null; + } + + Block catches = tryStmt.getCatchStatements(); + if (catches.getChildCount() == 0) { + throw new ParseException("A try clause must have at least one (possibly default) catch clause", tryStmt); + } + cStmt = (CatchClause) catches.getChildAt(0); + tryBlock.setEndIndex(cStmt.getBeginIndex()-1); + cStmt = (CatchClause) catches.getLastChild(); + if (cStmt.getEndIndex() == Integer.MIN_VALUE) { + cStmt.setEndIndex(cStmt.getBeginIndex()+1); + } + tryStmt.setEndIndex(cStmt.getEndIndex()); + + if (hasFinally) { + // Can't say yet where finally block is located. + } + + if (logger.isDebugEnabled()) { + dump(tryStmt, "Try"); + } + } + } + + /** + * Dumps instructions and exeption table. + */ + private void dumpCode() { + InstructionList il = new InstructionList(code.getCode()); + InstructionHandle[] handles = il.getInstructionHandles(); + + for (InstructionHandle handle : handles) { + System.out.println(handle.toString(true)); + InstructionTargeter[] targeters = handle.getTargeters(); + if (targeters != null) { + for (InstructionTargeter targeter : handle.getTargeters()) { + System.out.println(" Targeter: " + targeter.toString() + " " + targeter.getClass()); + } + } + } + + for (CodeException ce : code.getExceptionTable()) { + String exceptionType; + if (ce.getCatchType() > 0) { + Constant constant = constantPool.getConstant(ce.getCatchType()); + exceptionType = Pass1.constantToString(constant, constantPool); + } else { + exceptionType = "Default"; + } + System.out.println(ce.toString() + " " + exceptionType); + } + } + + public void parse(Method theMethod, MethodDeclaration theMethodDecl) throws IOException { + method = theMethod; + methodDecl = theMethodDecl; + + code = method.getCode(); + + if (logger.isDebugEnabled()) { + dumpCode(); + } + + Block.TAG = 0; + + compileCodeException(); + + bytes = new ByteSequence(code.getCode()); + + graph.createNode(0); + + makeTryFrames(); + + parseStatement(); + + try { + Optimizer optimizer = new Optimizer(methodDecl, tempDecls); + optimizer.optimize(); + } catch (Error e) { + J2JSCompiler.errorCount++; + if (logger.isDebugEnabled()) { + logger.debug("In Expression Optimizer:\n" + e + "\n" + Utils.stackTraceToString(e)); + } else { + logger.error("In Expression Optimizer:\n " + e); + } + } + + Block block; + if (J2JSCompiler.compiler.reductionLevel == 0) { + block = graph.reduceDumb(); + } else { + block = graph.reduce(); + } + + methodDecl.setBody(block); + + } + + private boolean isProcedure(ASTNode stmt) { + if (stmt instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) stmt; + return mi.getTypeBinding().equals(Type.VOID); + } + return false; + } + + Node cNode; + Node lastCurrentNode; + + private void setCurrentNode(Node theNode) { + if (cNode == theNode) return; + cNode = theNode; + if (cNode != null && cNode != lastCurrentNode) { + logger.debug("Switched to " + cNode); + lastCurrentNode = cNode; + } + } + + private void joinNodes(Node node) { + Collection nodes = node.preds(); + Iterator iter = nodes.iterator(); + while (iter.hasNext()) { + Node n = (Node) iter.next(); + if (n.stack.size() == 0) iter.remove(); + } + if (nodes.size() > 0) { + mergeStacks(nodes, node.stack); + } + } + + /** + * Selects a single node as currently active node. + */ + private void selectActiveNode(int pc) { + // Find all nodes currently active at pc. + List activeNodes = new ArrayList(); + for (Node node : graph.getNodes()) { + if (node.getCurrentPc() == pc) { + activeNodes.add(node); + } + } + + if (activeNodes.size() == 0) { + // No active node: Create one. + Node node = graph.createNode(pc); + setCurrentNode(node); + return; + } + + if (activeNodes.size() == 1) { + // Return single active node. + Node node = activeNodes.get(0); + setCurrentNode(node); + return; + } + + // Multiple nodes. Select the one starting at pc. + + Node node = graph.getNode(pc); + if (node == null) throw new RuntimeException("No node found at " + pc); + + setCurrentNode(node); + + // Add edges from all other active nodes to the selected node. + activeNodes.remove(node); + for (Node n : activeNodes) { + graph.addEdge(n, node); + } + } + + /** + * If the stack of the specified node is [s0, s1, ..., sn], then [t1=s1; ... tn=sn} is + * appended to its block, and the stack is replaced by [t1, t2, ..., tn]. + */ + private void expressionsToVariables(Node node, boolean clone) { + if (node.stack.size() == 0) return; + + logger.debug("expressionsToVariables ..."); + + for (int i=0; i 0) { + + int pc = bytes.getIndex(); + //Logger.getLogger().finer("@" + pc); + + if (cNode != null) { + cNode.setCurrentPc(pc); + } + + selectActiveNode(pc); + + if (cNode.getInitialPc() == pc) { + joinNodes(cNode); + } + + stack = cNode.stack; + + ASTNode stmt = parseInstruction(); + //InstructionHandle handle = il.findHandle(stmt.getBeginIndex()); + //System.out.println(">>>" + handle); + + if (stmt instanceof NoOperation) continue; + + depth += stmt.getStackDelta(); + + if (stmt instanceof VariableBinding) { + depth = depth; + } + + logger.debug(" -> " + stmt + " @ " + + methodDecl.getLineNumberCursor().getLineNumber(stmt) + + ", depth:" + depth + ", delta:" + stmt.getStackDelta()); + + if (stmt instanceof JumpSubRoutine) { + JumpSubRoutine jsr = (JumpSubRoutine) stmt; + cNode.block.setEndIndex(jsr.getEndIndex()); + + Node finallyNode = graph.getNode(jsr.getTargetIndex()); + + if (finallyNode == null) { + // Found finally clause. + finallyNode = graph.createNode(jsr.getTargetIndex()); + // Generate dummy expression for the astore instruction of the finally block. + finallyNode.stack = new ASTNodeStack(new Expression()); + + //finallyNode.jsrCallers.add(cNode); + } + finallyNode.jsrCallers.add(cNode); + if (cNode.preds().size() == 1 && finallyNode.preds().size() == 0 && cNode.getPred() instanceof TryHeaderNode) { + // Current node must be the default handler. + // TODO: This only works if default handler is a single node!! + TryHeaderNode tryHeaderNode = (TryHeaderNode) cNode.getPred(); + // Attach finally to its try header node. + tryHeaderNode.setFinallyNode(finallyNode); + } + + } else if (stmt instanceof ConditionalBranch) { + ConditionalBranch cond = (ConditionalBranch) stmt; + + if (bytes.getIndex() == cond.getTargetIndex()) { + // This is a conditional branch with an empty body, i.e. not a branch at all. We ignore it. + } else { + Node elseNode = graph.getOrCreateNode(bytes.getIndex()); + + Node ifNode; + if (cond.getTargetIndex() <= pc) { + Node[] nodes = graph.getOrSplitNodeAt(cNode, cond.getTargetIndex()); + cNode = nodes[0]; + ifNode = nodes[1]; + } else { + ifNode = graph.getOrCreateNode(cond.getTargetIndex()); + } + + BooleanExpression be = new BooleanExpression(cond.getExpression()); + + graph.addIfElseEdge(cNode, ifNode, elseNode, be); + expressionsToVariables(cNode, false); + cNode = null; + } + } else if (stmt instanceof Jump) { + int targetPc = ((Jump) stmt).getTargetIndex(); + Node targetNode; + + if (targetPc <= pc) { + // Backward jump. + Node[] nodes = graph.getOrSplitNodeAt(cNode, targetPc); + cNode = nodes[0]; + targetNode = nodes[1]; + } else { + targetNode = graph.getOrCreateNode(targetPc); + } + graph.addEdge(cNode, targetNode); + cNode = null; + } else if (stmt instanceof SynchronizedBlock || isProcedure(stmt)) { + cNode.block.appendChild(stmt); + } else if (stmt instanceof Assignment) { + expressionsToVariables(cNode, true); + cNode.block.appendChild(stmt); + } else if (stmt instanceof ThrowStatement || stmt instanceof ReturnStatement) { + cNode.block.appendChild(stmt); + cNode.close(); + cNode = null; + } else { + stack.push(stmt); + } + } + + } + + + void dump(Collection nodes) { + if (!logger.isDebugEnabled()) return; + + Iterator iter = nodes.iterator(); + while (iter.hasNext()) { + Node node = (Node) iter.next(); + dump(node.stack, node.toString()); + } + } + + public static void dump(ASTNode node, String msg) { + if (!logger.isDebugEnabled()) return; + + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(ba); + out.println(msg); + SimpleGenerator generator = new SimpleGenerator(out); + generator.visit(node); + out.close(); + try { + ba.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + logger.debug(ba.toString()); + } + + static void dump(List list, String msg) { + if (!logger.isDebugEnabled()) return; + + StringBuffer sb = new StringBuffer(); + sb.append("Begin dumping " + msg + "...\n"); + for (int i=0; i tempDecls = new ArrayList(); + + private Expression[] duplicate(Expression e) { + if (e instanceof NumberLiteral || e instanceof ThisExpression || e instanceof StringLiteral) { + // Refered values will never change nor will they have side effects, + // so we do not need to create an intermediate variable. + return new Expression[] {e, (Expression) e.clone()}; + } + + if (e instanceof VariableBinding && ((VariableBinding) e).isTemporary()) { + VariableBinding vb1 = (VariableBinding) e; + VariableBinding vb2 = (VariableBinding) vb1.clone(); + return new VariableBinding[] {vb1, vb2}; + } + + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + a.setRange(bytes.getIndex(), bytes.getIndex()); + VariableBinding vb1 = methodDecl.createAnonymousVariableBinding(e.getTypeBinding(), true); + VariableBinding vb2 = (VariableBinding) vb1.clone(); + VariableBinding vb3 = (VariableBinding) vb1.clone(); + tempDecls.add(vb1.getVariableDeclaration()); + vb1.getVariableDeclaration().setParentNode(methodDecl); + a.setLeftHandSide(vb1); + a.setRightHandSide(e); + cNode.block.appendChild(a); + return new VariableBinding[] {vb2, vb3}; + } + + /** + * Creates a new case group at the specified pc if it does not yet exist. + * + * @param header the switch header + * @param switchEdges all currently existing switch edges + * @param startPc the pc at which the case group starts + * @return the switch edge to the case group. + */ + private SwitchEdge getOrCreateCaseGroup(Node header, Map switchEdges, int startPc) { + SwitchEdge switchEdge = switchEdges.get(startPc); + if (switchEdge == null) { + Node caseGroupNode = graph.createNode(startPc); + switchEdge = (SwitchEdge) graph.addEdge(header, caseGroupNode, SwitchEdge.class); + switchEdges.put(startPc, switchEdge); + } + + return switchEdge; + } + + private int readUnsigned() throws IOException { + int index; + if (wide) { + index = bytes.readUnsignedShort(); + wide = false; // Clear flag + } else { + index = bytes.readUnsignedByte(); + } + return index; + } + + private int readSigned() throws IOException { + int index; + if (wide) { + index = bytes.readShort(); + wide = false; // Clear flag + } else { + index = bytes.readByte(); + } + return index; + } + + private void dup1() { + Expression[] value1 = duplicate(stack.pop()); + stack.push(value1[0]); + stack.push(value1[1]); + } + + private void dup2() { + Expression[] value1 = duplicate(stack.pop()); + Expression[] value2 = duplicate(stack.pop()); + stack.push(value2[0]); + stack.push(value1[0]); + stack.push(value2[1]); + stack.push(value1[1]); + } + + private ASTNode parseInstruction() throws IOException { + int currentIndex = bytes.getIndex(); + short opcode = (short) bytes.readUnsignedByte(); + + InstructionType instructionType = Const.instructionTypes[opcode]; + + Form form = selectForm(instructionType); + + int opStackDelta = form.getOpStackDelta(); + + ASTNode instruction = null; + + logger.debug(currentIndex + " " + instructionType.getName() + "[" + opcode + "] "); + + switch (opcode) { + /* + * Switches have variable length arguments. + */ + case Const.TABLESWITCH : + // Format: tableswitch, padding(byte), padding(byte), padding(byte), default(int), low(int), high(int) + // Operand stack: ..., index(int) -> ... + case Const.LOOKUPSWITCH : { + // Format: lookupswitch, padding(byte), padding(byte), padding(byte), default(int), npairs(int) + // Operand stack: ..., key() -> ... + Node switchNode = graph.createNode(currentIndex); + switchNode.isSwitchHeader = true; + graph.addEdge(cNode, switchNode); + cNode = null; + + int defaultOffset; + int npairs; + int offset; + int remainder = bytes.getIndex() % 4; + int noPadBytes = (remainder == 0) ? 0 : 4 - remainder; + + // Skip (0-3) padding bytes, i.e., the following bytes are 4-byte-aligned. + for (int i = 0; i < noPadBytes; i++) { + byte b; + + if ((b = bytes.readByte()) != 0) { + logger.warn("Padding byte != 0 in " + instructionType.getName() + ":" + b); + } + } + + defaultOffset = bytes.readInt(); + + int low = 0; + if (opcode==Const.LOOKUPSWITCH) { + npairs = bytes.readInt(); + offset = bytes.getIndex() - 8 - noPadBytes - 1; + } else { + low = bytes.readInt(); + int high = bytes.readInt(); + npairs = high - low + 1; + offset = bytes.getIndex() - 12 - noPadBytes - 1; + } + + defaultOffset += offset; + + switchNode.switchExpression = stack.pop(); + + TreeMap caseGroups = new TreeMap(); + + // Add all cases. + for (int i = 0; i < npairs; i++) { + int key; + if (opcode==Const.LOOKUPSWITCH) { + key = bytes.readInt(); + } else { + key = low + i; + } + + SwitchEdge switchEdge = getOrCreateCaseGroup(switchNode, caseGroups, offset + bytes.readInt()); + switchEdge.expressions.add(NumberLiteral.create(new Integer(key))); + } + + Node defaultNode = graph.createNode(defaultOffset); + graph.addEdge(switchNode, defaultNode); + //getOrCreateCaseGroup(switchNode, caseGroups, defaultOffset); + + instruction = new NoOperation(); + break; + } + + case Const.CHECKCAST : { + // Format: checkcast, index(short) + // Operand stack: ..., objectref() -> ..., objectref(object) + CastExpression cast = new CastExpression(); + int index = bytes.readUnsignedShort(); + ConstantClass c = (ConstantClass) constantPool.getConstant(index); + ObjectType type = new ObjectType(c.getBytes(constantPool).replace('/', '.')); + cast.setTypeBinding(type); + cast.setExpression(stack.pop()); + instruction = cast; + break; + } + + + case Const.INSTANCEOF : { + // Format: instanceof, index(short) + // Operand stack: ..., objectref() -> ..., result(int) + int index = bytes.readUnsignedShort(); + InstanceofExpression ex = new InstanceofExpression(); + Expression objectref = stack.pop(); + ex.setLeftOperand(objectref); + ConstantClass c = (ConstantClass) constantPool.getConstant(index); + ObjectType type = new ObjectType(c.getBytes(constantPool).replace('/', '.')); + ex.setRightOperand(type); + ex.widen(objectref); + instruction = ex; + break; + } + + + case Const.ACONST_NULL : + // Format: aconst_null + // Operand stack: ... -> ..., null(object) + instruction = new NullLiteral(); + break; + + case Const.JSR : { + // Format: jsr, branch(short) + // Operand stack: ... -> ..., address(returnAddress) + instruction = new JumpSubRoutine(currentIndex + bytes.readShort()); + opStackDelta = 0; + break; + } + + case Const.JSR_W : { + // Format: jsr_w, branch(int) + // Operand stack: ... -> ..., address() + instruction = new JumpSubRoutine(currentIndex + bytes.readInt()); + break; + } + + case Const.IFEQ : + // Format: ifeq, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.EQUALS, NumberLiteral.create(0)); + break; + case Const.IFNE : + // Format: ifne, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.NOT_EQUALS, NumberLiteral.create(0)); + break; + case Const.IFGE : + // Format: ifge, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.GREATER_EQUALS, NumberLiteral.create(0)); + break; + case Const.IFGT : + // Format: ifgt, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.GREATER, NumberLiteral.create(0)); + break; + case Const.IFLE : + // Format: ifle, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.LESS_EQUALS, NumberLiteral.create(0)); + break; + case Const.IFLT : + // Format: iflt, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.LESS, NumberLiteral.create(0)); + break; + case Const.IFNONNULL : + // Format: ifnonnull, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.NOT_EQUALS, new NullLiteral()); + break; + case Const.IFNULL : + // Format: ifnull, branch(short) + // Operand stack: ..., value() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.EQUALS, new NullLiteral()); + break; + + case Const.IF_ACMPEQ : + // Format: if_acmpeq, branch(short) + // Operand stack: ..., value1(), value2() -> ... + case Const.IF_ICMPEQ : + // Format: if_icmpeq, branch(short) + // Operand stack: ..., value1(), value2() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.EQUALS); + break; + case Const.IF_ACMPNE : + // Format: if_acmpne, branch(short) + // Operand stack: ..., value1(), value2() -> ... + case Const.IF_ICMPNE : + // Format: if_icmpne, branch(short) + // Operand stack: ..., value1(), value2() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.NOT_EQUALS); + break; + case Const.IF_ICMPGE : + // Format: if_icmpge, branch(short) + // Operand stack: ..., value1(), value2() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.GREATER_EQUALS); + break; + case Const.IF_ICMPGT : + // Format: if_icmpgt, branch(short) + // Operand stack: ..., value1(), value2() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.GREATER); + break; + case Const.IF_ICMPLE : + // Format: if_icmple, branch(short) + // Operand stack: ..., value1(), value2() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.LESS_EQUALS); + break; + case Const.IF_ICMPLT : + // Format: if_icmplt, branch(short) + // Operand stack: ..., value1(), value2() -> ... + instruction = createConditional(currentIndex, InfixExpression.Operator.LESS); + break; + + case Const.LCMP : + // Format: lcmp + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.FCMPL : + // Format: fcmpl + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.FCMPG : + // Format: fcmpg + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.DCMPL : + // Format: dcmpl + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.DCMPG : { + // Format: dcmpg + // Operand stack: ..., value1(), value2() -> ..., result(int) + MethodBinding binding = MethodBinding.lookup("javascript.Utils", "cmp", "(DDI)I"); + MethodInvocation mi = new MethodInvocation(methodDecl, binding); + + Expression value2 = stack.pop(); + mi.addArgument(stack.pop()); + mi.addArgument(value2); + + int gORl = 0; + if (instructionType.getName().endsWith("g")) gORl = 1; + else if (instructionType.getName().endsWith("l")) gORl = -1; + mi.addArgument(NumberLiteral.create(gORl)); + + instruction = mi; + + break; + } + + case Const.GOTO : { + // Format: goto, branch(short) + // Operand stack: ... -> ... + instruction = new Jump(currentIndex + bytes.readShort()); + break; + } + + case Const.GOTO_W : { + // Format: goto_w, branch(int) + // Operand stack: ... -> ... + instruction = new Jump(currentIndex + bytes.readInt()); + break; + } + + case Const.NEW: { + // Format: new, index(short) + // Operand stack: ... -> ..., objectref(object) + ConstantClass c = (ConstantClass) constantPool.getConstant(bytes.readUnsignedShort()); + ObjectType type = new ObjectType(c.getBytes(constantPool).replace('/', '.')); + + instruction = new ClassInstanceCreation(type); + } + break; + + /** + * Create new array with component type being primitiv. + */ + case Const.NEWARRAY: { + // Format: newarray, atype(byte) + // Operand stack: ..., count(int) -> ..., arrayref(object) + + String componentSignature = BasicType.getType(bytes.readByte()).getSignature(); + + // Create a new array with components of type componentType. + List dimensions = new ArrayList(); + dimensions.add(stack.pop()); + ArrayCreation ac = new ArrayCreation(methodDecl, new ObjectType("[" + componentSignature), dimensions); + instruction = ac; + break; + } + + /* + * Create new array of reference of non-primitiv type. + */ + case Const.ANEWARRAY: { + // Format: anewarray, index(short) + // Operand stack: ..., count(int) -> ..., arrayref(object) + + ConstantClass c = (ConstantClass) constantPool.getConstant(bytes.readUnsignedShort()); + String componentSignature = c.getBytes(constantPool).replace('/', '.'); + Type arrayType; + if (componentSignature.startsWith("[")) { + arrayType = new ObjectType("[" + componentSignature); + } else { + arrayType = new ObjectType("[L" + componentSignature + ";"); + } + + // Create a new array with components of type componentType. + List dimensions = new ArrayList(); + dimensions.add(stack.pop()); + ArrayCreation ac = new ArrayCreation(methodDecl, arrayType, dimensions); + instruction = ac; + break; + } + + /* + * Multidimensional array of references. + */ + case Const.MULTIANEWARRAY : { + // Format: multianewarray, index(short), dimension N(byte) + // Operand stack: ..., count1(), ...(), countN() -> ..., arrayref(object) + + ConstantClass c = (ConstantClass) constantPool.getConstant(bytes.readUnsignedShort()); + ObjectType arrayType = new ObjectType(c.getBytes(constantPool).replace('/', '.')); + + // Create a new multidimensional array of the array type arrayType. + // The array type must be an array class type of dimensionality greater than or equal to dimensions. + + int dimCount = bytes.readUnsignedByte(); + opStackDelta = 1 - dimCount; + List dimensions = new ArrayList(); + for (int i=0; i ... + case Const.PUTFIELD : { + // Format: putfield, index(short) + // Operand stack: ..., objectref(), value() -> ... + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + Expression rhs = stack.pop(); + + int index = bytes.readUnsignedShort(); + ConstantFieldref fieldRef = (ConstantFieldref) constantPool.getConstant(index, Constants.CONSTANT_Fieldref); + + FieldAccess fa = new FieldWrite(); + fa.setName(getFieldName(fieldRef)); + fa.setType(new ObjectType(fieldRef.getClass(constantPool))); + fa.initialize(methodDecl); + + if (opcode==Const.PUTFIELD) { + fa.setExpression(stack.pop()); + } + + a.setLeftHandSide(fa); + a.setRightHandSide(rhs); + + instruction = a; + break; + } + + case Const.GETFIELD : { + // Format: getfield, index(short) + // Operand stack: ..., objectref() -> ..., value() + int index = bytes.readUnsignedShort(); + ConstantFieldref fieldRef = (ConstantFieldref) constantPool.getConstant(index, Constants.CONSTANT_Fieldref); + + Expression ex = stack.pop(); + FieldAccess fa = new FieldRead(); + fa.setType(new ObjectType(fieldRef.getClass(constantPool))); + fa.setName(getFieldName(fieldRef)); + fa.setExpression(ex); + fa.initialize(methodDecl); + instruction = fa; + break; + } + + case Const.GETSTATIC : { + // Format: getstatic, index(short) + // Operand stack: ... -> ..., value() + int index = bytes.readUnsignedShort(); + ConstantFieldref fieldRef = (ConstantFieldref) constantPool.getConstant(index, Constants.CONSTANT_Fieldref); + + FieldAccess fa = new FieldRead(); + fa.setType(new ObjectType(fieldRef.getClass(constantPool))); + fa.setName(getFieldName(fieldRef)); + fa.initialize(methodDecl); + //Name e = new Name(fieldRef.getClass(constantPool)); + //fa.setExpression(e); + instruction = fa; + break; + } + + case Const.DUP : { + // Format: dup + // Operand stack: ..., value(cat1) -> ..., value(cat1), value(cat1) + dup1(); + instruction = stack.pop(); + break; + } + + case Const.DUP2 : + // (0) Format: dup2 + // (0) Operand stack: ..., value2(cat1), value1(cat1) -> ..., value2(cat1), value1(cat1), value2(cat1), value1(cat1) + // (1) Format: dup2 + // (1) Operand stack: ..., value(cat2) -> ..., value(cat2), value(cat2) + if (form.getIndex() == 0) { + dup2(); + instruction = stack.pop(); + } else { + dup1(); + instruction = stack.pop(); + } + break; + + case Const.DUP_X1 : { + // Format: dup_x1 + // Operand stack: ..., value2(cat1), value1(cat1) -> ..., value1(cat1), value2(cat1), value1(cat1) + dup1(); + stack.rotate(2); + instruction = stack.pop(); + break; + } + + case Const.DUP_X2 : { + // (0) Format: dup_x2 + // (0) Operand stack: ..., value3(cat1), value2(cat1), value1(cat1) -> ..., value1(cat1), value3(cat1), value2(cat1), value1(cat1) + // (1) Format: dup_x2 + // (1) Operand stack: ..., value2(cat2), value1(cat1) -> ..., value1(cat1), value2(cat2), value1(cat1) + if (form.getIndex()==0) { + dup1(); + stack.rotate(3); + } else { + dup1(); + stack.rotate(2); + } + instruction = stack.pop(); + break; + } + + case Const.DUP2_X1 : + // (0) Format: dup2_x1 + // (0) Operand stack: ..., value3(cat1), value2(cat1), value1(cat1) -> ..., value2(cat1), value1(cat1), value3(cat1), value2(cat1), value1(cat1) + // (1) Format: dup2_x1 + // (1) Operand stack: ..., value2(cat1), value1(cat2) -> ..., value1(cat2), value2(cat1), value1(cat2) + if (form.getIndex()==0) { + dup2(); + stack.rotate(4); + stack.rotate(4); + } else { + dup1(); + stack.rotate(2); + } + instruction = stack.pop(); + break; + + case Const.DUP2_X2 : + // (0) Format: dup2_x2 + // (0) Operand stack: ..., value4(cat1), value3(cat1), value2(cat1), value1(cat1) -> ..., value2(cat1), value1(cat1), value4(cat1), value3(cat1), value2(cat1), value1(cat1) + // (1) Format: dup2_x2 + // (1) Operand stack: ..., value3(cat1), value2(cat1), value1(cat2) -> ..., value1(cat2), value3(cat1), value2(cat1), value1(cat2) + // (2) Format: dup2_x2 + // (2) Operand stack: ..., value3(cat2), value2(cat1), value1(cat1) -> ..., value2(cat1), value1(cat1), value3(cat2), value2(cat1), value1(cat1) + // (3) Format: dup2_x2 + // (3) Operand stack: ..., value2(cat2), value1(cat2) -> ..., value1(cat2), value2(cat2), value1(cat2) + if (form.getIndex()==0) { + dup2(); + stack.rotate(5); + stack.rotate(5); + } else if (form.getIndex()==1) { + dup1(); + stack.rotate(3); + } else if (form.getIndex()==2) { + dup2(); + stack.rotate(4); + stack.rotate(4); + } else { + dup1(); + stack.rotate(2); + } + + instruction = stack.pop(); + break; + + case Const.SWAP : { + // Format: swap + // Operand stack: ..., value2(cat1), value1(cat1) -> ..., value1(cat1), value2(cat1) + stack.rotate(1); + instruction = new NoOperation(); + break; + } + + case Const.I2S : + // Format: i2s + // Operand stack: ..., value() -> ..., result(short) + case Const.I2F : + // Format: i2f + // Operand stack: ..., value() -> ..., result(float) + case Const.L2I : + // Format: l2i + // Operand stack: ..., value() -> ..., result(int) + case Const.F2I : + // Format: f2i + // Operand stack: ..., value(float) -> ..., result(int) + case Const.F2L : + // Format: f2l + // Operand stack: ..., value() -> ..., result(long) + case Const.L2F : + // Format: l2f + // Operand stack: ..., value() -> ..., result(float) + case Const.L2D : + // Format: l2d + // Operand stack: ..., value() -> ..., result(double) + case Const.D2I : + // Format: d2i + // Operand stack: ..., value() -> ..., result(int) + case Const.D2L : + // Format: d2l + // Operand stack: ..., value() -> ..., result(long) + case Const.D2F : + // Format: d2f + // Operand stack: ..., value() -> ..., result(float) + case Const.I2B : + // Format: i2b + // Operand stack: ..., value() -> ..., result(byte) + case Const.I2C : + // Format: i2c + // Operand stack: ..., value() -> ..., result(byte) + // Operation i2f may result in a loss of precision because values of type float have only 24 significand bits. + // Operation l2d may lose precision because values of type double have only 53 significand bits + instruction = new PrimitiveCast(opcode, stack.pop(), form.getResultType()); + break; + + case Const.I2L : + // Format: i2l + // Operand stack: ..., value() -> ..., result(long) + stack.peek().setTypeBinding(Type.LONG); + instruction = new NoOperation(); + break; + case Const.I2D : + // Format: i2d + // Operand stack: ..., value() -> ..., result(double) + case Const.F2D : + // Format: f2d + // Operand stack: ..., value() -> ..., result(double) + // Widening preserves the numeric value exactly, see VM Spec2, Section 2.6.2. + // For operation f2d there may be a rounding related to value set conversion. + stack.peek().setTypeBinding(Type.DOUBLE); + instruction = new NoOperation(); + break; + + case Const.INEG: + // Format: ineg + // Operand stack: ..., value() -> ..., result(int) + case Const.LNEG: + // Format: lneg + // Operand stack: ..., value() -> ..., result(long) + case Const.FNEG: + // Format: fneg + // Operand stack: ..., value() -> ..., result(float) + case Const.DNEG: + // Format: dneg + // Operand stack: ..., value() -> ..., result(double) + instruction = createPrefix(PrefixExpression.MINUS, stack.pop(), form.getResultType()); + break; + + case Const.ISHR : + // Format: ishr + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LSHR : + // Format: lshr + // Operand stack: ..., value1(), value2() -> ..., result(long) + instruction = createInfixRightLeft(InfixExpression.Operator.RIGHT_SHIFT_SIGNED, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.ISHL : + // Format: ishl + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LSHL : + // Format: lshl + // Operand stack: ..., value1(), value2() -> ..., result(long) + instruction = createInfixRightLeft(InfixExpression.Operator.LEFT_SHIFT, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IUSHR : + // Format: iushr + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LUSHR : + // Format: lushr + // Operand stack: ..., value1(), value2() -> ..., result(long) + instruction = createInfixRightLeft(InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IADD : + // Format: iadd + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LADD : + // Format: ladd + // Operand stack: ..., value1(), value2() -> ..., result(long) + case Const.FADD : + // Format: fadd + // Operand stack: ..., value1(), value2() -> ..., result(float) + case Const.DADD : + // Format: dadd + // Operand stack: ..., value1(double), value2(double) -> ..., result(double) + instruction = createInfixRightLeft(InfixExpression.Operator.PLUS, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.ISUB : + // Format: isub + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LSUB : + // Format: lsub + // Operand stack: ..., value1(), value2() -> ..., result(long) + case Const.FSUB : + // Format: fsub + // Operand stack: ..., value1(), value2() -> ..., result(float) + case Const.DSUB : + // Format: dsub + // Operand stack: ..., value1(double), value2(double) -> ..., result(double) + instruction = createInfixRightLeft(InfixExpression.Operator.MINUS, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IMUL : + // Format: imul + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LMUL : + // Format: lmul + // Operand stack: ..., value1(), value2() -> ..., result(long) + case Const.FMUL : + // Format: fmul + // Operand stack: ..., value1(), value2() -> ..., result(float) + case Const.DMUL : + // Format: dmul + // Operand stack: ..., value1(), value2() -> ..., result(double) + instruction = createInfixRightLeft(InfixExpression.Operator.TIMES, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IDIV : + // Format: idiv + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LDIV : + // Format: ldiv + // Operand stack: ..., value1(), value2() -> ..., result(long) + case Const.FDIV : + // Format: fdiv + // Operand stack: ..., value1(), value2() -> ..., result(float) + case Const.DDIV : + // Format: ddiv + // Operand stack: ..., value1(), value2() -> ..., result(double) + instruction = createInfixRightLeft(InfixExpression.Operator.DIVIDE, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IREM : + // Format: irem + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LREM : + // Format: lrem + // Operand stack: ..., value1(), value2() -> ..., result(long) + case Const.FREM : + // Format: frem + // Operand stack: ..., value1(), value2() -> ..., result(float) + case Const.DREM : + // Format: drem + // Operand stack: ..., value1(), value2() -> ..., result(double) + instruction = createInfixRightLeft(InfixExpression.Operator.REMAINDER, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IXOR : + // Format: ixor + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LXOR : + // Format: lxor + // Operand stack: ..., value1(), value2() -> ..., result(long) + instruction = createInfixRightLeft(InfixExpression.Operator.XOR, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IAND : + // Format: iand + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LAND : + // Format: land + // Operand stack: ..., value1(), value2() -> ..., result(long) + instruction = createInfixRightLeft(InfixExpression.Operator.AND, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IOR : + // Format: ior + // Operand stack: ..., value1(), value2() -> ..., result(int) + case Const.LOR : + // Format: lor + // Operand stack: ..., value1(), value2() -> ..., result(long) + instruction = createInfixRightLeft(InfixExpression.Operator.OR, stack.pop(), stack.pop(), form.getResultType()); + break; + + case Const.IINC : { + // Format: iinc, index(byte), const(int) + // Operand stack: ... -> ... + boolean isWide = wide; + int index = readUnsigned(); + // Reset wide flag. + wide = isWide; + int constByte = readSigned(); + + VariableBinding reference = createVariableBinding(index, Type.INT, true); + reference.setField(false); + +// if (stack.peek() instanceof VariableBinding) { +// if (reference.isSame(reference) && Math.abs(constByte) == 1) { +// stack.pop(); +// PostfixExpression pe = new PostfixExpression(); +// pe.setOperator(constByte==1?PStarExpression.INCREMENT:PStarExpression.DECREMENT); +// pe.setOperand(reference); +// instruction = pe; +// break; +// } +// } + + Assignment assign = new Assignment(Assignment.Operator.PLUS_ASSIGN); + assign.setLeftHandSide(reference); + assign.setRightHandSide(NumberLiteral.create(new Integer(constByte))); + instruction = assign; + break; + } + + case Const.ARRAYLENGTH : { + // Format: arraylength + // Operand stack: ..., arrayref() -> ..., length(int) + Expression arrayRef = stack.pop(); + FieldAccess access = new FieldRead(); + access.setExpression(arrayRef); + access.setName("length"); + //access.initialize(methodDecl); + instruction = access; + break; + } + + /* + * Remember wide byte which is used to form a 16-bit address in + * the following instruction. Relies on that the method is + * called again with the following opcode. + */ + case Const.WIDE : + // Format: wide + // Operand stack: ... -> ... + wide = true; + return new NoOperation(); + + case Const.ILOAD_0 : + // Format: iload_0 + // Operand stack: ... -> ..., value(int) + case Const.ILOAD_1 : + // Format: iload_1 + // Operand stack: ... -> ..., value(int) + case Const.ILOAD_2 : + // Format: iload_2 + // Operand stack: ... -> ..., value(int) + case Const.ILOAD_3 : { + // Format: iload_3 + // Operand stack: ... -> ..., value(int) + VariableBinding reference = createVariableBinding(opcode - Const.ILOAD_0, Type.INT, false); + reference.setField(false); + instruction = reference; + break; + } + + case Const.LLOAD_0 : + // Format: lload_0 + // Operand stack: ... -> ..., value(long) + case Const.LLOAD_1 : + // Format: lload_1 + // Operand stack: ... -> ..., value(long) + case Const.LLOAD_2 : + // Format: lload_2 + // Operand stack: ... -> ..., value(long) + case Const.LLOAD_3 : { + // Format: lload_3 + // Operand stack: ... -> ..., value(long) + VariableBinding reference = createVariableBinding(opcode - Const.LLOAD_0, Type.LONG, false); + reference.setField(false); + instruction = reference; + break; + } + + case Const.FLOAD_0 : + // Format: fload_0 + // Operand stack: ... -> ..., value(float) + case Const.FLOAD_1 : + // Format: fload_1 + // Operand stack: ... -> ..., value(float) + case Const.FLOAD_2 : + // Format: fload_2 + // Operand stack: ... -> ..., value(float) + case Const.FLOAD_3 : { + // Format: fload_3 + // Operand stack: ... -> ..., value(float) + VariableBinding reference = createVariableBinding(opcode - Const.FLOAD_0, Type.FLOAT, false); + reference.setField(false); + instruction = reference; + break; + } + + case Const.DLOAD_0 : + // Format: dload_0 + // Operand stack: ... -> ..., value(double) + case Const.DLOAD_1 : + // Format: dload_1 + // Operand stack: ... -> ..., value(double) + case Const.DLOAD_2 : + // Format: dload_2 + // Operand stack: ... -> ..., value(double) + case Const.DLOAD_3 : { + // Format: dload_3 + // Operand stack: ... -> ..., value(double) + VariableBinding reference = createVariableBinding(opcode - Const.DLOAD_0, Type.DOUBLE, false); + reference.setField(false); + instruction = reference; + break; + } + + case Const.ALOAD_0 : + // Format: aload_0 + // Operand stack: ... -> ..., objectref(object) + case Const.ALOAD_1 : + // Format: aload_1 + // Operand stack: ... -> ..., objectref(object) + case Const.ALOAD_2 : + // Format: aload_2 + // Operand stack: ... -> ..., objectref(object) + case Const.ALOAD_3 : { + // Format: aload_3 + // Operand stack: ... -> ..., objectref(object) + if (opcode == Const.ALOAD_0 && !Modifier.isStatic(methodDecl.getAccess())) { + ThisExpression reference = new ThisExpression(); + instruction = reference; + } else { + VariableBinding reference = createVariableBinding(opcode - Const.ALOAD_0, Type.OBJECT, false); + reference.setField(true); + instruction = reference; + } + break; + } + + case Const.ILOAD : + // Format: iload, index(byte) + // Operand stack: ... -> ..., value(int) + case Const.LLOAD : + // Format: lload, index(byte) + // Operand stack: ... -> ..., value(long) + case Const.FLOAD : + // Format: fload, index(byte) + // Operand stack: ... -> ..., value(float) + case Const.DLOAD : { + // Format: dload, index(byte) + // Operand stack: ... -> ..., value(double) + VariableBinding reference = createVariableBinding(readUnsigned(), form.getResultType(), false); + reference.setField(false); + instruction = reference; + break; + } + + case Const.ALOAD : { + // Format: aload, index(byte) + // Operand stack: ... -> ..., objectref(object) + VariableBinding reference = createVariableBinding(readUnsigned(), Type.OBJECT, false); + reference.setField(true); + instruction = reference; + break; + } + + case Const.BALOAD : + // Format: baload + // Operand stack: ..., arrayref(), index() -> ..., value(int) + case Const.CALOAD : + // Format: caload + // Operand stack: ..., arrayref(), index() -> ..., value(int) + case Const.SALOAD : + // Format: saload + // Operand stack: ..., arrayref(), index() -> ..., value(short) + case Const.IALOAD : + // Format: iaload + // Operand stack: ..., arrayref(), index() -> ..., value(int) + case Const.LALOAD : + // Format: laload + // Operand stack: ..., arrayref(), index() -> ..., value(long) + case Const.FALOAD : + // Format: faload + // Operand stack: ..., arrayref(), index() -> ..., value(float) + case Const.DALOAD : + // Format: daload + // Operand stack: ..., arrayref(), index() -> ..., value(double) + case Const.AALOAD : { + // Format: aaload + // Operand stack: ..., arrayref(), index() -> ..., objectref(object) + Expression index = stack.pop(); + Expression arrayRef = stack.pop(); + ArrayAccess aa; + aa = new ArrayAccess(); + aa.setTypeBinding(form.getResultType()); + aa.setArray(arrayRef); + aa.setIndex(index); + + instruction = aa; + break; + } + + case Const.BASTORE : + // Format: bastore + // Operand stack: ..., arrayref(), index(), value() -> ... + case Const.CASTORE : + // Format: castore + // Operand stack: ..., arrayref(), index(), value() -> ... + case Const.SASTORE : + // Format: sastore + // Operand stack: ..., array(), index(), value() -> ... + case Const.IASTORE : + // Format: iastore + // Operand stack: ..., arrayref(), index(), value() -> ... + case Const.LASTORE : + // Format: lastore + // Operand stack: ..., arrayref(), index(), value() -> ... + case Const.FASTORE : + // Format: fastore + // Operand stack: ..., arrayref(), index(), value() -> ... + case Const.DASTORE : + // Format: dastore + // Operand stack: ..., arrayref(), index(), value() -> ... + case Const.AASTORE : { + // Format: aastore + // Operand stack: ..., arrayref(), index(), value() -> ... + Expression value = stack.pop(); + Expression index = stack.pop(); + Expression arrayRef = stack.pop(); + if (arrayRef instanceof ArrayCreation) { + ArrayCreation ac = (ArrayCreation) arrayRef; + if (ac.getInitializer() == null) { + ac.setInitializer(new ArrayInitializer()); + } + ac.getInitializer().getExpressions().add(value); + instruction = new NoOperation(); + break; + } + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + + ArrayAccess aa; + aa = new ArrayAccess(); + aa.setArray(arrayRef); + aa.setIndex(index); + + a.setLeftHandSide(aa); + a.setRightHandSide(value); + instruction = a; + break; + } + + + case Const.DSTORE : + // Format: dstore, index(byte) + // Operand stack: ..., value(double) -> ... + case Const.DSTORE_0 : + // Format: dstore_0 + // Operand stack: ..., value(double) -> ... + case Const.DSTORE_1 : + // Format: dstore_1 + // Operand stack: ..., value(double) -> ... + case Const.DSTORE_2 : + // Format: dstore_2 + // Operand stack: ..., value(double) -> ... + case Const.DSTORE_3 : { + // Format: dstore_3 + // Operand stack: ..., value(double) -> ... + int index; + if (opcode == Const.DSTORE) { + index = readUnsigned(); + } else { + index = opcode - Const.DSTORE_0; + } + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + VariableBinding reference = createVariableBinding(index, Type.DOUBLE, true); + reference.setField(false); + a.setLeftHandSide(reference); + a.setRightHandSide(stack.pop()); + instruction = a; + break; + } + + case Const.FSTORE : + // Format: fstore, index(byte) + // Operand stack: ..., value() -> ... + case Const.FSTORE_0 : + // Format: fstore_0 + // Operand stack: ..., value(float) -> ... + case Const.FSTORE_1 : + // Format: fstore_1 + // Operand stack: ..., value(float) -> ... + case Const.FSTORE_2 : + // Format: fstore_2 + // Operand stack: ..., value(float) -> ... + case Const.FSTORE_3 : { + // Format: fstore_3 + // Operand stack: ..., value(float) -> ... + int index; + if (opcode == Const.FSTORE) { + index = readUnsigned(); + } else { + index = opcode - Const.FSTORE_0; + } + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + VariableBinding reference = createVariableBinding(index, Type.FLOAT, true); + reference.setField(false); + a.setLeftHandSide(reference); + a.setRightHandSide(stack.pop()); + instruction = a; + break; + } + + case Const.ISTORE : + // Format: istore, index(byte) + // Operand stack: ..., value() -> ... + case Const.ISTORE_0 : + // Format: istore_0 + // Operand stack: ..., value() -> ... + case Const.ISTORE_1 : + // Format: istore_1 + // Operand stack: ..., value() -> ... + case Const.ISTORE_2 : + // Format: istore_2 + // Operand stack: ..., value() -> ... + case Const.ISTORE_3 : { + // Format: istore_3 + // Operand stack: ..., value() -> ... + int index; + if (opcode == Const.ISTORE) { + index = readUnsigned(); + } else { + index = opcode - Const.ISTORE_0; + } + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + VariableBinding reference = createVariableBinding(index, Type.INT, true); + reference.setField(false); + a.setLeftHandSide(reference); + a.setRightHandSide(stack.pop()); + instruction = a; + break; + } + + case Const.LSTORE : + // Format: lstore, index(byte) + // Operand stack: ..., value() -> ... + case Const.LSTORE_0 : + // Format: lstore_0 + // Operand stack: ..., value() -> ... + case Const.LSTORE_1 : + // Format: lstore_1 + // Operand stack: ..., value() -> ... + case Const.LSTORE_2 : + // Format: lstore_2 + // Operand stack: ..., value() -> ... + case Const.LSTORE_3 : { + // Format: lstore_3 + // Operand stack: ..., value() -> ... + int index; + if (opcode == Const.LSTORE) { + index = readUnsigned(); + } else { + index = opcode - Const.LSTORE_0; + } + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + VariableBinding reference = createVariableBinding(index, Type.LONG, true); + reference.setField(false); + a.setLeftHandSide(reference); + a.setRightHandSide(stack.pop()); + instruction = a; + break; + } + + case Const.ASTORE : + // Format: astore, index(byte) + // Operand stack: ..., objectref() -> ... + case Const.ASTORE_0 : + // Format: astore_0 + // Operand stack: ..., objectref() -> ... + case Const.ASTORE_1 : + // Format: astore_1 + // Operand stack: ..., objectref() -> ... + case Const.ASTORE_2 : + // Format: astore_2 + // Operand stack: ..., objectref() -> ... + case Const.ASTORE_3 : { + // Format: astore_3 + // Operand stack: ..., objectref() -> ... + + Assignment a = new Assignment(Assignment.Operator.ASSIGN); + int index; + if (opcode==Const.ASTORE) { + index = readUnsigned(); + } else { + index = (opcode - Const.ASTORE_0); + } + VariableBinding reference = createVariableBinding(index, Type.OBJECT, true); + a.setLeftHandSide(reference); + + // This may be the first instruction of an exception handler. + // It will store the implied exception. This exception is pushed onto the + // stack by makeTryFrames(). However, if the handler is unreachable, the + // Java Compiler may not generate an exception handler, and the stack is therefore + // empty. + // TODO: Refactor this Class by using org.apache.bcel.generic.InstructionList() + // and having two passes, not just one! + if (stack.size() > 0) { + a.setRightHandSide(stack.pop()); + } + instruction = a; + break; + } + + case Const.ATHROW: { + // Format: athrow + // Operand stack: ..., objectref() -> ..., objectref(object) + ThrowStatement throwStmt = new ThrowStatement(); + throwStmt.setExpression(stack.pop()); + instruction = throwStmt; + break; + } + + case Const.ICONST_M1 : + // Format: iconst_m1 + // Operand stack: ... -> ..., i(int) + case Const.ICONST_0 : + // Format: iconst_0 + // Operand stack: ... -> ..., i(int) + case Const.ICONST_1 : + // Format: iconst_1 + // Operand stack: ... -> ..., i(int) + case Const.ICONST_2 : + // Format: iconst_2 + // Operand stack: ... -> ..., i(int) + case Const.ICONST_3 : + // Format: iconst_3 + // Operand stack: ... -> ..., i(int) + case Const.ICONST_4 : + // Format: iconst_4 + // Operand stack: ... -> ..., i(int) + case Const.ICONST_5 : + // Format: iconst_5 + // Operand stack: ... -> ..., i(int) + instruction = NumberLiteral.create(new Integer(-1 + opcode - Const.ICONST_M1)); + break; + + case Const.LCONST_0 : + // Format: lconst_0 + // Operand stack: ... -> ..., l(long) + case Const.LCONST_1 : + // Format: lconst_1 + // Operand stack: ... -> ..., l(long) + // Push the long constant 0 or 1 onto the operand stack. + instruction = NumberLiteral.create(new Long(opcode - Const.LCONST_0)); + break; + + case Const.FCONST_0 : + // Format: fconst_0 + // Operand stack: ... -> ..., f(float) + case Const.FCONST_1 : + // Format: fconst_1 + // Operand stack: ... -> ..., f(float) + case Const.FCONST_2 : + // Format: fconst_2 + // Operand stack: ... -> ..., f(float) + // Push the float constant 0.0, 1.0 or 2.0 onto the operand stack. + instruction = NumberLiteral.create(new Float(opcode - Const.FCONST_0)); + break; + + case Const.DCONST_0 : + // Format: dconst_0 + // Operand stack: ... -> ..., d(double) + case Const.DCONST_1 : + // Format: dconst_1 + // Operand stack: ... -> ..., d(double) + // Push the double constant 0.0 or 1.0 onto the operand stack. + instruction = NumberLiteral.create(new Double(opcode - Const.DCONST_0)); + break; + + case Const.BIPUSH : { + // Format: bipush, byte(byte) + // Operand stack: ... -> ..., value(int) + NumberLiteral literal = NumberLiteral.create(new Byte(bytes.readByte())); + instruction = literal; + break; + } + + case Const.SIPUSH : { + // Format: sipush + // Operand stack: ... -> ..., value(short) + NumberLiteral il = NumberLiteral.create(new Short(bytes.readShort())); + instruction = il; + break; + } + + case Const.LDC : + // Format: ldc, index(byte) + // Operand stack: ... -> ..., value(cat1) + case Const.LDC_W : + // Format: ldc_w + // Operand stack: ... -> ..., value(cat1) + case Const.LDC2_W : { + // Format: ldc2_w + // Operand stack: ... -> ..., value(cat1) + int index; + if (opcode == Const.LDC) { + index = bytes.readUnsignedByte(); + } else { + index = bytes.readUnsignedShort(); + } + Constant constant = constantPool.getConstant(index); + + if (opcode==Const.LDC2_W && (constant.getTag()!=Constants.CONSTANT_Double && constant.getTag()!=Constants.CONSTANT_Long)) + throw new RuntimeException("LDC2_W must load long or double"); + + if (constant.getTag() == Constants.CONSTANT_Integer) { + instruction = NumberLiteral.create(new Integer(((ConstantInteger) constant).getBytes())); + } else if (constant.getTag() == Constants.CONSTANT_Float) { + instruction = NumberLiteral.create(new Float(((ConstantFloat) constant).getBytes())); + } else if (constant.getTag() == Constants.CONSTANT_Long) { + instruction = NumberLiteral.create(new Long(((ConstantLong) constant).getBytes())); + } else if (constant.getTag() == Constants.CONSTANT_Double) { + instruction = NumberLiteral.create(new Double(((ConstantDouble) constant).getBytes())); + } else if (constant.getTag() == Constants.CONSTANT_Utf8) { + instruction = new StringLiteral(((ConstantUtf8) constant).getBytes()); + } else if (constant.getTag() == Constants.CONSTANT_String) { + int k = ((ConstantString) constant).getStringIndex(); + constant = constantPool.getConstant(k, Constants.CONSTANT_Utf8); + instruction = new StringLiteral(((ConstantUtf8) constant).getBytes()); + } else if (constant.getTag() == Constants.CONSTANT_Class) { + Signature signature = Project.getSingleton().getSignature(((ConstantClass) constant).getBytes(constantPool)); + instruction = new ClassLiteral(signature); + } else { + throw new RuntimeException("Cannot handle constant tag: " + constant.getTag()); + } + break; + } + + + case Const.RET: { + // Format: ret, index(byte) + // Operand stack: ... -> ... + int index = readUnsigned(); + ReturnStatement r = new ReturnStatement(currentIndex, currentIndex); + r.setExpression(createVariableBinding(index, Type.INT, false)); + instruction = r; + break; + } + + case Const.RETURN: + // Format: return + // Operand stack: ... -> ... + case Const.IRETURN: + // Format: ireturn + // Operand stack: ..., value() -> ... + case Const.FRETURN: + // Format: freturn + // Operand stack: ..., value() -> ... + case Const.LRETURN: + // Format: lreturn + // Operand stack: ..., value() -> ... + case Const.DRETURN: + // Format: dreturn + // Operand stack: ..., value(double) -> ... + case Const.ARETURN: { + // Format: areturn + // Operand stack: ..., objectref() -> ... + ReturnStatement r = new ReturnStatement(currentIndex, currentIndex); + if (opcode != Const.RETURN) { + r.setExpression(stack.pop()); + } + instruction = r; + break; + } + + case Const.POP : + // Format: pop + // Operand stack: ..., value(cat1) -> ... + case Const.POP2: { + // (0) Format: pop2 + // (0) Operand stack: ..., value(cat2) -> ... + // (1) Format: pop2 + // (1) Operand stack: ..., value2(cat1), value1(cat1) -> ... + + if (opcode == Const.POP2 && form.getIndex() == 1) { + throw new UnsupportedOperationException("InstructionType " + instructionType.getName() + " not supported"); + } + + //instruction = stack.pop(); + // TODO: Which is correct? + // Node is probably an invokation of a method with unused result. + ASTNode a = stack.pop(); + if (!(a instanceof VariableBinding)) { + // Can't have binding on stack! TODO: VariableBinding must not be an ASTNode, + // because it has no location. + cNode.block.appendChild(a); + } + instruction = new NoOperation(); + break; + } + + case Const.NOP : + // Format: nop + // Operand stack: ... -> ... + return new NoOperation(); + + case Const.XXXUNUSEDXXX : + // Format: xxxunusedxxx + // Operand stack: ... -> ... + logger.info("Byte code contains unused operation (Ignored)"); + return new NoOperation(); + + case Const.INVOKEINTERFACE : + // Format: invokeinterface, index(short), count(byte), 0(byte) + // Operand stack: ..., objectref(), arg1(), ...(), argN() -> ... + case Const.INVOKESPECIAL : + // Format: invokespecial + // Operand stack: ..., objectref(), arg1(), ...(), argN() -> ... + case Const.INVOKEVIRTUAL : + // Format: invokevirtual + // Operand stack: ..., objectref(), arg1(), ...(), argN() -> ... + case Const.INVOKESTATIC : { + // Format: invokestatic, index(short) + // Operand stack: ..., arg1(), ...(), argN() -> ... + int index = bytes.readUnsignedShort(); + MethodBinding methodBinding = MethodBinding.lookup(index, constantPool); + MethodInvocation invocation = new MethodInvocation(methodDecl, methodBinding); + + //Processor.getLogger().finer(method.getName() + "->" + invocation.binding); + + int nArgs = methodBinding.getParameterTypes().length; + int kk = stack.size() - nArgs; + for (int i=0; i 0 && stack.peek() instanceof ClassInstanceCreation && methodBinding.getName().equals("")) { +// ClassInstanceCreation cic = (ClassInstanceCreation) stack.pop(); +// List args = invocation.getArguments(); +// for (int i=0; i ... + SynchronizedBlock sb = new SynchronizedBlock(); + sb.monitor = stack.pop(); + sb.widen(sb.monitor); + sb.setEndIndex(currentIndex); + instruction = sb; + break; + } + case Const.MONITOREXIT : + // Format: monitorexit + // Operand stack: ..., objectref() -> ... + instruction = new NoOperation(); + instruction.widen(stack.pop()); + instruction.setEndIndex(currentIndex); + break; + + default : + throw new UnsupportedOperationException("InstructionType " + instructionType.getName() + " not supported"); + //break; + } + + if (opcode!=Const.WIDE && wide) throw new RuntimeException("Expected wide operation"); + + instruction.setStackDelta(opStackDelta); + if (opcode < Const.DUP || opcode > Const.DUP2_X2) { + instruction.leftWiden(currentIndex); + instruction.rightWiden(bytes.getIndex()-1); + } + currentNode = instruction; + return instruction; + } + + ConditionalBranch createConditional(int currentIndex, InfixExpression.Operator operator) throws IOException { + ConditionalBranch c = new ConditionalBranch(currentIndex + bytes.readShort()); + InfixExpression be = new InfixExpression(operator); + Expression rightOperand = stack.pop(); + be.setOperands(stack.pop(),rightOperand); + c.setExpression(be); + return c; + } + + ConditionalBranch createConditional(int currentIndex, InfixExpression.Operator operator, + Expression rightOperand) throws IOException { + ConditionalBranch c = new ConditionalBranch(currentIndex + bytes.readShort()); + Expression leftOperand = stack.pop(); + + if (leftOperand.getTypeBinding()!=null && leftOperand.getTypeBinding()==Type.BOOLEAN) { + if (operator == InfixExpression.Operator.EQUALS && NumberLiteral.isZero(rightOperand)) { + c.setExpression(Optimizer.negate(leftOperand)); + } else { + c.setExpression(leftOperand); + } + } else { + InfixExpression be = new InfixExpression(operator); + be.setOperands(leftOperand, rightOperand); + c.setExpression(be); + } + + return c; + } + + private String getFieldName(ConstantFieldref fieldRef) { + ConstantNameAndType nameAndType = (ConstantNameAndType) constantPool.getConstant(fieldRef.getNameAndTypeIndex()); + return nameAndType.getName(constantPool); + } + + public static String constantToString(Constant c, ConstantPool constantPool) throws ClassFormatException { + String str; + byte tag = c.getTag(); + + switch (tag) { + case Constants.CONSTANT_Class : + str = Utility.compactClassName(((ConstantClass) c).getBytes(constantPool), false); + break; + + case Constants.CONSTANT_String : + str = "\"" + Utils.escape(((ConstantString) c).getBytes(constantPool)) + "\""; + break; + + case Constants.CONSTANT_Utf8 : + str = ((ConstantUtf8) c).getBytes(); + break; + case Constants.CONSTANT_Double : + str = "" + ((ConstantDouble) c).getBytes(); + break; + case Constants.CONSTANT_Float : + str = "" + ((ConstantFloat) c).getBytes(); + break; + case Constants.CONSTANT_Long : + str = "" + ((ConstantLong) c).getBytes(); + break; + case Constants.CONSTANT_Integer : + str = "" + ((ConstantInteger) c).getBytes(); + break; + + case Constants.CONSTANT_NameAndType : + str = ((ConstantNameAndType) c).getName(constantPool); + break; + + case Constants.CONSTANT_InterfaceMethodref : + case Constants.CONSTANT_Methodref : + case Constants.CONSTANT_Fieldref : + str = ((ConstantCP) c).getClass(constantPool); + break; + + default : // Never reached + throw new RuntimeException("Unknown constant type " + tag); + } + + return str; + } + +} diff --git a/src/main/java/com/j2js/Utils.java b/src/main/java/com/j2js/Utils.java new file mode 100644 index 0000000..84f4347 --- /dev/null +++ b/src/main/java/com/j2js/Utils.java @@ -0,0 +1,160 @@ +package com.j2js; + +import com.j2js.J2JSCompiler; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.DateFormat; +import java.util.Date; +import java.util.Properties; + + +import org.apache.bcel.generic.ArrayType; +import org.apache.bcel.generic.BasicType; +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.Type; + +import com.j2js.dom.ASTNode; +import com.j2js.dom.MethodDeclaration; + +/** + * @author j2js.com + */ +public final class Utils { + + private static final String propertiesFile = "j2js.properties"; + private static final Properties properties; + + static { + properties = new Properties(); + try { + properties.load(Utils.class.getClassLoader().getResourceAsStream(propertiesFile)); + } catch (Exception e) { + J2JSCompiler.errorCount++; + Log.getLogger().error("Could not read from classpath: " + propertiesFile); + throw new RuntimeException(e); + } + } + + private Utils() {} + + public static String generateExceptionMessage(MethodDeclaration methodDecl, ASTNode node) { + String msg = null; + if (node != null) { + int line = methodDecl.getLineNumberCursor().getLineNumber(node); + if (line != -1) { + msg = "Error near line " + line; + } + } + if (msg == null) { + msg = "Error"; + } + + msg += " in " + methodDecl.getMethodBinding(); + + return msg; + } + + public static RuntimeException generateException(Throwable e, MethodDeclaration methodDecl, ASTNode node) { + String msg = generateExceptionMessage(methodDecl, node); + J2JSCompiler.errorCount++; + Log.getLogger().error(msg); + return new RuntimeException(msg, e); + } + + public static String stackTraceToString(Throwable e) { + StringWriter sw = new StringWriter(); + PrintWriter writer = new PrintWriter(sw); + e.printStackTrace(writer); + writer.close(); + return sw.getBuffer().toString(); + } + + public static String currentTimeStamp() { + return DateFormat.getDateTimeInstance().format(new Date()); + } + + public static String getVersion() { + return (String) properties.get("j2js.version"); + } + + public static String getProperty(String key) { + return (String) properties.get(key); + } + + public static String getSignature(Type type) { + String signature; + + if (type instanceof ArrayType) { + ArrayType aType = (ArrayType) type; + signature = getSignature(aType.getBasicType()); + for (int i = 0; i declaredMembers; + + // The super class. + private ClassUnit superUnit; + + // All interfaces implemented by this unit. + private Collection interfaces; + + // All derived classes. + private Collection subUnits; + + public boolean isInterface = false; + + public boolean isConstructorTainted = false; + + public Map[] annotations; + + private Project project; + + // Transient fields start here. + private transient boolean isResolved = false; + + // The class file of this unit. + private transient FileObject classFile; + + public ClassUnit() { + } + + public ClassUnit(Project theProject, Signature theSignature) { + project = theProject; + + interfaces = new HashSet(); + declaredMembers = new HashMap(); + subUnits = new HashSet(); + lastCompiled = -1; + setSignature(theSignature); + } + + /** + * Reset all fields filled by compilation. + */ + void clear() { + // Do not remove registered subunits! + lastCompiled = -1; + removeInterfaces(); + setSuperUnit(null); + declaredMembers.clear(); + } + + /** + * Returns true if this unit is up to date. + */ + public boolean isUpToDate() { + return lastCompiled >= getLastModified(); + } + + public Collection getInterfaces() { + return interfaces; + } + + public void addInterface(ClassUnit interfaceUnit) { + interfaces.add(interfaceUnit); + //interfaceUnit.addSubUnit(this); + } + + private void removeInterfaces() { + Iterator iter = interfaces.iterator(); + while (iter.hasNext()) { + ClassUnit interfaceUnit = (ClassUnit) iter.next(); + interfaceUnit.removeSubUnit(this); + iter.remove(); + } + } + + public void addSubUnit(ClassUnit subUnit) { + if (subUnit == null) throw new NullPointerException(); + subUnits.add(subUnit); + } + + public void removeSubUnit(ClassUnit subUnit) { + if (subUnit == null) throw new NullPointerException(); + subUnits.remove(subUnit); + } + + public Collection getSubUnits() { + return subUnits; + } + + /** + * Returns the declared member with the specified signature. + */ + public MemberUnit getDeclaredMember(String signature) { + if (signature == null) throw new NullPointerException(); + return declaredMembers.get(signature); + } + + /** + * Returns all types to which this class can be converted, i.e., the collection of + * all supertypes and implemented interfaces and the class itself. + */ + public Collection getSupertypes() { + TypeCollector collector = new TypeCollector(); + project.visitSuperTypes(this, collector); + return collector.collectedTypes; + } + + /** + * Returns a member object that reflects the specified public member method of the class + * or interface represented by this Class object. + */ +// public MemberUnit getMember(String signature) { +// if (signature == null) throw new NullPointerException(); +// +// ClassUnit clazz = this; +// do { +// MemberUnit member = clazz.getDeclaredMember(signature); +// if (member != null) return member; +// clazz = clazz.getSuperUnit(); +// } while (clazz != null); +// +// return null; +// } + + public Collection getMembers(String signature) { + if (signature == null) throw new NullPointerException(); + ArrayList list = new ArrayList(); + + for (ClassUnit clazz : getSupertypes()) { + MemberUnit member = clazz.getDeclaredMember(signature); + if (member != null) list.add(member); + } + + return list; + } + + public Collection getDeclaredMembers() { + if (!isResolved) throw new RuntimeException("Class is not yet resolved: " + getName()); + return declaredMembers.values(); + } + + public void addMemberUnit(MemberUnit unit) { + declaredMembers.put(unit.getSignature().toString(), unit); + } + + public ClassUnit getSuperUnit() { + return superUnit; + } + + public void setSuperUnit(ClassUnit theSuperUnit) { + if (superUnit != null) { + superUnit.removeSubUnit(this); + } + superUnit = theSuperUnit; + if (superUnit != null) { + superUnit.addSubUnit(this); + } + } + + public void write(int depth, Writer writer) throws IOException { + if (!isTainted()) return; + + Log.getLogger().debug(getIndent(depth) + this); + + if (getData() != null) { + writer.write(getData()); + } else { + // TODO: Is it correct to return so soon? + return; + } + + if (interfaces.size() > 0) { + //Logger.getLogger().info("Class + " + this.getName() + " has interfaces: "); + + writer.write("_T.interfaces = ["); + int i = 0; + for (ClassUnit interFace : interfaces) { + //Logger.getLogger().info(">>>" + interFace.getName()); + if (i++ > 0) writer.write(", "); + writer.write(String.valueOf(interFace.getSignature().getId())); + } + writer.write("];\n"); + } + + if (annotations != null) { + writer.write("_T.annotations = "); + Serializer serializer = new Serializer(writer); + serializer.serialize(annotations); + writer.write(";\n"); + } + + for (MemberUnit member : getDeclaredMembers()) { + if (member.isTainted()) { + member.write(depth + 1, writer); + if (member instanceof ProcedureUnit) { + project.currentGeneratedMethods++; + writer.flush(); + } + } + } + + for (ClassUnit child : getSubUnits()) { + // TODO Interface: Interfaces must not extend java.lang.Object! + //if (!child.isInterface) { + child.write(depth + 1, writer); + //} + } + } + + void setSignature(Signature theSignature) { + super.setSignature(theSignature); + } + + public FileObject getClassFile() { + if (classFile == null) { + classFile = J2JSCompiler.compiler.fileManager.getFileForInput( + getSignature().toString().replaceAll("\\.", "/") + ".class"); + } + return classFile; + } + + public void setLastCompiled(long theLastCompiled) { + lastCompiled = theLastCompiled; + } + + public boolean isResolved() { + return isResolved; + } + + public void setSuperTainted() { + ClassUnit clazz = this; + do { + clazz.setTainted(); + clazz = clazz.getSuperUnit(); + } while (clazz != null); + + for (ClassUnit i : interfaces) { + i.setSuperTainted(); + } + } + + public void setResolved(boolean theIsResolved) { + isResolved = theIsResolved; + } + public String getName() { + return getSignature().className(); + } + + public Project getProject() { + return project; + } + +} diff --git a/src/main/java/com/j2js/assembly/ConstructorUnit.java b/src/main/java/com/j2js/assembly/ConstructorUnit.java new file mode 100644 index 0000000..4e4a3a5 --- /dev/null +++ b/src/main/java/com/j2js/assembly/ConstructorUnit.java @@ -0,0 +1,14 @@ +package com.j2js.assembly; + +/** + * ConstructorUnit provides information about, and access to, a single constructor for a class. + * + * @author wolle + */ +public class ConstructorUnit extends ProcedureUnit { + + public ConstructorUnit(Signature theSignature, ClassUnit theDeclaringClazz) { + super(theSignature, theDeclaringClazz); + } + +} diff --git a/src/main/java/com/j2js/assembly/FieldUnit.java b/src/main/java/com/j2js/assembly/FieldUnit.java new file mode 100644 index 0000000..5f5f297 --- /dev/null +++ b/src/main/java/com/j2js/assembly/FieldUnit.java @@ -0,0 +1,21 @@ +package com.j2js.assembly; + +import java.io.IOException; +import java.io.Writer; + +/** + * A FieldUnit provides information about, and dynamic access to, a single field of a class or an interface. + * + * @author wolle + */ +public class FieldUnit extends MemberUnit { + + public FieldUnit(Signature theSignature, ClassUnit theDeclaringClazz) { + super(theSignature, theDeclaringClazz); + } + + void write(int depth, Writer writer) throws IOException { + return; + } + +} diff --git a/src/main/java/com/j2js/assembly/JavaScriptCompressor.java b/src/main/java/com/j2js/assembly/JavaScriptCompressor.java new file mode 100644 index 0000000..fbf449e --- /dev/null +++ b/src/main/java/com/j2js/assembly/JavaScriptCompressor.java @@ -0,0 +1,69 @@ +package com.j2js.assembly; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class JavaScriptCompressor { + + private int CODE = 0; + private int LINECOMMENT = 1; + private int MULTILINECOMMENT = 2; + private int STRING = 3; + + public String compress(InputStream input) throws FileNotFoundException, IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(input)); + StringBuffer buffer = new StringBuffer(); + int state = CODE; + char c; + char lastC = 0; + + do { + int i = reader.read(); + if (i == -1) break; + c = (char) i; + + if (state == LINECOMMENT) { + if (c == '\n') { + state = CODE; + buffer.append(c); + } // else ignore character. + } else if (state == MULTILINECOMMENT) { + if (c == '/' && lastC == '*') { + state = CODE; + } // else ignore character. + } else if (state == STRING) { + if (c == '"') { + state = CODE; + } + buffer.append(c); + } else if (c == '/' && lastC == c) { + state = LINECOMMENT; + buffer.deleteCharAt(buffer.length()-1); + } else if (c == '*' && lastC == '/') { + state = MULTILINECOMMENT; + buffer.deleteCharAt(buffer.length()-1); + } else if (c == '"') { + state = STRING; + buffer.append(c); + } else { + int length = buffer.length(); + if (length > 0 && Character.isWhitespace(c) && Character.isWhitespace(buffer.charAt(length-1))) { + + } else { + buffer.append(c); + } + } + lastC = c; + } while (true); + + reader.close(); + + return buffer.toString(); + } + +} diff --git a/src/main/java/com/j2js/assembly/JunkWriter.java b/src/main/java/com/j2js/assembly/JunkWriter.java new file mode 100644 index 0000000..740d590 --- /dev/null +++ b/src/main/java/com/j2js/assembly/JunkWriter.java @@ -0,0 +1,81 @@ +package com.j2js.assembly; + +import com.j2js.J2JSCompiler; + +import java.io.File; +import java.io.FileWriter; +import java.io.FilterWriter; +import java.io.IOException; +import java.io.StringWriter; + +import com.j2js.Log; + +public class JunkWriter extends FilterWriter { + + private File assembly; + private int junkCount = 0; + private int sizeOfCurrentJunk; + private int sizeOfAllJunks = 0; + + public JunkWriter(File assembly) throws IOException { + super(new StringWriter()); + this.assembly = assembly; + startNewJunk(); + } + + private void startNewJunk() throws IOException { + sizeOfAllJunks += sizeOfCurrentJunk; + + if (junkCount > 0) { + write("j2js.loadScript(" + sizeOfAllJunks + ");"); + out.flush(); + out.close(); + } + + Log logger = Log.getLogger(); + String newJunkName = (junkCount + 1) + ".js"; + logger.info("Creating assembly " + newJunkName); + out = new FileWriter(new File(assembly, newJunkName)); + sizeOfCurrentJunk = 0; + junkCount++; + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + super.write(cbuf, off, len); + sizeOfCurrentJunk += len; + } + + @Override + public void write(int c) throws IOException { + super.write(c); + sizeOfCurrentJunk++; + } + + @Override + public void write(String str, int off, int len) throws IOException { + super.write(str, off, len); + sizeOfCurrentJunk += len; + } + + @Override + public void flush() throws IOException { + super.flush(); + if (sizeOfCurrentJunk/1024 > J2JSCompiler.compiler.getJunkSizeInKiloBytes()) { + startNewJunk(); + } + } + + @Override + public void close() throws IOException { + sizeOfAllJunks += sizeOfCurrentJunk; + // Set to 0 in case super.close() calls flush(). + sizeOfCurrentJunk = 0; + super.close(); + } + + public int getSize() { + return sizeOfAllJunks; + } + +} diff --git a/src/main/java/com/j2js/assembly/MemberUnit.java b/src/main/java/com/j2js/assembly/MemberUnit.java new file mode 100644 index 0000000..1bbe86d --- /dev/null +++ b/src/main/java/com/j2js/assembly/MemberUnit.java @@ -0,0 +1,32 @@ +package com.j2js.assembly; + +/** + * The MemberUnit class is the base class for Field, Method and Constructor objects. + * + * @author wolle + */ +public abstract class MemberUnit extends Unit { + + // The class to which this method belongs. + ClassUnit declaringClass; + + MemberUnit(Signature theSignature, ClassUnit theDeclaringClazz) { + setSignature(theSignature); + declaringClass = theDeclaringClazz; + declaringClass.addMemberUnit(this); + } + + public ClassUnit getDeclaringClass() { + return declaringClass; + } + + public Signature getAbsoluteSignature() { + Signature s = Project.getSingleton().getSignature(declaringClass.toString(), getSignature().toString()); + return s; + } + + public String toString() { + return declaringClass.getName() + "#" + super.toString(); + } + +} diff --git a/src/main/java/com/j2js/assembly/MethodUnit.java b/src/main/java/com/j2js/assembly/MethodUnit.java new file mode 100644 index 0000000..6e85ab9 --- /dev/null +++ b/src/main/java/com/j2js/assembly/MethodUnit.java @@ -0,0 +1,14 @@ +package com.j2js.assembly; + +/** + * A MethodUnit provides information about, and access to, a single method on a class or interface. + * + * @author wolle + */ +public class MethodUnit extends ProcedureUnit { + + public MethodUnit(Signature theSignature, ClassUnit theDeclaringClazz) { + super(theSignature, theDeclaringClazz); + } + +} diff --git a/src/main/java/com/j2js/assembly/ProcedureUnit.java b/src/main/java/com/j2js/assembly/ProcedureUnit.java new file mode 100644 index 0000000..c3b01a5 --- /dev/null +++ b/src/main/java/com/j2js/assembly/ProcedureUnit.java @@ -0,0 +1,51 @@ +package com.j2js.assembly; + +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +import com.j2js.Log; + +public abstract class ProcedureUnit extends MemberUnit { + + // Set of all member signatures targeted by this method. + private Collection targetSignatures = new HashSet(); + + public ProcedureUnit(Signature theSignature, ClassUnit theDeclaringClazz) { + super(theSignature, theDeclaringClazz); + } + + public void addTarget(Signature targetSignature) { + if (!targetSignature.toString().contains("#")) { + throw new IllegalArgumentException("Signature must be field or method: " + targetSignature); + } + //Logger.getLogger().info("Adding " + this + " -> " + targetSignature); + targetSignatures.add(targetSignature); + } + + public void removeTargets() { + Iterator iter = targetSignatures.iterator(); + while (iter.hasNext()) { + iter.next(); + iter.remove(); + } + } + + void write(int depth, Writer writer) throws IOException { + if (getData() == null) return; + Log.getLogger().debug(getIndent(depth) + getSignature()); + writer.write(getData()); + } + + public String getData() { + if (!declaringClass.isResolved()) throw new RuntimeException("Class must be resolved"); + return super.getData(); + } + + public Collection getTargetSignatures() { + return targetSignatures; + } + +} diff --git a/src/main/java/com/j2js/assembly/Project.java b/src/main/java/com/j2js/assembly/Project.java new file mode 100644 index 0000000..2b1a07e --- /dev/null +++ b/src/main/java/com/j2js/assembly/Project.java @@ -0,0 +1,330 @@ +package com.j2js.assembly; + +import com.j2js.J2JSCompiler; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import org.apache.bcel.generic.ArrayType; +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.ReferenceType; +import org.apache.bcel.generic.Type; + +import com.j2js.Log; +import com.j2js.Utils; +import com.j2js.dom.ArrayCreation; +import com.j2js.dom.FieldAccess; +import com.j2js.dom.MethodBinding; +import com.j2js.dom.MethodDeclaration; +import com.j2js.dom.MethodInvocation; +import com.j2js.dom.TypeDeclaration; + +public class Project implements Serializable { + + static final long serialVersionUID = 0; + + private static Project singleton; + + // All managed classes mapped by class name. + private Map classesByName; + private ClassUnit javaLangObject; + private boolean compressed; + private boolean generateLineNumbers; + private Map signatures; + + private transient Stack ids; + private transient int currentId; + private transient int currentIndex; + + public transient int currentGeneratedMethods; + + public static Project getSingleton() { + if (singleton == null) throw new NullPointerException(); + return singleton; + } + + public static Project createSingleton(File cacheFile) { + + if (cacheFile != null && cacheFile.exists()) { + Log.getLogger().info("Using cache " + cacheFile); + try { + read(cacheFile); + } catch (Exception e) { + Log.getLogger().warn("Could not read cache:\n" + e.getMessage()); + } + } + + if (singleton == null || + singleton.compressed != J2JSCompiler.compiler.isCompression() || + singleton.generateLineNumbers != J2JSCompiler.compiler.isGenerateLineNumbers()) { + // Cache does not exist, could not be read, or compression does not match. + singleton = new Project(); + singleton.clear(); + } + + return singleton; + } + + private static void read(File file) throws Exception { + FileInputStream fis = new FileInputStream(file); + ObjectInputStream ois = new ObjectInputStream(fis); + singleton = (Project) ois.readObject(); + ois.close(); + } + + public static void write() throws IOException { + File file = J2JSCompiler.compiler.getCacheFile(); + if (file.exists() && !file.canWrite()) { + throw new IOException("Cannot write " + file); + } + FileOutputStream fos = new FileOutputStream(file); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(singleton); + oos.close(); + } + + public Signature getArraySignature(Type type) { + String signatureString = type.getSignature(); + /* Examples: + * L[java.lang.Integer;; -> [java.lang.Integer; + * L[I; -> [I + */ + if (!signatureString.startsWith("L") || !signatureString.endsWith(";")) { + throw new RuntimeException("Not a class signature: " + signatureString); + } + signatureString = signatureString.substring(1, signatureString.length()-1); + return getSignature(signatureString); + } + + /** + * All request for a signature delegate to this method. + */ + public Signature getSignature(String signatureString) { + if (signatureString.endsWith(";")) { + //throw new RuntimeException("Invalid signature: " + signatureString); + } + signatureString = signatureString.replaceAll("/", "."); + + Signature signature = signatures.get(signatureString); + if (signature == null) { + signature = new Signature(signatureString, getUniqueId()); + signatures.put(signatureString, signature); + } + + return signature; + } + + public Signature getSignature(String className, String relativeSignature) { + return getSignature(className + '#' + relativeSignature); + } + + public Signature getSignature(FieldAccess fa) { + return getSignature(fa.getType().getClassName(), fa.getName()); + } + + private int getUniqueId() { + if (ids == null) { + ids = new Stack(); + for (Signature signature : signatures.values()) { + ids.add(signature.getId()); + } + Collections.sort(ids); + } + + while (currentIndex(); + javaLangObject = null; + + signatures = new HashMap(); + ids = null; + currentId = 0; + currentIndex = 0; + compressed = J2JSCompiler.compiler.isCompression(); + generateLineNumbers = J2JSCompiler.compiler.isGenerateLineNumbers(); + } + + public void remove(ClassUnit clazz) { + classesByName.remove(clazz); + } + + void visitSuperTypes(ClassUnit clazz, TypeVisitor visitor) { + visitor.visit(clazz); + ClassUnit superClass = clazz.getSuperUnit(); + if (superClass != null) { + visitSuperTypes(superClass, visitor); + } + + for (ClassUnit interfaceUnit : clazz.getInterfaces()) { + visitor.visit(interfaceUnit); + visitSuperTypes(interfaceUnit, visitor); + } + } + + public ClassUnit getJavaLangObject() { + return javaLangObject; + } + + public ClassUnit getClassUnit(String className) { + ClassUnit clazz = classesByName.get(className); + if (clazz != null) return clazz; + + throw new RuntimeException("No such unit: " + className); + } + + public ClassUnit getClassUnit(ReferenceType type) { + String signature; + if (type instanceof ArrayType) { + ArrayType aType = (ArrayType) type; + signature = Utils.getSignature(aType.getBasicType()); + for (int i = 0; i getClasses() { + return classesByName.values(); + } + + public void resolve(ClassUnit clazz) { + if (clazz.isResolved()) return; + + if (clazz.getName().startsWith("[")) { + // This is an array type and not a class. + clazz.setSuperUnit(getJavaLangObject()); + //clazz.setTainted(); + clazz.setResolved(true); + // We need a member (and we chose length) in addReference(..) to taint the class. + new FieldUnit(getSignature("length"), clazz); + + TypeDeclaration typeDecl = new TypeDeclaration(new ObjectType(clazz.getName()), 0); + typeDecl.setSuperType(Type.OBJECT); + typeDecl.visit(J2JSCompiler.compiler.generator); + } else { + TypeResolver resolver = new TypeResolver(this, J2JSCompiler.compiler.generator); + visitSuperTypes(clazz, resolver); + } + } + +} diff --git a/src/main/java/com/j2js/assembly/Serializer.java b/src/main/java/com/j2js/assembly/Serializer.java new file mode 100644 index 0000000..2611e8c --- /dev/null +++ b/src/main/java/com/j2js/assembly/Serializer.java @@ -0,0 +1,153 @@ +package com.j2js.assembly; + +import java.util.List; +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; + +public class Serializer { + private Writer writer; + private SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy"); + + /** Creates a new instance of Serializer */ + public Serializer(Writer theWriter) { + writer = theWriter; + } + + public void serialize(Object object) throws NotSerializableException { + try { + this.serializeInternal(object); + } catch (IOException e) { + throw new NotSerializableException(e.getMessage()); + } + } + + private void serializeInternal(Object object) throws IOException { + if (object==null) { + writer.write("null"); + } else if (object instanceof String) { + quote((String)object); + } else if (object instanceof Number) { + numberToString((Number) object); + } else if (object instanceof Map) { + serializeMap((Map) object); + } else if (object instanceof List) { + serializeArray((List) object); + } else if (object.getClass().isArray()) { + serializeArray((Object[]) object);// && object.getClass().getComponentType().equals(Double.TYPE) double[]) object); + } else if (object instanceof Boolean) { + writer.write(((Boolean)object).toString()); + } else if (object instanceof Date) { + quote(dateFormatter.format((Date) object)); + } else { + throw serializeError("Unknown object type " + object.getClass().toString()); + } + } + + private void serializeArray(List list) throws IOException { + String sep = ""; + + writer.write('['); + for (Object o : list) { + writer.write(sep); + sep = ","; + this.serializeInternal(o); + } + writer.write(']'); + } + + private void serializeArray(Object[] array) throws IOException { + String sep = ""; + + writer.write('['); + for (int i=0; i map) throws IOException { + String sep = ""; + + writer.write('{'); + for (String key : map.keySet()) { + writer.write(sep); + sep = ","; + Object value = map.get(key); + if (key.matches("\\w*")) { + writer.write(key); + } else { + writer.write("\"" + key + "\""); + } + + writer.write(':'); + this.serializeInternal(value); + } + writer.write('}'); + } + + /** + * Stream a numeric value. + * @exception NotSerializableException If number is infinite or not a number. + * @param n A Number + * @return A String. + */ + private void numberToString(Number n) throws IOException { + if (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN())) { + throw serializeError("Can only serialize finite numbers"); + } + if (n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) { + throw serializeError("Can only serialize finite numbers"); + } + + writer.write(n.toString().toLowerCase()); + } + + private void numberToString(double n) throws IOException { + writer.write(String.valueOf(n)); + } + + /** + * Stream a string in double quotes " with the following backslash replacements: + * " -> \" + * \ -> \\ + * @param string A String + */ + private void quote(String s) throws IOException { + if (s == null) { + writer.write("null"); + return; + } + writer.write('"'); + int l = s.length(); + for (int i=0; i java.lang.Integer + * L[I; -> [I + */ +// public String getClassName() { +// if (!signatureString.startsWith("L") || !signatureString.endsWith(";")) { +// throw new RuntimeException("Not a class signature: " + signatureString); +// } +// return signatureString.substring(1, signatureString.length()-1); +// } + + public int hashCode() { + return signatureString.hashCode(); + } + + public boolean equals(Object obj) { + if (obj instanceof Signature) { + return signatureString.equals(((Signature) obj).signatureString); + } + return false; + } + + public String toString() { + return signatureString; + } + + public boolean isClass() { + return signatureString.indexOf('#') == -1; + } + + public boolean isArrayType() { + return isClass() && signatureString.startsWith("["); + } + + public boolean isConstructor() { + return signatureString.startsWith(""); + } + + public boolean isMethod() { + return !isConstructor() && signatureString.indexOf('(') != -1; + } + + public boolean isField() { + return !isClass() && signatureString.indexOf('(') == -1; + } + + public String className() { + String array[] = signatureString.split("#"); + //if (array[0].startsWith("[")) array[0] = array[0].substring(1); + return array[0]; + } + + public String relativeSignature() { + String array[] = signatureString.split("#"); + if (array.length != 2) { + throw new RuntimeException("Not a method signature: " + this); + } + return array[1]; + } + + /** + * Returns the relative signature. + */ + public Signature relative() { + return Project.getSingleton().getSignature(relativeSignature()); + } + + public int getId() { + return id; + } + + public String getCommentedId() { + StringBuffer sb = new StringBuffer(); + sb.append(String.valueOf(getId())); + if (!J2JSCompiler.compiler.isCompression()) { + sb.append(" /*"); + sb.append(toString()); + sb.append("*/"); + } + return sb.toString(); + } +} diff --git a/src/main/java/com/j2js/assembly/TypeCollector.java b/src/main/java/com/j2js/assembly/TypeCollector.java new file mode 100644 index 0000000..0b61561 --- /dev/null +++ b/src/main/java/com/j2js/assembly/TypeCollector.java @@ -0,0 +1,13 @@ +package com.j2js.assembly; + +import java.util.Collection; +import java.util.HashSet; + +public class TypeCollector implements TypeVisitor { + + Collection collectedTypes = new HashSet(); + + public void visit(ClassUnit clazz) { + collectedTypes.add(clazz); + } +} diff --git a/src/main/java/com/j2js/assembly/TypeResolver.java b/src/main/java/com/j2js/assembly/TypeResolver.java new file mode 100644 index 0000000..95cdf59 --- /dev/null +++ b/src/main/java/com/j2js/assembly/TypeResolver.java @@ -0,0 +1,77 @@ +package com.j2js.assembly; + +import com.j2js.J2JSCompiler; + +import com.j2js.Log; +import com.j2js.Parser; +import com.j2js.dom.TypeDeclaration; +import com.j2js.visitors.JavaScriptGenerator; + +public class TypeResolver implements TypeVisitor { + + private JavaScriptGenerator generator; + private Project project; + + public TypeResolver(Project theProject, JavaScriptGenerator theGenerator) { + project = theProject; + generator = theGenerator; + } + + public void visit(ClassUnit clazz) { + if (clazz.isResolved()) return; + + Log logger = Log.getLogger(); + + if (clazz.getSignature().toString().startsWith("[")) { + // Class is an array class without class file: Do nothing. + } else if (!clazz.isUpToDate()) { + clazz.clear(); + try { + compile(clazz); + J2JSCompiler.compiler.compileCount++; + } catch (RuntimeException ex) { + J2JSCompiler.errorCount++; + logger.error(ex.toString()); + //ex.printStackTrace(); + if (J2JSCompiler.compiler.failOnError) { + throw ex; + } + } + } else { + logger.debug("Up to date: " + clazz); + } + + clazz.setResolved(true); + } + + /** + * Compiles the unit. + */ + private void compile(ClassUnit classUnit) { + + if (classUnit.getClassFile() == null) { + Log.getLogger().warn("Cannot read " + classUnit.getClassFile()); + return; + } + + Log.getLogger().info("Cross-Compiling " + classUnit); + + TypeDeclaration typeDecl = parse(classUnit); + + // TODO + //if (!Modifier.isInterface(typeDecl.getAccess())) { + typeDecl.visit(generator); + //} + + // Set not current date but date of last modification. This is + // independent of system clock. + classUnit.setLastCompiled(classUnit.getLastModified()); + } + + private TypeDeclaration parse(ClassUnit classUnit) { + Parser parser = new Parser(classUnit); + TypeDeclaration typeDecl = parser.parse(); + + return typeDecl; + } +} diff --git a/src/main/java/com/j2js/assembly/TypeVisitor.java b/src/main/java/com/j2js/assembly/TypeVisitor.java new file mode 100644 index 0000000..f7b7865 --- /dev/null +++ b/src/main/java/com/j2js/assembly/TypeVisitor.java @@ -0,0 +1,5 @@ +package com.j2js.assembly; + +public interface TypeVisitor { + public void visit(ClassUnit clazz); +} diff --git a/src/main/java/com/j2js/assembly/Unit.java b/src/main/java/com/j2js/assembly/Unit.java new file mode 100644 index 0000000..7d406c1 --- /dev/null +++ b/src/main/java/com/j2js/assembly/Unit.java @@ -0,0 +1,62 @@ +package com.j2js.assembly; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; + +import com.j2js.Log; + + +public abstract class Unit implements Serializable { + + private Signature signature; + + private String data; + + private transient boolean isTainted = false; + private static transient String[] indentPerDepth = new String[10]; + + public Unit() { + } + + abstract void write(int depth, Writer writer) throws IOException; + + String getIndent(int depth) { + String indent = indentPerDepth[depth]; + if (indent == null) { + indent = ""; + for (int i=0; i uaArgs = new ArrayList(); + + @XmlElement(name = "devModeWebSocketURL", required = true) + public URI devModeWebSocketURL; + + @XmlElement(name = "devModeHttpURL", required = true) + public URI devModeHttpURL; + + public void write(OutputStream os) { + try { + JAXBContext context = JAXBContext.newInstance(Configuration.class); + Marshaller m = context.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + m.marshal(this, os); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Configuration read() { + File file = new File(System.getProperty("user.home") + "/.m2/j2js.xml"); + InputStream is = null; + try { + is = new FileInputStream(file); + } catch (FileNotFoundException e) { + throw new RuntimeException("Could not read file " + file); + } + System.out.println("Reading j2js configuration from " + file); + return Configuration.read(is); + } + + public static Configuration read(InputStream inStream) { + JAXBContext context; + try { + context = JAXBContext.newInstance(Configuration.class); + Unmarshaller um = context.createUnmarshaller(); + return (Configuration) um.unmarshal(inStream); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +// public ApplicationConfiguration getApplicationConfiguration(String applicationName) { +// if ( applicationName == null ) { +// throw new NullPointerException("applicationName cannot be null."); +// } +// +// for (ApplicationConfiguration applConfig : applications) { +// if ( applicationName.equals(applConfig.applicationName) ) { +// return applConfig; +// } +// } +// +// return null; +// } +} diff --git a/src/main/java/com/j2js/cfg/ConditionalEdge.java b/src/main/java/com/j2js/cfg/ConditionalEdge.java new file mode 100644 index 0000000..87a2800 --- /dev/null +++ b/src/main/java/com/j2js/cfg/ConditionalEdge.java @@ -0,0 +1,29 @@ +package com.j2js.cfg; + +import com.j2js.dom.BooleanExpression; + +public class ConditionalEdge extends Edge { + + private BooleanExpression expression; + private boolean negate = false; + + ConditionalEdge(Graph graph, Node theSource, Node theTarget) { + super(graph, theSource, theTarget); + } + + public BooleanExpression getBooleanExpression() { + return expression; + } + + public void setBooleanExpression(BooleanExpression expr) { + expression = expr; + } + + public boolean isNegate() { + return negate; + } + + public void setNegate(boolean theNegate) { + negate = theNegate; + } +} diff --git a/src/main/java/com/j2js/cfg/ControlFlowGraph.java b/src/main/java/com/j2js/cfg/ControlFlowGraph.java new file mode 100644 index 0000000..a9fee23 --- /dev/null +++ b/src/main/java/com/j2js/cfg/ControlFlowGraph.java @@ -0,0 +1,387 @@ +package com.j2js.cfg; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; + +import com.j2js.ASTNodeStack; +import com.j2js.cfg.transformation.Transformation; +import com.j2js.dom.ASTNode; +import com.j2js.dom.Block; +import com.j2js.dom.BooleanExpression; +import com.j2js.dom.InfixExpression; +import com.j2js.dom.TryStatement; + +/** + * Instances of this class represent a Control Flow Graph CFG. + */ +public class ControlFlowGraph extends Graph { + + // Ordering is only used by method getNodeAt(). + private SortedMap nodesByPc = new TreeMap(); + + // The single entry point of control. + private Node sourceNode; + + private List tryStatements; + + public ControlFlowGraph(List theTryStatements) { + tryStatements = theTryStatements; + } + + /** + * Returns the try statement which contains the specified node. + */ + private TryStatement selectTryStatement(Node node) { + int pc = node.getInitialPc(); + for (int i=0; i= block.getBeginIndex() && pc <= block.getEndIndex()) return tryStmt; + } + return null; + } + + + public Node createNode(int pc) { + return createNode(pc, Node.class); + } + + public Node getOrCreateNode(int pc) { + Node node = getNode(pc); + if (node == null) { + node = createNode(pc, Node.class); + } + return node; + } + + public Node createNode(int pc, Class nodeClass) { + if (pc < 0) throw new RuntimeException("Program counter may not be negative"); + + Node node = super.createNode(nodeClass); + node.setInitialPc(pc); + if (nodesByPc.put(node.getInitialPc(), node) != null) { + throw new RuntimeException("Node already exists: " + node); + } + + if (pc == 0) sourceNode = node; + + return node; + } + + /** + * Returns the largest (w.r. to its initial pc) node closest to the specified pc. + */ + public Node getNodeAt(int pc) { + int minPcDelta = Integer.MAX_VALUE; + Node node = null; + for (Node n : getNodes()) { + if (n.getInitialPc() <= pc && pc-n.getInitialPc() < minPcDelta) { + minPcDelta = pc-n.getInitialPc(); + node = n; + if (minPcDelta == 0) return node; + } + } + + if (node == null) { + throw new RuntimeException("No node at pc " + pc); + } + + return node; + } + + /** + * Splits a node at the given pc value into two nodes nodeA and nodeB. + * nodeB will start at pc and will have the outbound edges of the original node, and an + * extra edge is added from nodeA to nodeB. + */ + public Node split(Node node, int pc) { + if (node.block.getBeginIndex() >= pc) throw new RuntimeException("Block must contain program counter"); + + Node nodeB = createNode(pc); + + // Reroot all outbound edges at nodeB. + for (Edge edge : new ArrayList(node.getOutEdges())) { + edge.reroot(nodeB); + } + + addEdge(node, nodeB); + + // Transfer code starting at pc to nodeB. + ASTNode astNode = node.block.getFirstChild(); + while (astNode != null) { + if (astNode.getBeginIndex() >= pc) { + node.setCurrentPc(astNode.getBeginIndex()-1); + nodeB.block.appendChildren(astNode, node.block.getLastChild()); + break; + } + astNode = astNode.getNextSibling(); + } + + // Transfer stack to nodeB. + nodeB.stack = node.stack; + node.stack = new ASTNodeStack(); + + return nodeB; + } + + public Node[] getOrSplitNodeAt(Node currentNode, int pc) { + Node targetNode = getNodeAt(pc); + if (targetNode.getInitialPc() != pc) { + // No node starts at target pc. We have to split the node. + Node nodeB = split(targetNode, pc); + if (targetNode == currentNode) { + currentNode = nodeB; + } + targetNode = nodeB; + } + return new Node[]{currentNode, targetNode}; + } + + /** + * Redirect all incoming edges for try bodys to its try header. + */ + private void processTrys() { + for (int i=0; i(tryNode.getInEdges())) { + int pc = edge.source.getInitialPc(); + if (pc >= stmt.getBeginIndex() && pc <= stmt.getEndIndex()) continue; + if (edge.source == header) continue; + edge.redirect(header); + } + } + } + + /** + * Reroots all local edges exiting a try body at the corresponding try header. + */ + private void processTrys2() { + for (Node node: nodesByPc.values()) { + TryStatement sourceTry = selectTryStatement(node); + if (sourceTry == null) continue; + + for (Edge edge : node.getOutEdges()) { + if (edge.target.getInEdges().size() != 1) { + continue; + } + TryStatement targetTry = selectTryStatement(edge.target); + if (targetTry == null || targetTry != sourceTry) { + edge.reroot(sourceTry.header); + } + } + } + } + + /** + * Reroot the fall through successor directly at the try header. + */ + private void processTrys1() { + for (int i=0; i finallyNode.getInitialPc()) { + removeInEdges(node); + addEdge(header, node); + node.setDomParent(header); + } + } + } + } + + public Node getSource() { + return sourceNode; + } + + public Node getNode(int pc) { + return nodesByPc.get(pc); + } + + /** + * Finds the set of all successors of the specified predecessor which are not dominated by it. + * Each node in this set is then marked as a global target of the predecessor. + * If the predecessor is a branch, then a newly created node will function as the referer. + */ + private void markGlobalTargets(Node predecessor) { + for (Edge edge : predecessor.getOutEdges()) { + Node target = edge.target; + if (target.getDomParent() == predecessor) + continue; + + if (predecessor.isBranch()) { + Node node = createNode(Node.class); + edge.redirect(node); + node.setDomParent(predecessor); + edge = addEdge(node, target); + } + } + } + + /** + * For each node in tree, mark global targets. + */ + void visitToMark(Node node) { + // Be concurrent safe. + for (Node child : new ArrayList(node.getDomChildren())) { + visitToMark(child); + } + + markGlobalTargets(node); + } + + /** + * Recursively (depth first) traverses the dominator tree rooted at the specified node and post-processes + * all possible reductions. + */ + void visit(Node node) { + + // Be concurrent safe. + for (Node child : new ArrayList(node.getDomChildren())) { + visit(child); + } + + do { + Transformation t = Transformation.select(this, node); + if (t == null) break; + node = t.apply(); + dump("After transformation"); + } while (true); + + if (node.getDomChildren().size() > 0) { + throw new RuntimeException("Could not reduce graph at " + node); + } + } + + public void replaceNode(Node node, Node newNode) { + + super.replaceNode(node, newNode); + + if (newNode != null) { + nodesByPc.put(node.getInitialPc(), newNode); + } else { + nodesByPc.remove(node.getInitialPc()); + } + + if (node == sourceNode) { + if (newNode == null) { + throw new RuntimeException("Cannot remove source node " + sourceNode); + } + sourceNode = newNode; + } + } + + public Block reduce() { + processTrys(); + processTrys2(); + + dump("Before Shortcuts"); + processShortcuts(); + + DominatorTree builder = new DominatorTree(this); + builder.build(); + + processTrys1(); + + visitToMark(getSource()); + dump("Begin reduce"); + + visit(getSource()); + + if (size() != 1) { + throw new RuntimeException("Could not reduce graph"); + } + + Block block = new Block(); + + rollOut(getSource(), block); + + return block; + } + + /** + * Performs an OR or AND shortcut on two branches A and B by replacing them with new node A&B or A|B. + */ + boolean performAndOrShortcut(Node a, Node b) { + if (b.getInEdges().size() != 1) return false; + if (b.block.getChildCount() > 0) { + // Node b is not a mere conditional. + return false; + } + + + ConditionalEdge aToC; + ConditionalEdge bToC; + boolean isOR = true; + + while(true) { + aToC = a.getConditionalEdge(isOR); + bToC = b.getConditionalEdge(isOR); + if (bToC.target == aToC.target) break; + if (!isOR) return false; + isOR = false; + } + + if (aToC.target.getInEdges().size() != 2) return false; + + ConditionalEdge bToD = b.getConditionalEdge(!isOR); + ConditionalEdge aToB = a.getConditionalEdge(!isOR); + + aToB.redirect(bToD.target); + removeEdge(bToC); + removeEdge(bToD); + removeNode(b); + + InfixExpression infix = new InfixExpression( + isOR?InfixExpression.Operator.CONDITIONAL_OR:InfixExpression.Operator.CONDITIONAL_AND); + // Note that the order aToC, then bToC is important. + infix.setOperands( + aToC.getBooleanExpression().getExpression(), + bToC.getBooleanExpression().getExpression()); + + BooleanExpression be = new BooleanExpression(infix); + aToC.setBooleanExpression(be); + aToB.setBooleanExpression(be); + + logger.debug("Created shortcut and removed " + b); + + return true; + } + + public void processShortcuts() { + Collection branches = new HashSet(); + for (Node node : getNodes()) { + if (node.isBranch()) branches.add(node); + } + + L: while (true) { + Iterator iter = branches.iterator(); + while (iter.hasNext()) { + Node branch = iter.next(); + for (Node node: branch.succs()) { + if (node.isBranch() && performAndOrShortcut(branch, node)) { + branches.remove(node); + continue L; + } + } + } + break L; + } + + } +} diff --git a/src/main/java/com/j2js/cfg/DominatorTree.java b/src/main/java/com/j2js/cfg/DominatorTree.java new file mode 100644 index 0000000..c1e96d1 --- /dev/null +++ b/src/main/java/com/j2js/cfg/DominatorTree.java @@ -0,0 +1,233 @@ +package com.j2js.cfg; + +import java.util.*; + +import com.j2js.Log; + +/** + * Class to build the dominator tree of a given control flow graph. + * The algorithm is according Purdum-Moore, which isn't as fast as Lengauer-Tarjan, but a lot simpler. + */ +public class DominatorTree { + + private ControlFlowGraph graph; + + public DominatorTree(ControlFlowGraph theGraph) { + graph = theGraph; + } + + /** + * Sets the pre-order index of a node. + */ + private void visit(Node node, Collection visited) { + // Establish preorder index. + node.setPreOrderIndex(visited.size()); + visited.add(node); + + for (Node succ : node.succs()) { + if (! visited.contains(succ)) { + visit(succ, visited); + } + } + + } + + /** + * Builds the dominator tree and store it in the respective nodes. + * It will remove all unreachable nodes on the way! + */ + public void build() { + + // Construct list of nodes in pre-order order. + ArrayList preOrder = new ArrayList(); + + visit(graph.getSource(), preOrder); + + // Remove unreachable nodes. + for (Node node : new ArrayList(graph.getNodes())) { + if (!preOrder.contains(node)) { + Log.getLogger().warn("Unreachable code detected and removed"); + //Logger.getLogger().info("Removed " + node); + graph.removeInEdges(node); + graph.removeOutEdges(node); + graph.removeNode(node); + } else if (node.getPreOrderIndex() == -1) { + throw new RuntimeException("Pre-order not set for " + node); + } + } + + + int size = graph.size(); // The number of vertices in the cfg + + Map snkPreds = new HashMap(); // The predacessor vertices from the sink + + + // Determine the predacessors of the cfg's sink node + //insertEdgesToSink(graph, snkPreds, reverse); + + // Get the index of the root + int rootIndex = graph.getSource().getPreOrderIndex(); + + if (rootIndex < 0 || rootIndex >= size) throw new RuntimeException("Root index out of range"); + + // Bit matrix indicating the dominators of each node. + // If bit j of dom[i] is set, then node j dominates node i. + BitSet[] domMatrix = new BitSet[size]; + + // Initially, all the bits in the dominance matrix are set, except + // for the root node. The root node is initialized to have itself + // as an immediate dominator. + for (int i = 0; i < size; i++) { + BitSet domVector = new BitSet(size); + + if (i == rootIndex) { + // Only root dominates root. + domVector.set(rootIndex); + } + else { + // Assume that all nodes dominate non-root node i. + domVector.set(0, size); + } + + domMatrix[i] = domVector; + } + + // Did the dominator bit vector array change? + boolean changed; + + do { + changed = false; + + // Fetch all nodes in pre-order. + Iterator nodes = preOrder.iterator(); + + // Compute the dominators of each node in the cfg. We iterate + // over every node in the cfg. The dominators of a node N are + // found by taking the intersection of the dominator bit vectors + // of each predacessor of N and unioning that with N. This + // process is repeated until no changes are made to any dominator + // bit vector. + + while (nodes.hasNext()) { + Node node = (Node) nodes.next(); + + int i = node.getPreOrderIndex(); + + if (i < 0 || i >= size) throw new RuntimeException("Unreachable node " + node); + + // We already know the dominators of the root, keep looking + if (i == rootIndex) { + continue; + } + + BitSet oldSet = domMatrix[i]; + + // domVector := intersection of dom(pred) for all pred(node). + BitSet domVector = new BitSet(size); + domVector.or(oldSet); + + + Collection preds = node.preds(); + + Iterator e = preds.iterator(); + + // Find the intersection of the dominators of node's + // predacessors. + while (e.hasNext()) { + Node pred = (Node) e.next(); + + int j = pred.getPreOrderIndex(); + if (j == -1) throw new RuntimeException("Unreachable node " + pred); + + domVector.and(domMatrix[j]); + } + + // Don't forget to account for the sink node if node is a + // leaf node. Appearantly, there are not edges between + // leaf nodes and the sink node! + preds = (Collection) snkPreds.get(node); + + if (preds != null) { + e = preds.iterator(); + + while (e.hasNext()) { + Node pred = (Node) e.next(); + + int j = pred.getPreOrderIndex(); + + domVector.and(domMatrix[j]); + } + } + + // Include yourself in your dominators?! + domVector.set(i); + + // If the set changed, set the changed bit. + if (!domVector.equals(oldSet)) { + changed = true; + domMatrix[i] = domVector; + } + } + } while (changed); + + // Once we have the predacessor bit vectors all squared away, we can + // determine which vertices dominate which vertices. + + // Initialize each node's (post)dominator parent and children + for (Node node : graph.getNodes()) { + node.setDomParent(null); + node.getDomChildren().clear(); + } + + // A node's immediate dominator is its closest dominator. So, we + // start with the dominators, dom(b), of a node, b. To find the + // imediate dominator of b, we remove all nodes from dom(b) that + // dominate any node in dom(b). + + for (Node node : graph.getNodes()) { + int i = node.getPreOrderIndex(); + + if (i < 0 || i >= size) throw new RuntimeException("Unreachable node " + node); + + if (i == rootIndex) { + continue; + } + + // Find the immediate dominator + // idom := dom(node) - dom(dom(node)) - node + BitSet domVector = domMatrix[i]; + + BitSet idom = new BitSet(size); + idom.or(domVector); + idom.clear(i); + + for (int j = 0; j < size; j++) { + if (i != j && domVector.get(j)) { + // idom = idom - (domMatrix[j] - {j}) + BitSet b = new BitSet(size); + // Complement of domMatrix[j]. + b.or(domMatrix[j]); b.flip(0, size); + b.set(j); + idom.and(b); + } + } + + Node parent = null; + + // A node should only have one immediate dominator. + for (int j = 0; j < size; j++) { + if (idom.get(j)) { + Node p = preOrder.get(j); + if (parent != null) + throw new RuntimeException(node + " has more than one immediate dominator: " + parent + " and " + p); + parent = p; + } + } + + if (parent == null) + throw new RuntimeException(node + " has 0 immediate dominators"); + + node.setDomParent(parent); + } + } +} diff --git a/src/main/java/com/j2js/cfg/Edge.java b/src/main/java/com/j2js/cfg/Edge.java new file mode 100644 index 0000000..6c961c3 --- /dev/null +++ b/src/main/java/com/j2js/cfg/Edge.java @@ -0,0 +1,78 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 12.11.2005 + */ +package com.j2js.cfg; + +/** + * @author wolfgang + */ +public class Edge { + + private Graph graph; + public Node source; + public Node target; + public EdgeType type; + private Node orgSource; + + Edge(Graph theGraph, Node theSource, Node theTarget) { + graph = theGraph; + source = theSource; + target = theTarget; + } + + public boolean equals(Object other) { + if (other == null || !(other instanceof Edge)) return false; + Edge otherEdge = (Edge) other; + return source.getId().equals(otherEdge.source.getId()) && target.getId().equals(otherEdge.target.getId()); + } + + /** + * Replace the target of this edge. + */ + public void reroot(Node newSource) { + source.outEdges.remove(this); + newSource.outEdges.add(this); + source = newSource; + } + + /** + * Replace the target of this edge. + */ + public void redirect(Node newTarget) { + target.inEdges.remove(this); + newTarget.inEdges.add(this); + target = newTarget; + } + + /** + * Returns true if this edge is a backward (i.e. loop) edge. + */ + public boolean isBackEdge() { + return target.isDomAncestor(source); + } + + public boolean isGlobal() { + return orgSource != null; + } + + public String toString() { + String s = getClass().getName(); + // Extract unqualified class name. + s = s.substring(s.lastIndexOf('.')+1); + return s + " " + source.getId() + " -> " + target.getId(); + } + + public Node getOrgSource() { + if (orgSource == null) { + return source; + } + return orgSource; + } + + public void setOrgSource(Node theOrgSource) { + orgSource = theOrgSource; + } + + +} diff --git a/src/main/java/com/j2js/cfg/EdgeCollections.java b/src/main/java/com/j2js/cfg/EdgeCollections.java new file mode 100644 index 0000000..3849ff8 --- /dev/null +++ b/src/main/java/com/j2js/cfg/EdgeCollections.java @@ -0,0 +1,17 @@ +package com.j2js.cfg; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class EdgeCollections { + + public static Set getSources(Collection edges) { + HashSet sources = new HashSet(); + for (Edge edge : edges) { + sources.add(edge.source); + } + return sources; + } + +} diff --git a/src/main/java/com/j2js/cfg/EdgeType.java b/src/main/java/com/j2js/cfg/EdgeType.java new file mode 100644 index 0000000..99a7e9c --- /dev/null +++ b/src/main/java/com/j2js/cfg/EdgeType.java @@ -0,0 +1,17 @@ +package com.j2js.cfg; + +public class EdgeType { + public static EdgeType FINALLY = new EdgeType("Finally"); + public static EdgeType CATCH = new EdgeType("Catch"); + public static EdgeType TRYBODY = new EdgeType("TryBody"); + + private String name; + + private EdgeType(String theName) { + name = theName; + } + + public String toString() { + return name; + } +} diff --git a/src/main/java/com/j2js/cfg/Graph.java b/src/main/java/com/j2js/cfg/Graph.java new file mode 100644 index 0000000..e482505 --- /dev/null +++ b/src/main/java/com/j2js/cfg/Graph.java @@ -0,0 +1,286 @@ +package com.j2js.cfg; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.j2js.Log; +import com.j2js.cfg.Node; + +import com.j2js.dom.Block; +import com.j2js.dom.BooleanExpression; +import com.j2js.dom.BreakStatement; +import com.j2js.dom.IfStatement; + +public abstract class Graph { + + private Map nodes = new HashMap(); + + int nodeIdSequence = 0; + + Log logger = Log.getLogger(); + + public Graph() { + } + + public Node getNodeById(String id) { + return nodes.get(id); + } + + public Node createNode(Class nodeClass) { + return createNode(nodeClass, Integer.toString(nodeIdSequence++, 26)); + } + + public Node createNode(Class nodeClass, String id) { + Node node; + try { + Constructor constructor = nodeClass.getConstructor(new Class[]{Graph.class}); + node = (Node) constructor.newInstance(new Object[] {this}); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + node.id = id; + nodes.put(id, node); + return node; + } + + /** + * Returns true if node set A is contained in node set B. + */ + public boolean isContained(Set nodesA, Node[] nodesB) { + Set foo = new HashSet(Arrays.asList(nodesB)); + return foo.containsAll(nodesA); + } + + /** + * Redirects all inbound edges of a node to another node. + */ + public void replaceAsTarget(Node oldTarget, Node newTarget) { + for (Edge edge : new ArrayList(oldTarget.getInEdges())) { + edge.redirect(newTarget); + } + } + + public Edge removeEdge(Node source, Node target) { + Edge edge = getEdge(source, target); + return removeEdge(edge); + } + + public Edge removeEdge(Edge edge) { + edge.source.getOutEdges().remove(edge); + edge.target.getInEdges().remove(edge); + return edge; + } + +// public void replaceNodes(Node header, List nodes, Node newNode) { +// replaceNodes(header, (Node[]) nodes.toArray(new Node[nodes.size()]), newNode); +// } + + public String getEdgeId(Node source, Node target) { + return source.getId() + "->" + target.getId(); + } + + public void addIfElseEdge(Node source, Node ifTarget, Node elseTarget, BooleanExpression be) { + ConditionalEdge ifEdge = (ConditionalEdge) addEdge(source, ifTarget, ConditionalEdge.class); + ifEdge.setBooleanExpression(be); + ConditionalEdge elseEdge = (ConditionalEdge) addEdge(source, elseTarget, ConditionalEdge.class); + elseEdge.setBooleanExpression(be); + elseEdge.setNegate(true); + } + + public Edge addEdge(Node source, Node target) { + return addEdge(source, target, Edge.class); + } + + public Edge addEdge(Node source, Node target, Class clazz) { + Edge edge = getEdge(source, target); + if (edge != null) { + // TODO: Why not allow adding multiple edges? This is possible + // anyway through reroot or redirect! + throw new RuntimeException("Edge already exists"); + } + if (clazz.equals(Edge.class)) { + edge = new Edge(this, source, target); + } else if (clazz.equals(SwitchEdge.class)) { + edge = new SwitchEdge(this, source, target); + } else if (clazz.equals(ConditionalEdge.class)) { + edge = new ConditionalEdge(this, source, target); + } else { + throw new RuntimeException("Illegal edge class " + clazz); + } + source.addEdge(edge); + if (source != target) { + target.addEdge(edge); + } + return edge; + } + + public Edge getEdge(Node source, Node target) { + for (Edge edge : source.getOutEdges()) { + if (edge.target == target) return edge; + } + return null; + } + + public void removeNode(Node node) { + replaceNode(node, null); + } + + public Set removeOutEdges(Node node) { + Set outEdges = new HashSet(node.outEdges); + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + removeEdge(edge); + } + + return outEdges; + } + + /** + * Removes all in-edges to the specified node. + * + * @return the set of removed in-edges + */ + public Set removeInEdges(Node node) { + Set inEdges = new HashSet(node.inEdges); + Iterator iter = inEdges.iterator(); + while (iter.hasNext()) { + Edge edge = iter.next(); + removeEdge(edge); + } + + return inEdges; + } + + public Set removeSelfEdges(Node node) { + Set selfEdges = new HashSet(); + + for (Edge edge: new HashSet(node.outEdges)) { + if (edge.target != node) continue; + removeEdge(edge); + selfEdges.add(edge); + } + + return selfEdges; + } + + public void rerootGlobalOutEdges(Node node, Node newSource) { + Edge[] edges = node.getOutEdgesArray(); + for (int i=0; i(oldNode.getDomChildren())) { + child.setDomParent(newNode); + } + } + + if (oldNode.inEdges.size() > 0 || oldNode.outEdges.size() > 0) + throw new RuntimeException("Cannot replace node with edges"); + + oldNode.setDomParent(null); + } + + public Collection getNodes() { + return nodes.values(); + } + + public int size() { + return getNodes().size(); + } + + public Block reduceDumb() { + Block block = new Block(); + + for (Node node : getNodes()) { + block.appendChild(node.block); + + if (node.isBranch()) { + IfStatement ifStmt = new IfStatement(); + ConditionalEdge cEdge = node.getConditionalEdge(true); + ifStmt.setExpression(cEdge.getBooleanExpression().getExpression()); + ifStmt.setIfBlock(new Block()); + Block targetBlock = cEdge.target.block; + ifStmt.getIfBlock().appendChild(new BreakStatement(targetBlock)); + ifStmt.setElseBlock(new Block()); + targetBlock = node.getConditionalEdge(false).target.block; + ifStmt.getElseBlock().appendChild(new BreakStatement(targetBlock)); + block.appendChild(ifStmt); + } else { + for (Edge e : node.getOutEdges()) { + BreakStatement bStmt = new BreakStatement(e.target.block); + node.block.appendChild(bStmt); + } + } + } + return block; + } + + boolean isTarget(Node n, Set edgeSet) { + Iterator iter = edgeSet.iterator(); + while (iter.hasNext()) { + Edge e = (Edge) iter.next(); + if (n == e.target) return true; + } + return false; + } + + public void rollOut(Node node, Block targetBlock) { + if (node.trans != null) { + node.trans.rollOut(targetBlock); + } else { + targetBlock.appendChildren(node.block); + } + } + + public void dump(String msg) { + StringBuffer sb = new StringBuffer(); + sb.append(msg + " ...\n"); + for (Node node : getNodes()) { + sb.append(node.describe() + "\n"); + //dump(node.block, "Block"); + } + sb.append("... " + msg); + logger.debug(sb.toString()); + } +} diff --git a/src/main/java/com/j2js/cfg/Node.java b/src/main/java/com/j2js/cfg/Node.java new file mode 100644 index 0000000..4ba115e --- /dev/null +++ b/src/main/java/com/j2js/cfg/Node.java @@ -0,0 +1,334 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 12.11.2005 + */ +package com.j2js.cfg; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import com.j2js.ASTNodeStack; +import com.j2js.cfg.transformation.Transformation; +import com.j2js.dom.Block; +import com.j2js.dom.Expression; +import com.j2js.dom.IfStatement; + +/** + * @author wolfgang + */ +public class Node { + + public class Reference { + public Node source; + public boolean isBackward = false; + } + + public static int NON_HEADER = 0; + public static int REDUCIBLE = 1; + public static int IRREDUCIBLE = 2; + + String id; + + Set inEdges = new LinkedHashSet(); + Set outEdges = new LinkedHashSet(); + Graph graph; + private int currentPc = -1; + private int initialPc = -1; + public ASTNodeStack stack = new ASTNodeStack(); + public Block block = new Block(); + public boolean closed = false; + + protected int preOrderIndex = -1; + + private Node domParent; // Block that (immediately) dominates this Block + private Set domChildren = new HashSet(); // Blocks that this Block dominates + + IfStatement ifStmt; + public Expression switchExpression; + + public Transformation trans; + public Collection jsrCallers = new HashSet(); + + public boolean isSwitchHeader = false; + + public Node(Graph theGraph, int pc) { + this(theGraph); + setInitialPc(pc); + } + + public Node(Graph theGraph) { + graph = theGraph; + } + + public int getComplexity() { + Node node = this; + int complexity = 0; + while (node.trans != null) { + complexity++; + node = node.trans.header; + } + return complexity; + } + + /** + * @return Returns the pc. + */ + public int getCurrentPc() { + return currentPc; + } + /** + * @param pc The pc to set. + */ + public void setCurrentPc(int pc) { + currentPc = pc; + } + + public void close() { + closed = true; + } + + public void addEdge(Edge e) { + if (e.source == this) { + if (!outEdges.add(e)) { + throw new RuntimeException("\n" + this + "\nalready bound to " + e); + } + } + + if (e.target == this) { + if (!inEdges.add(e)) { + throw new RuntimeException("" + this + " already bound to " + e); + } + } + } + + public String toString() { + String s = getClass().getName(); + // Same as getClass().getSimpleName() in JDK 5.0. + s = s.replaceFirst(".*\\.", ""); + + s += " " + id + "[" + initialPc + ", " + currentPc + "]"; + //if (preOrderIndex >= 0) s += " preIndex=" + preOrderIndex; + if (domParent != null) s += " dominated by " + domParent.id; + if (isLoopHeader()) s += " LH"; + return s; + } + + public String describe() { + String s = toString(); + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge e = (Edge) iter.next(); + s += "\n\t" + e; + } + return s; + } + + + /** + * @return Returns the inEdges. + */ + public Set getInEdges() { + return inEdges; + } + + public Edge getSelfEdge() { + Iterator iterator = inEdges.iterator(); + while (iterator.hasNext()) { + Edge edge = (Edge) iterator.next(); + if (edge.source == this) return edge; + } + return null; + } + +// public Set getGlobalInEdges() { +// HashSet set = new HashSet(); +// Iterator iterator = inEdges.iterator(); +// while (iterator.hasNext()) { +// Edge edge = (Edge) iterator.next(); +// if (edge.global) set.add(edge); +// } +// return set; +// } + +// public Set getForewardOutEdges() { +// HashSet set = new HashSet(); +// Iterator iterator = outEdges.iterator(); +// while (iterator.hasNext()) { +// Edge edge = (Edge) iterator.next(); +// if (!edge.isBackEdge()) set.add(edge); +// } +// return set; +// } + + /** + * @return Returns the outEdges. + */ + public Set getOutEdges() { + return outEdges; + } + + public Edge[] getOutEdgesArray() { + return outEdges.toArray(new Edge[outEdges.size()]); + } + + public Edge[] getInEdgesArray() { + return inEdges.toArray(new Edge[inEdges.size()]); + } + + public int getPreOrderIndex() { + return preOrderIndex; + } + + public void setPreOrderIndex(int thePreOrderIndex) { + preOrderIndex = thePreOrderIndex; + } + + public Set succs() { + Set list = new LinkedHashSet(); + Edge[] edges = getOutEdgesArray(); + for (int i=edges.length-1; i>=0; i--) { + list.add(edges[i].target); + } + return list; + } + + public Set preds() { + Set list = new LinkedHashSet(); + Iterator iter = inEdges.iterator(); + while (iter.hasNext()) { + Edge e = (Edge) iter.next(); + list.add(e.source); + } + return list; + } + + public Node getPred() { + int count = inEdges.size(); + if (count != 1) throw new RuntimeException("Requested unique predecessor, found " + count); + return inEdges.iterator().next().source; + } + + public Node getSucc() { + return getOutEdge().target; + } + + public Edge getLocalOutEdgeOrNull() { + Edge outEdge = null; + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (outEdge != null) new RuntimeException("Found multiple local out-edges"); + outEdge = edge; + } + return outEdge; + } + + public Edge getOutEdge() { + int count = outEdges.size(); + if (count != 1) throw new RuntimeException("Requested unique successor, found " + count); + return outEdges.iterator().next(); + } + + /** + * Return true if this node is equal to or dominates the specified node. + */ + public boolean isDomAncestor(Node node) { + do { + if (node == null) return false; + if (node == this) return true; + node = node.getDomParent(); + } while (true); + } + + public Set getDomChildren() { + return domChildren; + } + + public Node getDomChild() { + if (domChildren.size() != 1) throw new RuntimeException("Node must have single child"); + return getDomChildren().iterator().next(); + } + + public Node getDomParent() { + return domParent; + } + + public void setDomParent(Node newDomParent) { + // If this Block already had a dominator specified, remove + // it from its dominator's children. + if (domParent != null) { + domParent.domChildren.remove(this); + } + + domParent = newDomParent; + + // Add this Block to its new dominator's children. + if (domParent != null) { + domParent.domChildren.add(this); + } + } + + public boolean isBranch() { + Edge[] edges = getOutEdgesArray(); + if (edges.length != 2) return false; + if (edges[0] instanceof ConditionalEdge && edges[1] instanceof ConditionalEdge) return true; + if ((edges[0] instanceof ConditionalEdge) || (edges[1] instanceof ConditionalEdge)) + throw new RuntimeException("Node must not have mixed edges"); + return false; + } + + /** + * Returns either the true or false edge. + */ + public ConditionalEdge getConditionalEdge(boolean trueFalse) { + if (!isBranch()) throw new RuntimeException("Node must be a branch"); + Iterator iter = outEdges.iterator(); + Edge edge = iter.next(); + if (!trueFalse) edge = iter.next(); + return (ConditionalEdge) edge; + } + + public String getId() { + return id; + } + + public int getInitialPc() { + return initialPc; + } + + public Graph getGraph() { + return graph; + } + + public void setInitialPc(int theInitialPc) { + initialPc = theInitialPc; + currentPc = theInitialPc; + } + + public boolean isLoopHeader() { + Iterator iter = inEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (edge.isBackEdge()) return true; + } + return false; + } + + public boolean hasSelfEdges() { + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (edge.target == this) return true; + } + return false; + } + +// public void replaceEdge(Edge oldEdge, Edge newEdge) { +// source.outEdges.remove(this); +// newSource.outEdges.add(this); +// source = newSource; +// } +} diff --git a/src/main/java/com/j2js/cfg/SwitchEdge.java b/src/main/java/com/j2js/cfg/SwitchEdge.java new file mode 100644 index 0000000..ce58a82 --- /dev/null +++ b/src/main/java/com/j2js/cfg/SwitchEdge.java @@ -0,0 +1,14 @@ +package com.j2js.cfg; + +import java.util.ArrayList; +import java.util.List; + +import com.j2js.dom.NumberLiteral; + +public class SwitchEdge extends Edge { + public List expressions = new ArrayList(); + + SwitchEdge(Graph graph, Node theSource, Node theTarget) { + super(graph, theSource, theTarget); + } +} diff --git a/src/main/java/com/j2js/cfg/TryHeaderNode.java b/src/main/java/com/j2js/cfg/TryHeaderNode.java new file mode 100644 index 0000000..e1d74d0 --- /dev/null +++ b/src/main/java/com/j2js/cfg/TryHeaderNode.java @@ -0,0 +1,74 @@ +package com.j2js.cfg; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.j2js.dom.TryStatement; + +public class TryHeaderNode extends Node { + + public TryStatement tryStmt; + + public TryHeaderNode(Graph theGraph) { + super(theGraph); + } + + public void setTryBody(Node theTryBody) { + Edge edge = graph.addEdge(this, theTryBody); + edge.type = EdgeType.TRYBODY; + } + + public void setFinallyNode(Node theFinallyNode) { + Edge edge = graph.addEdge(this, theFinallyNode); + edge.type = EdgeType.FINALLY; + //finallyNode = theFinallyNode; + } + + public void addCatchNode(Node node) { + Edge edge = graph.addEdge(this, node); + edge.type = EdgeType.CATCH; + //catchNodes.add(node); + } + + public Node getSuccessor() { + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (edge.type == null) return edge.target; + } + return null; + } + + public Node getTryBody() { + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (edge.type == EdgeType.TRYBODY) return edge.target; + } + throw new RuntimeException(); + } + + public Node getFinallyNode() { + Iterator iter = outEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (edge.type == EdgeType.FINALLY) return edge.target; + } + return null; + } + + public List getCatchNodes() { + List catchNodes = new ArrayList(); + for (Edge edge : outEdges) { + if (edge.type == EdgeType.CATCH) catchNodes.add(edge.target); + } + + return catchNodes; + } + + public TryStatement getTryStatement() { + return tryStmt; + } + +} diff --git a/src/main/java/com/j2js/cfg/WrapperNode.java b/src/main/java/com/j2js/cfg/WrapperNode.java new file mode 100644 index 0000000..90f7ef0 --- /dev/null +++ b/src/main/java/com/j2js/cfg/WrapperNode.java @@ -0,0 +1,11 @@ +package com.j2js.cfg; + +public class WrapperNode extends Node{ + + Node node; + + public WrapperNode(Graph theGraph) { + super(theGraph); + } + +} diff --git a/src/main/java/com/j2js/cfg/transformation/Loop.java b/src/main/java/com/j2js/cfg/transformation/Loop.java new file mode 100644 index 0000000..fa298d3 --- /dev/null +++ b/src/main/java/com/j2js/cfg/transformation/Loop.java @@ -0,0 +1,47 @@ +package com.j2js.cfg.transformation; + +import java.util.Iterator; +import java.util.Set; + +import com.j2js.cfg.Edge; +import com.j2js.dom.Block; +import com.j2js.dom.BooleanLiteral; +import com.j2js.dom.WhileStatement; + + +public class Loop extends Transformation { + + private Set selfEdges; + + public boolean applies_() { + return header.hasSelfEdges(); + } + + public void apply_() { + // Remove self edges. + selfEdges = graph.removeSelfEdges(header); + } + + void rollOut_(Block block) { + WhileStatement loopStmt = new WhileStatement(); + Block loopBody = new Block(); + loopStmt.setBlock(loopBody); + loopStmt.setExpression(new BooleanLiteral(true)); + + block.appendChild(loopStmt); + + Iterator iter = selfEdges.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (!edge.isGlobal()) continue; + loopStmt.isLabeled(); + produceJump(edge, loopStmt); + } + + graph.rollOut(header, loopBody); + } + + public String toString() { + return super.toString() + "(" + header + ")"; + } +} diff --git a/src/main/java/com/j2js/cfg/transformation/Merge.java b/src/main/java/com/j2js/cfg/transformation/Merge.java new file mode 100644 index 0000000..27f0068 --- /dev/null +++ b/src/main/java/com/j2js/cfg/transformation/Merge.java @@ -0,0 +1,77 @@ +package com.j2js.cfg.transformation; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import com.j2js.cfg.ControlFlowGraph; +import com.j2js.cfg.Edge; +import com.j2js.cfg.EdgeCollections; +import com.j2js.cfg.Node; +import com.j2js.dom.Block; + +/** + * @author wolfgang + */ +public class Merge extends Transformation { + + private Node tail; + private Set inEdgesForTail; + + public Merge() { + } + + public Merge(ControlFlowGraph theGraph) { + graph = theGraph; + } + + public boolean applies_() { + HashSet headerSet = new HashSet(); + headerSet.add(header); + + for (Node child : header.getDomChildren()) { + if (EdgeCollections.getSources(child.getInEdges()).equals(headerSet)) { + tail = child; + return true; + } + } + + return false; + } + + public void apply_() { + // Remove all in-edges to Tail. + inEdgesForTail = graph.removeInEdges(tail); + + // Reroot all out-edges from Tail. + graph.rerootOutEdges(tail, newNode, false); + + //Remove Tail. + graph.removeNode(tail); + } + + void rollOut_(Block block) { + Block labeledBlock = block; + + Iterator iter = inEdgesForTail.iterator(); + while (iter.hasNext()) { + Edge edge = (Edge) iter.next(); + if (!edge.isGlobal()) continue; + + if (labeledBlock == block) { + labeledBlock = new Block(); + block.appendChild(labeledBlock); + } + + produceJump(edge, labeledBlock); + } + + graph.rollOut(header, labeledBlock); + graph.rollOut(tail, block); + block.appendChildren(newNode.block); + } + + public String toString() { + return super.toString() + "(" + header + ", " + tail + ")"; + } +} diff --git a/src/main/java/com/j2js/cfg/transformation/Switch.java b/src/main/java/com/j2js/cfg/transformation/Switch.java new file mode 100644 index 0000000..d581bd3 --- /dev/null +++ b/src/main/java/com/j2js/cfg/transformation/Switch.java @@ -0,0 +1,83 @@ +package com.j2js.cfg.transformation; + +import java.util.ArrayList; +import java.util.List; + +import com.j2js.cfg.Edge; +import com.j2js.cfg.Node; +import com.j2js.cfg.SwitchEdge; +import com.j2js.dom.Block; +import com.j2js.dom.NumberLiteral; +import com.j2js.dom.SwitchCase; +import com.j2js.dom.SwitchStatement; + +/** + */ +public class Switch extends Transformation { + + private List caseGroups = new ArrayList(); + private List> caseGroupExpressions = new ArrayList>(); + + public boolean applies_() { + return header.isSwitchHeader; + } + + private void removeFallThroughEdgesl() { + Edge prevPotentialFallThroughEdge = null; + + for (Edge e : header.getOutEdges()) { + if (!(e instanceof SwitchEdge)) continue; + + SwitchEdge edge = (SwitchEdge) e; + Node caseGroup = edge.target; + + if (prevPotentialFallThroughEdge != null && prevPotentialFallThroughEdge.target == caseGroup) { + // This is a fall through edge. + graph.removeEdge(prevPotentialFallThroughEdge); + } + + prevPotentialFallThroughEdge = caseGroup.getLocalOutEdgeOrNull(); + } + } + + void apply_() { + removeFallThroughEdgesl(); + + for (Edge e : new ArrayList(header.getOutEdges())) { + if (!(e instanceof SwitchEdge)) continue; + + SwitchEdge edge = (SwitchEdge) e; + Node caseGroup = edge.target; + caseGroups.add(caseGroup); + caseGroupExpressions.add(edge.expressions); + graph.rerootOutEdges(caseGroup, newNode, true); + graph.removeOutEdges(caseGroup); + graph.removeInEdges(caseGroup); + graph.removeNode(caseGroup); + } + } + + void rollOut_(Block block) { + SwitchStatement switchStmt = new SwitchStatement(); + switchStmt.setExpression(header.switchExpression); + + for (int i=0; i " + newNode); + return newNode; + } + + public boolean applies(Node node) { + graph = (ControlFlowGraph) node.getGraph(); + header = node; + return applies_(); + } + + abstract boolean applies_(); + abstract void apply_(); + abstract void rollOut_(Block block); + + public void rollOut(Block block) { + rollOut_(block); + block.appendChildren(newNode.block); + } + + void produceJump(Edge edge, Block labeledBlock) { + Node referer = edge.getOrgSource(); + Block breakBlock; + if (edge instanceof ConditionalEdge) { + ConditionalEdge condEdge = (ConditionalEdge) edge; + BooleanExpression condExpr = condEdge.getBooleanExpression(); + Expression expr = Optimizer.simplifyBooleanExpression(condExpr.getExpression(), condEdge.isNegate()); + IfStatement ifStmt = new IfStatement(); + ifStmt.setExpression(expr); + referer.block.appendChild(ifStmt); + Block ifBlock = new Block(); + ifStmt.setIfBlock(ifBlock); + breakBlock = ifBlock; + } else { + breakBlock = referer.block; + } + + if (edge.isBackEdge()) { + breakBlock.appendChild(new ContinueStatement(labeledBlock)); + } else { + breakBlock.appendChild(new BreakStatement(labeledBlock)); + } + } + + public String toString() { + return getClass().getName(); + } +} diff --git a/src/main/java/com/j2js/cfg/transformation/Try.java b/src/main/java/com/j2js/cfg/transformation/Try.java new file mode 100644 index 0000000..4ad7779 --- /dev/null +++ b/src/main/java/com/j2js/cfg/transformation/Try.java @@ -0,0 +1,77 @@ +package com.j2js.cfg.transformation; + +import java.util.Iterator; +import java.util.List; + +import com.j2js.cfg.Node; +import com.j2js.cfg.TryHeaderNode; +import com.j2js.dom.Block; +import com.j2js.dom.CatchClause; +import com.j2js.dom.TryStatement; + +public class Try extends Transformation { + + private Node tryBodyNode; + private List catchNodes; + private Node finallyNode; + + boolean applies_() { + return header instanceof TryHeaderNode; + } + + void apply_() { + TryHeaderNode head = (TryHeaderNode) header; + catchNodes = head.getCatchNodes(); + + for (Node catchNode : head.getCatchNodes()) { + graph.rerootOutEdges(catchNode, newNode, false); + graph.removeInEdges(catchNode); + graph.removeNode(catchNode); + } + + tryBodyNode = head.getTryBody(); + graph.rerootOutEdges(tryBodyNode, newNode, false); + graph.removeInEdges(tryBodyNode); + graph.removeNode(tryBodyNode); + + finallyNode = head.getFinallyNode(); + if (finallyNode != null) { + Block b = finallyNode.block; + // Remove return address. + b.removeChild(b.getFirstChild()); + // Remove return statement. + b.removeChild(b.getLastChild()); + graph.rerootOutEdges(finallyNode, newNode, false); + graph.removeInEdges(finallyNode); + graph.removeNode(finallyNode); + } + + } + + void rollOut_(Block block) { + TryHeaderNode head = (TryHeaderNode) header; + + TryStatement stmt = head.getTryStatement(); + block.appendChild(stmt); + + graph.rollOut(tryBodyNode, stmt.getTryBlock()); + + if (finallyNode != null) { + stmt.setFinallyBlock(new Block()); + graph.rollOut(finallyNode, stmt.getFinallyBlock()); + } + + CatchClause cc = (CatchClause) stmt.getCatchStatements().getFirstChild(); + Iterator iter = catchNodes.iterator(); + while (iter.hasNext()) { + Node catchNode = (Node) iter.next(); + graph.rollOut(catchNode, cc); + cc = (CatchClause) cc.getNextSibling(); + } + } + + public String toString() { + return super.toString() + "(" + header + ")"; + } + +} diff --git a/src/main/java/com/j2js/dom/ASTNode.java b/src/main/java/com/j2js/dom/ASTNode.java new file mode 100644 index 0000000..630a1b9 --- /dev/null +++ b/src/main/java/com/j2js/dom/ASTNode.java @@ -0,0 +1,200 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/* + * Statement.java + * + * Created on 21. Mai 2004, 11:45 + */ + +/** + * + * @author kuehn + */ +public class ASTNode { + + public static final int BEFORE = 0; + public static final int AFTER = 1; + public static final int SAME = 2; + public static final int CONTAINS = 3; + public static final int ISCONTAINED = 4; + + int beginIndex = Integer.MAX_VALUE; + int endIndex = Integer.MIN_VALUE; + private ASTNode parent = null; + private ASTNode previousSibling = null; + private ASTNode nextSibling = null; + + private int stackDelta = 0; + + public ASTNode() { + super(); + } + + public ASTNode(int theBeginIndex, int theEndIndex) { + setRange(theBeginIndex, theEndIndex); + } + + /** + * @return Returns the stackDelta. + */ + public int getStackDelta() { + return stackDelta; + } + + /** + * @param theStackDelta The stackDelta to set. + */ + public void setStackDelta(int theStackDelta) { + stackDelta = theStackDelta; + } + + public void widen(ASTNode node) { + leftWiden(node.beginIndex); + rightWiden(node.endIndex); + } + + public void leftWiden(int targetBeginIndex) { + if (targetBeginIndex < beginIndex) beginIndex = targetBeginIndex; + } + + public void rightWiden(int targetEndIndex) { + if (targetEndIndex > endIndex) endIndex = targetEndIndex; + } + + public void setRange(int theBeginIndex, int theEndIndex) { + setBeginIndex(theBeginIndex); + setEndIndex(theEndIndex); + } + +// private void checkRange() { +// if (endIndex!=Integer.MIN_VALUE && beginIndex!=Integer.MAX_VALUE && endIndex" + endIndex); +// } +// } + + /** Getter for property beginIndex. + * @return Value of property beginIndex. + */ + public int getBeginIndex() { + return beginIndex; + } + + /** Setter for property beginIndex. + * @param theBeginIndex New value of property beginIndex. + */ + public void setBeginIndex(int theBeginIndex) { + beginIndex = theBeginIndex; + } + + /** Getter for property endIndex. + * @return Value of property endIndex. + */ + public int getEndIndex() { + return endIndex; + } + + /** Setter for property endIndex. + * @param endIndex New value of property endIndex. + */ + public void setEndIndex(int theEndIndex) { + endIndex = theEndIndex; + } + + public boolean isRightSiblingOf(ASTNode leftSibling) { + for (ASTNode node=this; node!=null; node=node.getPreviousSibling()) { + if (node == leftSibling) { + return true; + } + } + return false; + } + + public ASTNode rightMostSibling() { + for (ASTNode node=this;;) { + if (node.getNextSibling() == null) { + return node; + } + node = node.getNextSibling(); + } + } + + public boolean isAncestorOf(ASTNode node) { + do { + node = node.getParentNode(); + if (node == this) { + return true; + } + } while (node != null); + + return false; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + if (getBeginIndex() != Integer.MAX_VALUE) { + sb.append("["); + sb.append(getBeginIndex()); + sb.append(", "); + sb.append(getEndIndex()); + sb.append("]"); + } + return sb.toString(); + } + + /** + * @return Returns the parent. + */ + public ASTNode getParentNode() { + return parent; + } + + public Block getParentBlock() { + return (Block) parent; + } + + public Block getLogicalParentBlock() { + if (parent != null && parent.parent instanceof IfStatement) { + return (Block) parent.parent; + } + return (Block) parent; + } + + /** + * @param theParent The parent to set. + */ + public void setParentNode(ASTNode theParent) { + parent = theParent; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the nextSibling. + */ + public ASTNode getNextSibling() { + return nextSibling; + } + /** + * @param theNextSibling The nextSibling to set. + */ + public void setNextSibling(ASTNode theNextSibling) { + nextSibling = theNextSibling; + } + /** + * @return Returns the previousSibling. + */ + public ASTNode getPreviousSibling() { + return previousSibling; + } + /** + * @param thePreviousSibling The previousSibling to set. + */ + public void setPreviousSibling(ASTNode thePreviousSibling) { + previousSibling = thePreviousSibling; + } +} diff --git a/src/main/java/com/j2js/dom/ArrayAccess.java b/src/main/java/com/j2js/dom/ArrayAccess.java new file mode 100644 index 0000000..8f1aef2 --- /dev/null +++ b/src/main/java/com/j2js/dom/ArrayAccess.java @@ -0,0 +1,54 @@ +/* + * Created on Oct 31, 2004 + */ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class ArrayAccess extends Expression implements Assignable { + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public boolean isSame(Object obj) { + if (!(obj instanceof ArrayAccess)) return false; + ArrayAccess other = (ArrayAccess) obj; + if (getArray() instanceof VariableBinding && other.getArray() instanceof VariableBinding) { + VariableBinding vba = (VariableBinding) getArray(); + VariableBinding vbb = (VariableBinding) other.getArray(); + return vba.getVariableDeclaration() == vbb.getVariableDeclaration(); + } + return false; + } + + /** + * @return Returns the array. + */ + public Expression getArray() { + return (Expression) getChildAt(0); + } + /** + * @param array The array to set. + */ + public void setArray(Expression array) { + widen(array); + setChildAt(0, array); + } + /** + * @return Returns the index. + */ + public Expression getIndex() { + return (Expression) getChildAt(1); + } + /** + * @param index The index to set. + */ + public void setIndex(Expression index) { + widen(index); + setChildAt(1, index); + } +} diff --git a/src/main/java/com/j2js/dom/ArrayCreation.java b/src/main/java/com/j2js/dom/ArrayCreation.java new file mode 100644 index 0000000..35a881d --- /dev/null +++ b/src/main/java/com/j2js/dom/ArrayCreation.java @@ -0,0 +1,50 @@ +package com.j2js.dom; + +import java.util.List; + +import org.apache.bcel.generic.Type; + +import com.j2js.assembly.Project; +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class ArrayCreation extends Expression { + + private List dimensions; + private ArrayInitializer initializer; + + public ArrayCreation(MethodDeclaration methodDecl, Type theType, List theDimensions) { + type = theType; + dimensions = theDimensions; + for (ASTNode dimension : dimensions) { + this.widen(dimension); + } + Project.getSingleton().addReference(methodDecl, this); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the initializer. + */ + public ArrayInitializer getInitializer() { + return initializer; + } + /** + * @param theInitializer The initializer to set. + */ + public void setInitializer(ArrayInitializer theInitializer) { + initializer = theInitializer; + } + /** + * @return Returns the dimensions. + */ + public List getDimensions() { + return dimensions; + } + +} diff --git a/src/main/java/com/j2js/dom/ArrayInitializer.java b/src/main/java/com/j2js/dom/ArrayInitializer.java new file mode 100644 index 0000000..b151a07 --- /dev/null +++ b/src/main/java/com/j2js/dom/ArrayInitializer.java @@ -0,0 +1,24 @@ +package com.j2js.dom; + +import java.util.List; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class ArrayInitializer extends Expression { + + private List expressions = new java.util.ArrayList(); + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the expressions. + */ + public List getExpressions() { + return expressions; + } +} diff --git a/src/main/java/com/j2js/dom/Assignable.java b/src/main/java/com/j2js/dom/Assignable.java new file mode 100644 index 0000000..a60a0ba --- /dev/null +++ b/src/main/java/com/j2js/dom/Assignable.java @@ -0,0 +1,17 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 16.10.2005 + */ +package com.j2js.dom; + +/** + * Tagging interface for all node types which are legal assignment targets. + */ +public interface Assignable { + + /** + * Returns true if the specified Assignable corresponds to this Assignable. + */ + public boolean isSame(Object obj); + +} diff --git a/src/main/java/com/j2js/dom/Assignment.java b/src/main/java/com/j2js/dom/Assignment.java new file mode 100644 index 0000000..df54d30 --- /dev/null +++ b/src/main/java/com/j2js/dom/Assignment.java @@ -0,0 +1,96 @@ +package com.j2js.dom; + +import java.util.HashMap; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author kuehn + */ +public class Assignment extends Expression { + + static public class Operator { + + static private HashMap opsByToken = new HashMap(); + + static public Operator lookup(String token) { + return opsByToken.get(token); + } + + static public Operator ASSIGN = new Operator("="); + static public Operator PLUS_ASSIGN = new Operator("+="); + static public Operator MINUS_ASSIGN = new Operator("-="); + static public Operator TIMES_ASSIGN = new Operator("*="); + static public Operator DIVIDE_ASSIGN = new Operator("/="); + static public Operator BIT_AND_ASSIGN = new Operator("&="); + static public Operator BIT_OR_ASSIGN = new Operator("|="); + static public Operator BIT_XOR_ASSIGN = new Operator("^="); + static public Operator REMAINDER_ASSIGN = new Operator("%="); + static public Operator LEFT_SHIFT_ASSIGN = new Operator("<<="); + static public Operator RIGHT_SHIFT_SIGNED_ASSIGN = new Operator(">>="); + static public Operator RIGHT_SHIFT_UNSIGNED_ASSIGN = new Operator(">>>="); + + private String token; + + Operator(String theToken) { + token = theToken; + opsByToken.put(theToken, this); + } + + public String toString() { + return token; + } + } + + private Operator operator; + + public Assignment(Operator theOperator) { + super(); + operator = theOperator; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @param rightHandSide The rightHandSide to set. + */ + public void setRightHandSide(Expression rightHandSide) { + widen(rightHandSide); + setChildAt(1, rightHandSide); + } + + /** + * @return Returns the rightHandSide. + */ + public Expression getRightHandSide() { + return (Expression) getChildAt(1); + } + + /** + * @param leftHandSide The leftHandSide to set. + */ + public void setLeftHandSide(Expression leftHandSide) { + setChildAt(0, leftHandSide); + } + + /** + * @return Returns the leftHandSide. + */ + public Expression getLeftHandSide() { + return (Expression) getChildAt(0); + } + /** + * @return Returns the operator. + */ + public Operator getOperator() { + return operator; + } + /** + * @param theOerator The operator to set. + */ + public void setOperator(Operator theOperator) { + operator = theOperator; + } +} diff --git a/src/main/java/com/j2js/dom/Block.java b/src/main/java/com/j2js/dom/Block.java new file mode 100644 index 0000000..cd68d02 --- /dev/null +++ b/src/main/java/com/j2js/dom/Block.java @@ -0,0 +1,356 @@ +package com.j2js.dom; + +import com.j2js.Log; +import com.j2js.visitors.AbstractVisitor; +import org.w3c.dom.DOMException; + +/* + * Block.java + * + * Created on 21. Mai 2004, 11:38 + */ +public class Block extends ASTNode { + + public static int TAG = 0; + + private String label; + + private ASTNode firstChild = null; + private ASTNode lastChild = null; + private int childCount = 0; + + public Block() { + super(); + } + + public Block(int theBeginIndex) { + setBeginIndex(theBeginIndex); + } + + public Block(int theBeginIndex, int theEndIndex) { + this(theBeginIndex); + setRange(theBeginIndex, theEndIndex); + } + +// public Block getFooBlock() { +// Block block = this; +// while (block!=null) { +// if (!block.getClass().equals(Block.class)) { +// return block; +// } +// block = block.getParentBlock(); +// } +// return this; +// } + + public int getTargetPc() { + if (lastChild instanceof Jump) { + return ((Jump) lastChild).getTargetIndex(); + } + return Integer.MAX_VALUE; + } + + public int getTargetIndex() { + return beginIndex; + } + + public void setBeginIndex(int theBeginIndex) { + super.setBeginIndex(theBeginIndex); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public void appendChildren(ASTNode begin, ASTNode end) { + if (begin == null || end == null) { + throw new RuntimeException("Illegal null parameters"); + } + if (begin.getParentBlock() != null) + (begin.getParentBlock()).removeChildren(begin, end); + + if (firstChild == null) { + setFirstChildInternal(begin); + } else { + ASTNode prev = getLastChild(); + prev.setNextSibling(begin); + begin.setPreviousSibling(prev); + } + setLastChildInternal(end); + + ASTNode node = begin; + while (node != null) { + node.setParentNode(this); + childCount++; + if (node == end) break; + node = node.getNextSibling(); + } + } + + /** + * Appends all children of the specified block to this block. + * @param sourceBlock + */ + public void appendChildren(Block sourceBlock) { + if (sourceBlock.getChildCount() > 0) { + appendChildren(sourceBlock.getFirstChild(), sourceBlock.getLastChild()); + } + } + + /** + * Sets the specified node at the index. Returns the replaced node, or null if the index + * was equal to the cild count. + */ + public ASTNode setChildAt(int index, ASTNode newChild) { + if (index == childCount) { + appendChild(newChild); + return null; + } else if (index < 0 || index > childCount) { + throw new RuntimeException("Index " + index + " out of range [0, " + childCount + "]"); + } + + return replaceChild(newChild, getChildAt(index)); + } + + public ASTNode getChildAt(int index) { + if (childCount == 0) { + throw new RuntimeException("Requested child at index " + index + ", but block has no children"); + } + if (index < 0 || index >= childCount) { + throw new RuntimeException("Index " + index + " out of range [0, " + (childCount-1) + "]"); + } + + if (index == childCount-1) { + return getLastChild(); + } + + ASTNode node = getFirstChild(); + int i = 0; + while (i < index) { + i++; + node = node.getNextSibling(); + } + + return node; + } + + public ASTNode appendChild(ASTNode newChild) { + Log.getLogger().debug("Appending " + newChild + " to " + this); + + unlink(newChild); + + if (firstChild == null) { + newChild.setPreviousSibling(null); + setFirstChildInternal(newChild); + } else { + ASTNode prev = getLastChild(); + prev.setNextSibling(newChild); + newChild.setPreviousSibling(prev); + } + setLastChildInternal(newChild); + newChild.setParentNode(this); + childCount++; + + return newChild; + } + + public ASTNode replaceChild(ASTNode newChild, ASTNode oldChild) { + Log.getLogger().debug("Replacing " + oldChild + " by " + newChild + " in " + this); + if (oldChild.getParentNode() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Node " + oldChild + " is not a child of " + this); + } + unlink(newChild); + if (oldChild.getPreviousSibling() != null) { + oldChild.getPreviousSibling().setNextSibling(newChild); + } + if (oldChild.getNextSibling() != null) { + oldChild.getNextSibling().setPreviousSibling(newChild); + } + newChild.setPreviousSibling(oldChild.getPreviousSibling()); + newChild.setNextSibling(oldChild.getNextSibling()); + newChild.setParentNode(this); + if (getFirstChild() == oldChild) { + setFirstChildInternal(newChild); + } + if (getLastChild() == oldChild) { + setLastChildInternal(newChild); + } + oldChild.setPreviousSibling(null); + oldChild.setNextSibling(null); + oldChild.setParentNode(null); + return oldChild; + } + + public ASTNode removeChild(ASTNode oldChild) { + Log.getLogger().debug("Removing " + oldChild + " from " + this); + removeChildren(oldChild, oldChild); + + oldChild.setPreviousSibling(null); + oldChild.setNextSibling(null); + return oldChild; + } + + /** + * Removes all children from begin to end inclusively. + */ + private void removeChildren(ASTNode begin, ASTNode end) { + if (begin == null || end == null) throw new RuntimeException("Illegal null parameters"); + ASTNode node = begin; + while (node!= null) { + if (node.getParentNode() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Node " + node + " is not a child of " + this); + } + node.setParentNode(null); + childCount--; + if (node == end) break; + node = node.getNextSibling(); + } + + + if (node != end) { + throw new RuntimeException("Node " + end + " is not a right-sibling of " + begin); + } + + if (begin.getPreviousSibling() != null) { + begin.getPreviousSibling().setNextSibling(end.getNextSibling()); + } + if (end.getNextSibling() != null) { + end.getNextSibling().setPreviousSibling(begin.getPreviousSibling()); + } + if (getFirstChild() == begin) { + setFirstChildInternal(end.getNextSibling()); + } + if (getLastChild() == end) { + setLastChildInternal(begin.getPreviousSibling()); + } + } + + public void removeChildren() { + if (getFirstChild() != null) { + removeChildren(getFirstChild(), getLastChild()); + } + } + + public int getChildCount() { + return childCount; + } + + /** + * Inserts the node newChild after the existing child node refChild. + * If refChild is null, insert newChild at the beginning of the list of children. + * + * @param newChild The node to insert + * @param refChild The reference node, i.e., the node before which the new node must be inserted + * @return + */ +// public ASTNode insertAfter(ASTNode newChild, ASTNode refChild) { +// if (refChild == null) { +// refChild = getFirstChild(); +// } else { +// refChild = refChild.getNextSibling(); +// } +// return insertBefore(newChild, refChild); +// } + + /** + * Inserts the node newChild before the existing child node refChild. + * If refChild is null, insert newChild at the end of the list of children. + * + * @param newChild The node to insert + * @param refChild The reference node, i.e., the node before which the new node must be inserted + * @return + */ + public ASTNode insertBefore(ASTNode newChild, ASTNode refChild) { + if (refChild == null) { + return appendChild(newChild); + } + + Log.getLogger().debug("Inserting " + newChild + " before " + refChild + " in " + this); + + if (refChild.getParentNode() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, "Reference " + refChild + " is not a child of " + this); + } + + unlink(newChild); + + if (refChild.getPreviousSibling() != null) { + refChild.getPreviousSibling().setNextSibling(newChild); + } + newChild.setPreviousSibling(refChild.getPreviousSibling()); + newChild.setNextSibling(refChild); + newChild.setParentNode(this); + childCount++; + + refChild.setPreviousSibling(newChild); + + if (getFirstChild() == refChild) { + setFirstChildInternal(newChild); + } + + return newChild; + } + + public void addStatements(java.util.List statements) { + for (int i=0; i "; + if (target != null) { + Exception e = new Exception(); + if (target == this) { + s += "self"; + } else if (e.getStackTrace().length>20) { + s += "..."; + } else { + s += "" + target.toString(); + } + } else { + s += "null"; + } + return s; + } + /** + * @return Returns the target. + */ + public ASTNode getTarget() { + return target; + } + /** + * @param theTarget The target to set. + */ + public void setTarget(ASTNode theTarget) { + target = theTarget; + if (theTarget != null) targetIndex = theTarget.getBeginIndex(); + } +} diff --git a/src/main/java/com/j2js/dom/BreakStatement.java b/src/main/java/com/j2js/dom/BreakStatement.java new file mode 100644 index 0000000..d9b4c46 --- /dev/null +++ b/src/main/java/com/j2js/dom/BreakStatement.java @@ -0,0 +1,19 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +public class BreakStatement extends LabeledJump { + + public BreakStatement(String theLabel) { + super(theLabel); + } + + public BreakStatement(Block block) { + super(block); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/src/main/java/com/j2js/dom/CastExpression.java b/src/main/java/com/j2js/dom/CastExpression.java new file mode 100644 index 0000000..16afe3b --- /dev/null +++ b/src/main/java/com/j2js/dom/CastExpression.java @@ -0,0 +1,31 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author kuehn + */ +public class CastExpression extends Expression { + + private Expression expression; + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @param theExpression The expression to set. + */ + public void setExpression(Expression theExpression) { + widen(theExpression); + expression = theExpression; + } + + /** + * @return Returns the expression. + */ + public Expression getExpression() { + return expression; + } + +} diff --git a/src/main/java/com/j2js/dom/CatchClause.java b/src/main/java/com/j2js/dom/CatchClause.java new file mode 100644 index 0000000..963857d --- /dev/null +++ b/src/main/java/com/j2js/dom/CatchClause.java @@ -0,0 +1,45 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + + +/* + * CatchStatement.java + * + * Created on 22. Mai 2004, 22:49 + */ + +/** + * + * @author kuehn + */ +public class CatchClause extends Block { + + private VariableDeclaration exception; + + /** Creates a new instance of CatchStatement */ + //public CatchStatement() { + //} + + public CatchClause(int theBeginIndex) { + super(theBeginIndex); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the exception. + */ + public VariableDeclaration getException() { + return exception; + } + + /** + * @param theException The exception to set. + */ + public void setException(VariableDeclaration theException) { + exception = theException; + } +} diff --git a/src/main/java/com/j2js/dom/ClassInstanceCreation.java b/src/main/java/com/j2js/dom/ClassInstanceCreation.java new file mode 100644 index 0000000..1529fa4 --- /dev/null +++ b/src/main/java/com/j2js/dom/ClassInstanceCreation.java @@ -0,0 +1,24 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.ObjectType; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class ClassInstanceCreation extends MethodInvocation { + + public ClassInstanceCreation(ObjectType theType) { + type = theType; + } + + public ClassInstanceCreation(MethodDeclaration methodDecl, MethodBinding methodBinding) { + super(methodDecl, methodBinding); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/src/main/java/com/j2js/dom/ClassLiteral.java b/src/main/java/com/j2js/dom/ClassLiteral.java new file mode 100644 index 0000000..29221aa --- /dev/null +++ b/src/main/java/com/j2js/dom/ClassLiteral.java @@ -0,0 +1,22 @@ +package com.j2js.dom; + +import com.j2js.assembly.Signature; +import com.j2js.visitors.AbstractVisitor; + +public class ClassLiteral extends Expression { + + private Signature signature; + + public ClassLiteral(Signature theSignature) { + signature = theSignature; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public Signature getSignature() { + return signature; + } + +} diff --git a/src/main/java/com/j2js/dom/ConditionalBranch.java b/src/main/java/com/j2js/dom/ConditionalBranch.java new file mode 100644 index 0000000..b747329 --- /dev/null +++ b/src/main/java/com/j2js/dom/ConditionalBranch.java @@ -0,0 +1,41 @@ +/* + * Created on Feb 13, 2005 + */ +package com.j2js.dom; + +/** + * Copyright by Wolfgang Kuehn 2005 + */ +public class ConditionalBranch extends Branch { + + private Expression expression; + + public ConditionalBranch(int targetIndex) { + super(targetIndex); + } + + /** + * This constructor is for testing purposes only!! + */ + public ConditionalBranch(int theBeginIndex, int theEndIndex, int targetIndex) { + super(targetIndex); + setExpression(new Expression(theBeginIndex, theEndIndex)); + } + + /** + * @return Returns the condition. + */ + public Expression getExpression() { + return expression; + } + + /** + * @param condition The condition to set. + */ + public void setExpression(Expression theExpression) { + expression = theExpression; + widen(theExpression); + appendChild(theExpression); + } + +} diff --git a/src/main/java/com/j2js/dom/ConditionalExpression.java b/src/main/java/com/j2js/dom/ConditionalExpression.java new file mode 100644 index 0000000..175d1c9 --- /dev/null +++ b/src/main/java/com/j2js/dom/ConditionalExpression.java @@ -0,0 +1,61 @@ +/* + * Created on Sep 10, 2005 + */ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class ConditionalExpression extends Expression { + + private Expression conditionExpression = null; + private Expression thenExpression = null; + private Expression elseExpression = null; + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the conditionExpression. + */ + public Expression getConditionExpression() { + return conditionExpression; + } + /** + * @param theConditionExpression The conditionExpression to set. + */ + public void setConditionExpression(Expression theConditionExpression) { + widen(theConditionExpression); + conditionExpression = theConditionExpression; + } + /** + * @return Returns the elseExpression. + */ + public Expression getElseExpression() { + return elseExpression; + } + /** + * @param theElseExpression The elseExpression to set. + */ + public void setElseExpression(Expression theElseExpression) { + widen(theElseExpression); + elseExpression = theElseExpression; + } + /** + * @return Returns the thenExpression. + */ + public Expression getThenExpression() { + return thenExpression; + } + /** + * @param theThenExpression The thenExpression to set. + */ + public void setThenExpression(Expression theThenExpression) { + widen(theThenExpression); + thenExpression = theThenExpression; + } + +} diff --git a/src/main/java/com/j2js/dom/ContinueStatement.java b/src/main/java/com/j2js/dom/ContinueStatement.java new file mode 100644 index 0000000..a28ede9 --- /dev/null +++ b/src/main/java/com/j2js/dom/ContinueStatement.java @@ -0,0 +1,16 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +public class ContinueStatement extends LabeledJump { + + public ContinueStatement(Block block) { + super(block); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +} + diff --git a/src/main/java/com/j2js/dom/DoStatement.java b/src/main/java/com/j2js/dom/DoStatement.java new file mode 100644 index 0000000..b3bb863 --- /dev/null +++ b/src/main/java/com/j2js/dom/DoStatement.java @@ -0,0 +1,32 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/* + * DoStatement.java + * + * Created on 21. Mai 2004, 17:30 + */ + +/** + * + * @author kuehn + */ +public class DoStatement extends LoopStatement { + + public DoStatement() { + super(); + } + + public DoStatement(int theBeginIndex) { + super(theBeginIndex); + } + + public DoStatement(int theBeginIndex, int theEndIndex) { + super(theBeginIndex, theEndIndex); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/com/j2js/dom/Expression.java b/src/main/java/com/j2js/dom/Expression.java new file mode 100644 index 0000000..032bbf0 --- /dev/null +++ b/src/main/java/com/j2js/dom/Expression.java @@ -0,0 +1,52 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.Form; + +/** + * Copyright by Wolfgang Kuehn 2005 + * Created on Feb 17, 2005 + */ +public class Expression extends Block implements Cloneable { + + Type type = Type.UNKNOWN; + + public Expression() { + super(); + } + + public Expression(int theBeginIndex, int theEndIndex) { + super(theBeginIndex, theEndIndex); + } + + public Object clone() { + if (getChildCount() > 0) throw new RuntimeException("Cannot clone expression with children"); + ASTNode node; + try { + node = (ASTNode) super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + node.setParentNode(null); + node.setPreviousSibling(null); + node.setNextSibling(null); + return node; + } + + public Type getTypeBinding() { + return type; + } + + public void setTypeBinding(Type theType) { + type = theType; + } + + public int getCategory() { + if (getTypeBinding().getType() == Type.LONG.getType()) return Form.CATEGORY2; + if (getTypeBinding().getType() == Type.DOUBLE.getType()) return Form.CATEGORY2; + return Form.CATEGORY1; + //return getTypeBinding().getType() == Type.LONG.getType() || getTypeBinding().getType() == Type.DOUBLE.getType()?Form.CATEGORY2:Form.CATEGORY1; + } + +} diff --git a/src/main/java/com/j2js/dom/FieldAccess.java b/src/main/java/com/j2js/dom/FieldAccess.java new file mode 100644 index 0000000..08cb5a7 --- /dev/null +++ b/src/main/java/com/j2js/dom/FieldAccess.java @@ -0,0 +1,85 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.ObjectType; + +import com.j2js.assembly.Project; +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public abstract class FieldAccess extends Expression { + + private String name; + private ObjectType type; + //private MethodDeclaration methodDecl; + + public FieldAccess() { + } + + public void initialize(MethodDeclaration methodDecl) { + Project.getSingleton().addReference(methodDecl, this); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +// if (!faa.getName().equals(fab.getName())) return false; +// + public boolean isSame(Object obj) { + if (!(obj instanceof FieldAccess)) return false; + FieldAccess other = (FieldAccess) obj; + if (!name.equals(other.name)) return false; + if (getExpression() instanceof VariableBinding && other.getExpression() instanceof VariableBinding) { + VariableBinding vba = (VariableBinding) getExpression(); + VariableBinding vbb = (VariableBinding) other.getExpression(); + return vba.getVariableDeclaration() == vbb.getVariableDeclaration(); + } + return false; + } + + /** + * @return Returns the expression. + */ + public Expression getExpression() { + return (Expression) getFirstChild(); + } + + /** + * @param expression The expression to set. + */ + public void setExpression(Expression expression) { + widen(expression); + removeChildren(); + appendChild(expression); + } + + /** + * @return Returns the name. + */ + public String getName() { + return name; + } + + /** + * @param theName The name to set. + */ + public void setName(String theName) { + name = theName; + } + + public String toString() { + return super.toString() + " " + name; + } + + public ObjectType getType() { + return type; + } + + public void setType(ObjectType theType) { + if (type != null) throw new RuntimeException("Type is already set"); + type = theType; + } + +} diff --git a/src/main/java/com/j2js/dom/FieldRead.java b/src/main/java/com/j2js/dom/FieldRead.java new file mode 100644 index 0000000..8e01ed9 --- /dev/null +++ b/src/main/java/com/j2js/dom/FieldRead.java @@ -0,0 +1,14 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +public class FieldRead extends FieldAccess { + + public FieldRead() { + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/src/main/java/com/j2js/dom/FieldWrite.java b/src/main/java/com/j2js/dom/FieldWrite.java new file mode 100644 index 0000000..27de586 --- /dev/null +++ b/src/main/java/com/j2js/dom/FieldWrite.java @@ -0,0 +1,14 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +public class FieldWrite extends FieldAccess implements Assignable { + + public FieldWrite() { + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/src/main/java/com/j2js/dom/Goto.java b/src/main/java/com/j2js/dom/Goto.java new file mode 100644 index 0000000..0fd9f0e --- /dev/null +++ b/src/main/java/com/j2js/dom/Goto.java @@ -0,0 +1,13 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 12.11.2005 + */ +package com.j2js.dom; + +/** + * @author wolfgang + */ +public class Goto extends ASTNode { + + +} diff --git a/src/main/java/com/j2js/dom/IfStatement.java b/src/main/java/com/j2js/dom/IfStatement.java new file mode 100644 index 0000000..3b2f62e --- /dev/null +++ b/src/main/java/com/j2js/dom/IfStatement.java @@ -0,0 +1,46 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author kuehn + */ +public class IfStatement extends Block { + + public IfStatement() { + super(); + } + + public Expression getExpression() { + return (Expression) getChildAt(0); + } + + public void setExpression(Expression expression) { + widen(expression); + setChildAt(0, expression); + } + + public Block getIfBlock() { + return (Block) getChildAt(1); + } + + public void setIfBlock(Block block) { + widen(block); + setChildAt(1, block); + } + + public Block getElseBlock() { + if (getChildCount() < 3) return null; + return (Block) getChildAt(2); + } + + public void setElseBlock(Block block) { + widen(block); + setChildAt(2, block); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + +} diff --git a/src/main/java/com/j2js/dom/InfixExpression.java b/src/main/java/com/j2js/dom/InfixExpression.java new file mode 100644 index 0000000..473097d --- /dev/null +++ b/src/main/java/com/j2js/dom/InfixExpression.java @@ -0,0 +1,111 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author kuehn + */ +public class InfixExpression extends Expression { + + static public class Operator { + static public Operator CONDITIONAL_AND = new Operator("&&"); + static public Operator CONDITIONAL_OR = new Operator("||"); + static public Operator PLUS = new Operator("+"); + static public Operator MINUS = new Operator("-"); + static public Operator TIMES = new Operator("*"); + static public Operator DIVIDE = new Operator("/"); + static public Operator REMAINDER = new Operator("%"); + static public Operator XOR = new Operator("^"); + static public Operator AND = new Operator("&"); + static public Operator OR = new Operator("|"); + static public Operator EQUALS = new Operator("=="); + static public Operator NOT_EQUALS = new Operator("!="); + static public Operator GREATER_EQUALS = new Operator(">="); + static public Operator GREATER = new Operator(">"); + static public Operator LESS_EQUALS = new Operator("<="); + static public Operator LESS = new Operator("<"); + static public Operator RIGHT_SHIFT_SIGNED = new Operator(">>"); + static public Operator LEFT_SHIFT = new Operator("<<"); + static public Operator RIGHT_SHIFT_UNSIGNED = new Operator(">>>"); + + static { + EQUALS.complement = NOT_EQUALS; + NOT_EQUALS.complement = EQUALS; + GREATER_EQUALS.complement = LESS; + GREATER.complement = LESS_EQUALS; + LESS_EQUALS.complement = GREATER; + LESS.complement = GREATER_EQUALS; + CONDITIONAL_AND.complement = CONDITIONAL_OR; + CONDITIONAL_OR.complement = CONDITIONAL_AND; + } + + private String token; + private Operator complement; + + Operator(String theToken) { + token = theToken; + } + + public String toString() { + return token; + } + + /** + * @return Returns the complement. + */ + public Operator getComplement() { + return complement; + } + } + + private Operator operator; + + public InfixExpression(Operator op) { + super(); + operator = op; + if (operator.getComplement() != null) type = org.apache.bcel.generic.Type.BOOLEAN; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the leftOperand. + */ + public Expression getLeftOperand() { + return (Expression) getChildAt(0); + } + + /** + * @param leftOperand The leftOperand to set. + */ + public void setOperands(Expression leftOperand, Expression rightOperand) { + widen(leftOperand); + widen(rightOperand); + removeChildren(); + appendChild(leftOperand); + appendChild(rightOperand); + } + + /** + * @return Returns the rightOperand. + */ + public Expression getRightOperand() { + return (Expression) getChildAt(1); + } + + /** + * @return Returns the operator. + */ + public Operator getOperator() { + return operator; + } + + /** + * @param theOperator The operator to set. + */ + public void setOperator(Operator theOperator) { + operator = theOperator; + } +} diff --git a/src/main/java/com/j2js/dom/InstanceofExpression.java b/src/main/java/com/j2js/dom/InstanceofExpression.java new file mode 100644 index 0000000..777ce1e --- /dev/null +++ b/src/main/java/com/j2js/dom/InstanceofExpression.java @@ -0,0 +1,31 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.visitors.AbstractVisitor; + +/** + * Copyright by Wolfgang Kuehn 2005 + * Created on Feb 27, 2005 + */ +public class InstanceofExpression extends Expression { + private Expression leftOperand; + private Type rightOperand; + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public Expression getLeftOperand() { + return leftOperand; + } + public void setLeftOperand(Expression theLeftOperand) { + leftOperand = theLeftOperand; + } + public Type getRightOperand() { + return rightOperand; + } + public void setRightOperand(Type theRightOperand) { + rightOperand = theRightOperand; + } +} diff --git a/src/main/java/com/j2js/dom/Jump.java b/src/main/java/com/j2js/dom/Jump.java new file mode 100644 index 0000000..35cbaf0 --- /dev/null +++ b/src/main/java/com/j2js/dom/Jump.java @@ -0,0 +1,20 @@ +package com.j2js.dom; + +/** + * @author wolfgang + */ +public class Jump extends Branch { + + public Jump() { + super(); + } + + public Jump(int targetIndex) { + super(targetIndex); + } + +// public Jump(int beginIndex, int endIndex, int targetIndex) { +// this(targetIndex); +// setRange(beginIndex, endIndex); +// } +} diff --git a/src/main/java/com/j2js/dom/JumpSubRoutine.java b/src/main/java/com/j2js/dom/JumpSubRoutine.java new file mode 100644 index 0000000..25c9102 --- /dev/null +++ b/src/main/java/com/j2js/dom/JumpSubRoutine.java @@ -0,0 +1,12 @@ +package com.j2js.dom; + +/** + * @author wolfgang + */ +public class JumpSubRoutine extends Branch { + + public JumpSubRoutine(int targetIndex) { + super(targetIndex); + } + +} diff --git a/src/main/java/com/j2js/dom/LabeledJump.java b/src/main/java/com/j2js/dom/LabeledJump.java new file mode 100644 index 0000000..cb89347 --- /dev/null +++ b/src/main/java/com/j2js/dom/LabeledJump.java @@ -0,0 +1,20 @@ +package com.j2js.dom; + +abstract public class LabeledJump extends Jump { + + String label; + + public LabeledJump(String newLabel) { + super(); + label = newLabel; + } + + public LabeledJump(Block block) { + super(); + label = block.setLabeled(); + } + + public String getLabel() { + return label; + } +} diff --git a/src/main/java/com/j2js/dom/LoopStatement.java b/src/main/java/com/j2js/dom/LoopStatement.java new file mode 100644 index 0000000..548d0be --- /dev/null +++ b/src/main/java/com/j2js/dom/LoopStatement.java @@ -0,0 +1,44 @@ +package com.j2js.dom; + +/* + * LoopStatement.java + * + * Created on 21. Mai 2004, 16:51 + */ + +/** + * + * @author kuehn + */ +public class LoopStatement extends Block { + + public LoopStatement() { + super(); + } + + public LoopStatement(int theBeginIndex) { + super(theBeginIndex); + } + + public LoopStatement(int theBeginIndex, int theEndIndex) { + super(theBeginIndex, theEndIndex); + } + + public Expression getExpression() { + return (Expression) getChildAt(1); + } + + public void setExpression(Expression expression) { + widen(expression); + setChildAt(1, expression); + } + + public Block getBlock() { + return (Block) getChildAt(0); + } + + public void setBlock(Block block) { + widen(block); + setChildAt(0, block); + } +} diff --git a/src/main/java/com/j2js/dom/MethodBinding.java b/src/main/java/com/j2js/dom/MethodBinding.java new file mode 100644 index 0000000..d074c0e --- /dev/null +++ b/src/main/java/com/j2js/dom/MethodBinding.java @@ -0,0 +1,120 @@ +/* + * Created on 29.01.2006 + * Copyright Wolfgang Kuehn 2005 + */ +package com.j2js.dom; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.bcel.Constants; +import org.apache.bcel.classfile.ConstantCP; +import org.apache.bcel.classfile.ConstantNameAndType; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.generic.ObjectType; +import org.apache.bcel.generic.Type; + +import com.j2js.Utils; + +/** + * @author kuehn + */ +public class MethodBinding { + + private static Map methodBindingsByKey = new HashMap(); + + public static MethodBinding lookup(int index, ConstantPool constantPool) { + ConstantCP methodRef = (ConstantCP) constantPool.getConstant(index); + ConstantNameAndType nameAndType = (ConstantNameAndType) + constantPool.getConstant(methodRef.getNameAndTypeIndex(), Constants.CONSTANT_NameAndType); + + String name = nameAndType.getName(constantPool); + String signature = nameAndType.getSignature(constantPool); + + return lookup(methodRef.getClass(constantPool), name, signature); + } + + public static MethodBinding lookup(String className, String name, String signature) { + String key = className + "#" + name + signature; + + MethodBinding binding = methodBindingsByKey.get(key); + if (binding != null) return binding; + + binding = new MethodBinding(); + binding.declaringClass = new ObjectType(className); + binding.name = name; + binding.parameterTypes = Type.getArgumentTypes(signature); + binding.returnType = Type.getReturnType(signature); + binding.signature = signature; + + methodBindingsByKey.put(key, binding); + + return binding; + } + + private ObjectType declaringClass; + private String name; + private Type[] parameterTypes; + private Type returnType; + private String signature; + + private MethodBinding() { + } + + /** + * Returns the type binding representing the class or interface that declares this method or constructor. + */ + public ObjectType getDeclaringClass() { + return declaringClass; + } + + /** + * Returns the name of the method declared in this binding. + */ + public String getName() { + return name; + } + + /** + * Returns a list of type bindings representing the formal parameter types, in declaration order, of this method or constructor. + */ + public Type[] getParameterTypes() { + return parameterTypes; + } + + /** + * Returns the binding for the return type of this method. + */ + public Type getReturnType() { + return returnType; + } + + /** + * Returns whether this binding is for a constructor or a method. + */ + public boolean isConstructor() { + return "".equals(name); + } + + public String getSignature() { + return signature; + } + + public String toString() { + return getDeclaringClass().getClassName() + "#" + getRelativeSignature(); + } + + public String getRelativeSignature() { + String signature = getName() + "("; + String sep = ""; + for (int i = 0; i < getParameterTypes().length; i++) { + Type type = getParameterTypes()[i]; + signature += sep + Utils.getSignature(type); + sep = ","; + } + signature += ")"; + signature += Utils.getSignature(returnType); + return signature; + } + +} diff --git a/src/main/java/com/j2js/dom/MethodDeclaration.java b/src/main/java/com/j2js/dom/MethodDeclaration.java new file mode 100644 index 0000000..b79efc3 --- /dev/null +++ b/src/main/java/com/j2js/dom/MethodDeclaration.java @@ -0,0 +1,134 @@ +package com.j2js.dom; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.bcel.classfile.Code; +import org.apache.bcel.generic.Type; + +import com.j2js.LineNumberCursor; +import com.j2js.assembly.Project; +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class MethodDeclaration extends ASTNode { + + private String tempPrefix = "_"; + private Block block; + private Map parameters = new LinkedHashMap(); + private Map localVariables = new HashMap(); + private int accessFlags; + private Code code; + private MethodBinding methodBinding; + private LineNumberCursor lineNumberCursor; + + public MethodDeclaration(MethodBinding theMethodBinding, int theAccessFlags, Code theCode) { + methodBinding = theMethodBinding; + accessFlags = theAccessFlags; + code = theCode; + lineNumberCursor = new LineNumberCursor(code); + Project.getSingleton().getOrCreateProcedureUnit(methodBinding); + } + + public int getAccess() { + return accessFlags; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public boolean isInstanceConstructor() { + return methodBinding.getName().equals(""); + } + + /** + * @return Returns the block. + */ + public Block getBody() { + return block; + } + + /** + * @param theBlock The block to set. + */ + public void setBody(Block theBlock) { + block = theBlock; + theBlock.setParentNode(this); + } + + public VariableBinding createVariableBinding(String name, Type type, boolean isWrite) { + if (type == null) throw new NullPointerException(); + + VariableDeclaration decl = getParameter(name); + if (decl == null) { + decl = getLocalVariable(name); + } + if (decl == null) { + decl = new VariableDeclaration(!isWrite); + decl.setName(name); + decl.setType(type); + addLocalVariable(decl); + } + + VariableBinding binding = new VariableBinding(decl); + + return binding; + } + + private int vbCount = 0; + public VariableBinding createAnonymousVariableBinding(Type type, boolean isWrite) { + String name = tempPrefix + (vbCount++); + VariableBinding vb = createVariableBinding(name, type, isWrite); + vb.setTemporary(true); + return vb; + } + + public void addParameter(VariableDeclaration variableDecl) { + parameters.put(variableDecl.getName(), variableDecl); + } + + public Collection getParameters() { + return parameters.values(); + } + + public VariableDeclaration getParameter(String name) { + return parameters.get(name); + } + + public void addLocalVariable(VariableDeclaration variableDecl) { + localVariables.put(variableDecl.getName(), variableDecl); + } + + public void removeLocalVariable(String name) { + localVariables.remove(name); + } + + public Collection getLocalVariables() { + return localVariables.values(); + } + + public VariableDeclaration getLocalVariable(String name) { + return localVariables.get(name); + } + + public MethodBinding getMethodBinding() { + return methodBinding; + } + + public String toString() { + return methodBinding.toString(); + } + + public Code getCode() { + return code; + } + + public LineNumberCursor getLineNumberCursor() { + return lineNumberCursor; + } +} diff --git a/src/main/java/com/j2js/dom/MethodInvocation.java b/src/main/java/com/j2js/dom/MethodInvocation.java new file mode 100644 index 0000000..d3e8e2e --- /dev/null +++ b/src/main/java/com/j2js/dom/MethodInvocation.java @@ -0,0 +1,118 @@ +package com.j2js.dom; + +import java.util.List; + +import org.apache.bcel.generic.Type; + +import com.j2js.ASTNodeStack; +import com.j2js.assembly.Project; +import com.j2js.visitors.AbstractVisitor; + +/** + * @author kuehn + */ +public class MethodInvocation extends Expression { + + private Expression expression; + private MethodDeclaration methodDecl; + + /** + * Special handling for superclass, private, and instance initialization method invocations. + */ + public boolean isSpecial = false; + private MethodBinding methodBinding; + + public MethodInvocation() { + } + + public MethodInvocation(MethodDeclaration theMethodDecl) { + methodDecl = theMethodDecl; + } + + public MethodInvocation(MethodDeclaration theMethodDecl, MethodBinding theMethodBinding) { + methodDecl = theMethodDecl; + setMethodBinding(theMethodBinding); + } + + public Type getTypeBinding() { + if (methodBinding == null) return super.getTypeBinding(); + return methodBinding.getReturnType(); + } + + /** + * Returns true if this method invocation applies to a super class of the specified class. + */ + public boolean isSuper(String currentClassName) { + if (!isSpecial) return false; + // Use resolved class unless + // 1) the resolved method is not an instance initialization method, + if (methodBinding.isConstructor()) return false; + // 2) and the class of the resolved method is a superclass of the current class and + String name = methodBinding.getDeclaringClass().getClassName(); + if (currentClassName.equals(name)) return false; + + // TODO: The resolved class is different from the current class, but this does not imply that + // the resolved class is a superclass! Problem: How do we get this information without loading + // the class hierarchy? + return true; + } + + /** + * @return Returns the arguments. + */ + public List getArguments() { + ASTNodeStack stack = new ASTNodeStack(); + ASTNode node = getFirstChild(); + if (expression != null) { + node = node.getNextSibling(); + } + + while (node != null) { + stack.add(node); + node = node.getNextSibling(); + } + + return stack; + + } + + /** + * @param arguments The arguments to set. + */ + public void addArgument(Expression argument) { + widen(argument); + appendChild(argument); + } + + /** + * @return Returns the expression. + */ + public Expression getExpression() { + return expression; + } + + /** + * @param expression The expression to set. + */ + public void setExpression(Expression targetExpression) { + if (expression != null) { + throw new RuntimeException("Expression is already set"); + } + expression = targetExpression; + widen(expression); + insertBefore(expression, getFirstChild()); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public MethodBinding getMethodBinding() { + return methodBinding; + } + + public void setMethodBinding(MethodBinding theMethodBinding) { + methodBinding = theMethodBinding; + Project.getSingleton().addReference(methodDecl, this); + } +} diff --git a/src/main/java/com/j2js/dom/Name.java b/src/main/java/com/j2js/dom/Name.java new file mode 100644 index 0000000..87d5911 --- /dev/null +++ b/src/main/java/com/j2js/dom/Name.java @@ -0,0 +1,39 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * Copyright by Wolfgang Kuehn 2005 + * Created on Feb 20, 2005 + */ +public class Name extends Expression { + + private String identifier; + + public Name(String newIdentifier) { + //super(); + identifier = newIdentifier; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public boolean equals(Object obj) { + if (!(obj instanceof Name)) return false; + return identifier.equals(((Name) obj).identifier); + } + + /** + * @return Returns the identifier. + */ + public String getIdentifier() { + return identifier; + } + /** + * @param theIdentifier The identifier to set. + */ + public void setIdentifier(String theIdentifier) { + identifier = theIdentifier; + } +} diff --git a/src/main/java/com/j2js/dom/NoOperation.java b/src/main/java/com/j2js/dom/NoOperation.java new file mode 100644 index 0000000..02a9700 --- /dev/null +++ b/src/main/java/com/j2js/dom/NoOperation.java @@ -0,0 +1,11 @@ +/* + * Created on Oct 31, 2004 + */ +package com.j2js.dom; + +/** + * @author wolfgang + */ +public class NoOperation extends ASTNode { + // Tagging class. +} diff --git a/src/main/java/com/j2js/dom/NullLiteral.java b/src/main/java/com/j2js/dom/NullLiteral.java new file mode 100644 index 0000000..bb27e0d --- /dev/null +++ b/src/main/java/com/j2js/dom/NullLiteral.java @@ -0,0 +1,13 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class NullLiteral extends Expression { + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/com/j2js/dom/NumberLiteral.java b/src/main/java/com/j2js/dom/NumberLiteral.java new file mode 100644 index 0000000..8667c80 --- /dev/null +++ b/src/main/java/com/j2js/dom/NumberLiteral.java @@ -0,0 +1,91 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class NumberLiteral extends Expression { + + public static Number ZERO = new Integer(0); + public static Number ONE = new Integer(1); + public static Number MINUS_ONE = new Integer(-1); + + private Number value; + + public static boolean isZero(Expression expr) { + if (expr == null || !(expr instanceof NumberLiteral)) return false; + return ((NumberLiteral) expr).getValue().equals(ZERO); + } + + public static boolean isOne(Expression expr) { + if (expr == null || !(expr instanceof NumberLiteral)) return false; + return ((NumberLiteral) expr).getValue().equals(ONE); + } + + public static boolean isMinusOne(Expression expr) { + if (expr == null || !(expr instanceof NumberLiteral)) return false; + return ((NumberLiteral) expr).getValue().equals(MINUS_ONE); + } + + private NumberLiteral(Number theValue) { + value = theValue; + if (theValue instanceof Integer) { + type = Type.INT; + } else if (theValue instanceof Byte) { + type = Type.BYTE; + } else if (theValue instanceof Float) { + type = Type.FLOAT; + } else if (theValue instanceof Double) { + type = Type.DOUBLE; + } else if (theValue instanceof Long) { + type = Type.LONG; + } else if (theValue instanceof Short) { + type = Type.SHORT; + } else + // TODO: Other types + throw new RuntimeException("Type not supported: " + theValue.getClass()); + } + + public static NumberLiteral create(int i) { + Number value; + if (i == 0) value = ZERO; + else if (i == 1) value = ONE; + else value = new Integer(i); + return new NumberLiteral(value); + } + + public static NumberLiteral create(Number value) { + if (value instanceof Integer) { + Integer i = (Integer) value; + if (i.intValue()==0) value = ZERO; + else if (i.intValue()==1) value = ONE; + } + return new NumberLiteral(value); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the value. + */ + public Number getValue() { + return value; + } + + /** + * @param theValue The value to set. + */ + public void setValue(Number theValue) { + value = theValue; + } + + public String toString() { + return super.toString() + " value " + value; + } + +} diff --git a/src/main/java/com/j2js/dom/PStarExpression.java b/src/main/java/com/j2js/dom/PStarExpression.java new file mode 100644 index 0000000..a773e49 --- /dev/null +++ b/src/main/java/com/j2js/dom/PStarExpression.java @@ -0,0 +1,71 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * Class representing the union of pre- and post-fix expression. + * @author kuehn + */ +public class PStarExpression extends Expression { + + // Operators common to both pre- and post-fix expression. + static public Operator INCREMENT = new Operator("++"); + static public Operator DECREMENT = new Operator("--"); + + static public class Operator { + + private String token; + + Operator(String theToken) { + token = theToken; + } + + public String toString() { + return token; + } + + public Operator complement() { + if (this == PStarExpression.INCREMENT) return PStarExpression.DECREMENT; + else if (this == PStarExpression.DECREMENT) return PStarExpression.INCREMENT; + else throw new RuntimeException("Operation not supported for " + this); + } + + } + + private ASTNode operand; + private Operator operator; + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the operand. + */ + public ASTNode getOperand() { + return operand; + } + + /** + * @param leftOperand The leftOperand to set. + */ + public void setOperand(ASTNode theOperand) { + widen(theOperand); + operand = theOperand; + } + + /** + * @return Returns the operator. + */ + public Operator getOperator() { + return operator; + } + + /** + * @param theOperator The operator to set. + */ + public void setOperator(Operator theOperator) { + operator = theOperator; + } +} + diff --git a/src/main/java/com/j2js/dom/Pop.java b/src/main/java/com/j2js/dom/Pop.java new file mode 100644 index 0000000..fb26f17 --- /dev/null +++ b/src/main/java/com/j2js/dom/Pop.java @@ -0,0 +1,15 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 19.10.2005 + */ +package com.j2js.dom; + +/** + * @author wolfgang + */ +public class Pop extends Expression { + + public Pop(Expression e) { + //setRange(-1,-1); + } +} diff --git a/src/main/java/com/j2js/dom/PostfixExpression.java b/src/main/java/com/j2js/dom/PostfixExpression.java new file mode 100644 index 0000000..9a336c3 --- /dev/null +++ b/src/main/java/com/j2js/dom/PostfixExpression.java @@ -0,0 +1,18 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * Tagging class for a postfix expression. + * @author kuehn + */ +public class PostfixExpression extends PStarExpression { + + public PostfixExpression() { + super(); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/com/j2js/dom/PrefixExpression.java b/src/main/java/com/j2js/dom/PrefixExpression.java new file mode 100644 index 0000000..9f59b27 --- /dev/null +++ b/src/main/java/com/j2js/dom/PrefixExpression.java @@ -0,0 +1,25 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * Tagging class for a prefix expression. + * @author kuehn + */ +public class PrefixExpression extends PStarExpression { + + static public Operator NOT = new Operator("!"); +// static public Operator INCREMENT = new Operator("++"); +// static public Operator DECREMENT = new Operator("--"); + static public Operator MINUS = new Operator("-"); + static public Operator PLUS = new Operator("+"); + static public Operator COMPLEMENT = new Operator("~"); + + public PrefixExpression() { + super(); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/com/j2js/dom/PrimitiveCast.java b/src/main/java/com/j2js/dom/PrimitiveCast.java new file mode 100644 index 0000000..f7bef0c --- /dev/null +++ b/src/main/java/com/j2js/dom/PrimitiveCast.java @@ -0,0 +1,25 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.visitors.AbstractVisitor; + +public class PrimitiveCast extends Expression { + public int castType = 0; + public Expression expression; + + public PrimitiveCast(int theCastType, Expression expr, Type typeBinding) { + super(); + type = typeBinding; + castType = theCastType; + expression = expr; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public Expression getExpression() { + return expression; + } +} diff --git a/src/main/java/com/j2js/dom/Push.java b/src/main/java/com/j2js/dom/Push.java new file mode 100644 index 0000000..0abf448 --- /dev/null +++ b/src/main/java/com/j2js/dom/Push.java @@ -0,0 +1,16 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 19.10.2005 + */ +package com.j2js.dom; + +/** + * @author wolfgang + */ +public class Push extends Expression { + + public Push(Expression e) { + widen(e); + } + +} diff --git a/src/main/java/com/j2js/dom/ReturnStatement.java b/src/main/java/com/j2js/dom/ReturnStatement.java new file mode 100644 index 0000000..a4007af --- /dev/null +++ b/src/main/java/com/j2js/dom/ReturnStatement.java @@ -0,0 +1,44 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/* + * Created on Sep 12, 2004 + * + * To change the template for this generated file go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ + +/** + * @author wolfgang + * + * To change the template for this generated type comment go to + * Window - Preferences - Java - Code Generation - Code and Comments + */ +public class ReturnStatement extends ASTNode { + + private Expression expression; + + public ReturnStatement(int theBeginIndex, int theEndIndex) { + setRange(theBeginIndex, theEndIndex); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @param theExpression The expression to set. + */ + public void setExpression(Expression theExpression) { + widen(theExpression); + expression = theExpression; + } + + /** + * @return Returns the expression. + */ + public Expression getExpression() { + return expression; + } +} diff --git a/src/main/java/com/j2js/dom/StringLiteral.java b/src/main/java/com/j2js/dom/StringLiteral.java new file mode 100644 index 0000000..ec41551 --- /dev/null +++ b/src/main/java/com/j2js/dom/StringLiteral.java @@ -0,0 +1,37 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class StringLiteral extends Expression { + + private String value; + + public StringLiteral(String theValue) { + value = theValue; + type = Type.STRING; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the value. + */ + public String getValue() { + return value; + } + + /** + * @param theValue The value to set. + */ + public void setValue(String theValue) { + value = theValue; + } + +} diff --git a/src/main/java/com/j2js/dom/SwitchCase.java b/src/main/java/com/j2js/dom/SwitchCase.java new file mode 100644 index 0000000..c0e0528 --- /dev/null +++ b/src/main/java/com/j2js/dom/SwitchCase.java @@ -0,0 +1,38 @@ +/* + * Created on Oct 24, 2004 + */ +package com.j2js.dom; + +import java.util.List; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class SwitchCase extends Block { + + private List expressions; + + public SwitchCase(int theBeginIndex) { + super(theBeginIndex); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the expression. + */ + public List getExpressions() { + return expressions; + } + /** + * @param expression The expression to set. + */ + public void setExpressions(List theExpressions) { + expressions = theExpressions; + } + +} diff --git a/src/main/java/com/j2js/dom/SwitchStatement.java b/src/main/java/com/j2js/dom/SwitchStatement.java new file mode 100644 index 0000000..e49334c --- /dev/null +++ b/src/main/java/com/j2js/dom/SwitchStatement.java @@ -0,0 +1,40 @@ +/* + * Created on Oct 24, 2004 + */ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class SwitchStatement extends Block { + + private Expression expression; + + public SwitchStatement() { + super(); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public SwitchCase getDefault() { + return (SwitchCase) getLastChild(); + } + + /** + * @return Returns the expression. + */ + public Expression getExpression() { + return expression; + } + /** + * @param theExpression The expression to set. + */ + public void setExpression(Expression theExpression) { + widen(theExpression); + expression = theExpression; + } +} diff --git a/src/main/java/com/j2js/dom/SynchronizedBlock.java b/src/main/java/com/j2js/dom/SynchronizedBlock.java new file mode 100644 index 0000000..aeba418 --- /dev/null +++ b/src/main/java/com/j2js/dom/SynchronizedBlock.java @@ -0,0 +1,18 @@ +/* + * Copyright 2005 by Wolfgang Kuehn + * Created on 18.10.2005 + */ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class SynchronizedBlock extends Block { + public Expression monitor; + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/com/j2js/dom/ThisExpression.java b/src/main/java/com/j2js/dom/ThisExpression.java new file mode 100644 index 0000000..05d3e82 --- /dev/null +++ b/src/main/java/com/j2js/dom/ThisExpression.java @@ -0,0 +1,27 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author kuehn + */ +public class ThisExpression extends VariableBinding { + + private static VariableDeclaration vd; + + static { + vd = new VariableDeclaration(VariableDeclaration.NON_LOCAL); + vd.setName("this"); + vd.setType(Type.OBJECT); + } + + public ThisExpression() { + super(vd); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } +} diff --git a/src/main/java/com/j2js/dom/ThrowStatement.java b/src/main/java/com/j2js/dom/ThrowStatement.java new file mode 100644 index 0000000..dc179cb --- /dev/null +++ b/src/main/java/com/j2js/dom/ThrowStatement.java @@ -0,0 +1,27 @@ +package com.j2js.dom; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class ThrowStatement extends Block { + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the expression. + */ + public Expression getExpression() { + return (Expression) getFirstChild(); + } + /** + * @param expression The expression to set. + */ + public void setExpression(Expression expression) { + widen(expression); + appendChild(expression); + } +} diff --git a/src/main/java/com/j2js/dom/TryStatement.java b/src/main/java/com/j2js/dom/TryStatement.java new file mode 100644 index 0000000..79b0c47 --- /dev/null +++ b/src/main/java/com/j2js/dom/TryStatement.java @@ -0,0 +1,68 @@ +package com.j2js.dom; + +import com.j2js.cfg.TryHeaderNode; +import com.j2js.visitors.AbstractVisitor; + +/** + * TryStatement.java + * + * Created on 21. Mai 2004, 17:33 + * @author kuehn + */ +public class TryStatement extends Block { + + public TryHeaderNode header; + + /** Creates a new instance of TryStatement */ + public TryStatement() { + super(); + } + + public void addCatchStatement(CatchClause catchStatement) { + if (getChildCount() < 2) throw new RuntimeException("Illegal DOM state"); + ((Block) getChildAt(1)).appendChild(catchStatement); + } + + public Block getCatchStatements() { + return (Block) getChildAt(1); + } + + /** Getter for property finallyBlock. + * @return Value of property finallyBlock. + */ + public Block getFinallyBlock() { + if (getChildCount() < 3) return null; + return (Block) getChildAt(2); + } + + /** Setter for property finallyBlock. + * @param finallyBlock New value of property finallyBlock. + */ + public void setFinallyBlock(Block finallyBlock) { + setChildAt(2, finallyBlock); + } + + /** + * @return Returns the tryBlock. + */ + public Block getTryBlock() { + return (Block) getChildAt(0); + } + + /** + * @param tryBlock The tryBlock to set. + */ + public void setTryBlock(Block tryBlock) { + setChildAt(0, tryBlock); + setChildAt(1, new Block()); + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public String toString() { + return super.toString(); + } + +} diff --git a/src/main/java/com/j2js/dom/TypeDeclaration.java b/src/main/java/com/j2js/dom/TypeDeclaration.java new file mode 100644 index 0000000..823d6f4 --- /dev/null +++ b/src/main/java/com/j2js/dom/TypeDeclaration.java @@ -0,0 +1,121 @@ +/* + * Created on 22.10.2004 + */ +package com.j2js.dom; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.bcel.generic.ObjectType; + +import com.j2js.assembly.Project; +import com.j2js.visitors.AbstractVisitor; + + +/** + * @author kuehn + */ +public class TypeDeclaration extends ASTNode { + + private ObjectType type; + private ObjectType superType; + + private ArrayList methods = new ArrayList(); + //private MethodDeclaration initializer; + private List fields = new ArrayList(); + private int accessFlags; + //private String superClassName; + + public TypeDeclaration(ObjectType theType, int theAccessFlags) { + type = theType; + accessFlags = theAccessFlags; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + /** + * @return Returns the methods. + */ + public MethodDeclaration[] getMethods() { + MethodDeclaration[] a = new MethodDeclaration[methods.size()]; + return methods.toArray(a); + } + + public int getAccess() { + return accessFlags; + } + + /** + * @param methods The methods to set. + */ + public void addMethod(MethodDeclaration method) { + method.setParentNode(this); + methods.add(method); + } + + /** + * @return Returns the name. + */ + public ObjectType getType() { + return type; + } + + /** + * @return Returns the package portion of this types name. + */ + public String getPackageName() { + String name = type.getClassName(); + int index = name.lastIndexOf('.'); + if (index != -1) + return name.substring(0, index); + else + return name; + } + + public String getClassName() { + return type.getClassName(); + } + + public String getUnQualifiedName() { + String name = type.getClassName(); + int index = name.lastIndexOf('.'); + if (index != -1) + return name.substring(index+1); + else + return name; + } + + /** + * @return Returns the fields. + */ + public List getFields() { + return fields; + } + /** + * @param fields The fields to set. + */ + public void addField(VariableDeclaration field) { + fields.add(field); + Project.getSingleton().getOrCreateFieldUnit(type, field.getName()); + } + /** + * @return Returns the superClassName. + */ + public ObjectType getSuperType() { + return superType; + } + + /** + * Sets the super type. + */ + public void setSuperType(ObjectType newSuperType) { + superType = newSuperType; + } + + public String toString() { + return type.getClassName(); + } + +} diff --git a/src/main/java/com/j2js/dom/VariableBinding.java b/src/main/java/com/j2js/dom/VariableBinding.java new file mode 100644 index 0000000..c8ef7d3 --- /dev/null +++ b/src/main/java/com/j2js/dom/VariableBinding.java @@ -0,0 +1,83 @@ +package com.j2js.dom; + +import org.apache.bcel.generic.Type; + +import com.j2js.visitors.AbstractVisitor; + +/** + * @author wolfgang + */ +public class VariableBinding extends Expression implements Assignable { + private boolean field; + private VariableDeclaration decl; + private boolean isTemporary = false; + + public static boolean isBoolean(Expression expr) { + if (expr == null || !(expr instanceof VariableBinding)) return false; + if (((VariableBinding) expr).getVariableDeclaration().getType() != Type.BOOLEAN) return false; + return true; + } + + public VariableBinding(VariableDeclaration theDecl) { + super(); + decl = theDecl; + decl.vbs.add(this); + setTypeBinding(theDecl.getType()); + } + + public Object clone() { + // TODO: Make cloning explicit. + VariableBinding other = (VariableBinding) super.clone(); + decl.vbs.add(other); + return other; + } + + public void visit(AbstractVisitor visitor) { + visitor.visit(this); + } + + public boolean isSame(Object other) { + if (other == null || !(other instanceof VariableBinding)) return false; + return decl == ((VariableBinding) other).decl; + } + + /** + * @return Returns the name. + */ + public String getName() { + return decl.getName(); + } + + /** + * @return Returns the field. + */ + public boolean isField() { + return field; + } + + /** + * @param theField The field to set. + */ + public void setField(boolean theField) { + field = theField; + } + + /** + * @return Returns the decl. + */ + public VariableDeclaration getVariableDeclaration() { + return decl; + } + + public String toString() { + return super.toString() + ' ' + decl.getName(); + } + + public boolean isTemporary() { + return isTemporary; + } + + public void setTemporary(boolean isTemporary) { + this.isTemporary = isTemporary; + } +} diff --git a/src/main/java/com/j2js/dom/VariableDeclaration.java b/src/main/java/com/j2js/dom/VariableDeclaration.java new file mode 100644 index 0000000..20cddb9 --- /dev/null +++ b/src/main/java/com/j2js/dom/VariableDeclaration.java @@ -0,0 +1,111 @@ +package com.j2js.dom; + +import com.j2js.J2JSCompiler; + +import java.util.List; +import java.util.ArrayList; + +import com.j2js.visitors.AbstractVisitor; + +import org.apache.bcel.classfile.LocalVariable; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.generic.Type; + +/** + * @author wolfgang + */ +public class VariableDeclaration extends ASTNode { + public static int LOCAL = 0; + public static int NON_LOCAL = 1; + public static int LOCAL_PARAMETER = 2; + + private String name; + private Type type; + private int modifiers; + private int location; + private boolean isInitialized; + public List vbs = new ArrayList(); + + public static String getLocalVariableName(Method method, int slot, int pc) { + if (false && method.getLocalVariableTable() != null) { + // TODO: Use source variable name as comment only. + LocalVariable[] table = method.getLocalVariableTable().getLocalVariableTable(); + LocalVariable lvar = null; + for (int i=0; i + + + J2JS Package Documentation + + + + + + + + + +

+

+ This document describes the production patterns for byte code by the + jdk 1.4.2 compiler. +

+
+ +
+ An if statement is compiled as follows: +
+        cond(A) block A
+ + An if-else statement is compiled as follows: +
+        cond(A) block goto(B) A block B
+
+ +
+ +
+
+        aload         // Load object reference.
+        dup           // Duplicate object reference.
+        getfield      // Fetch field oldValue.
+        delta         // Can be any value, could even be an expression.
+        add/sub       // Increment/decrement value.
+                      // Stack: ref newValue
+        dup_x1        // Duplicate top and put it on third position.
+                      // Stack: newValue ref newValue.
+        putfield      // Store field value.
+                      // Stack: newValue
+ + Reduction: + ++field, --field, or (field = field + delta), if delta not equal to constant +/-1. +
+ +
+
+        aload         // Load object reference.
+        dup           // Duplicate object reference.
+        getfield      // Fetch field value.
+                      // Stack: ref oldValue
+        dup_x1        // Duplicate top and put it on third position.
+                      // Stack: oldValue ref oldValue
+        const         // Must be constant +/-1.
+        add/sub       // Increment/decrement value.
+                      // Stack: oldValue ref newValue
+        putfield      // Store field value.
+                      // Stack: oldValue.
+ + Reduction: + field++ or field--. +
+ +
+
+        iinc(delta)   // Increment variable.
+        load          // Load variable.
+                      // Stack: newValue
+
+ +
+
+        load          // Load variable. 
+        delta         // Any expression.
+        add           // Increment.
+        dup           // Stack: newValue newValue
+        store         // Store to variable.
+                      // Stack: newValue
+
+ +
+
+	 load          // Load variable.
+	 iinc(delta)   // Increment variable.
+	 // Stack: oldValue
+
+ +

+ Note that we could match the pattern [load, iinc, load] + with either (E) [i++, i] or (C) [i, ++i]. + Of these obviously equivalent alternatives we choose (E). +

+ +
+ +
+ +
+    While statement:
+       goto(B) A block B cond(A)
+ +
+    Do statement:
+       A block cond(A)
+      
+ +
+    For statement (equivalent to a do statement):
+       init A block increment cond(A)
+      
+ + +
+ +
+

+ In Java, a try statement has the following structure: +

+
+               try Block Catch* (Catch | Finally)
+      
+ +
+ + A try block with finally clause has the following structure: + +
+    TryCatchFinally:
+        tryBlock 
+        Handler  // 0..n times
+        DefaultHandler
+        B Finally 
+        A jsr(B) 
+	
+
+ + Each time the control flow exits the try block or one of the non-default handlers, + a jsr(B) is prepended. If the try block or one of the non-default + handlers are exited by default (fall-through), then a goto(A) is appended at the end of the block. + +
+ There must be at least one handler. All handlers are non-default. Typically, goreth is a goto(A), + but this must not be the case. +
+    TryCatchFinally:
+        tryBlock goreth  
+        Handler goreth  // 0..n
+        Handler
+        A
+	
+
+ +
+    DefaultHandler: (Any Exception)
+        astore_exception jsr(B) aload_exception athrow
+      
+ +
+    Handler:
+        astore_exception catchBlock
+      
+ +
+    Finally:
+        astore_address finallyBlock ret
+      
+ +

+ The store operations in the handlers save the exception to a local variable, whereas the store + operation at the beginning of the subroutine saves the return address to local variable. +

+

+ For each catch block, a codeException(BeginPC, EndPC, HandlerPC) is recorded, + where HandlerPC = begin(Default?Handler) and +

    +
  1. BeginPC = begin(tryBlock), EndPC = begin(first Handler)-1, i.e. including the goto(A) + for each non-default handler, or both +
  2. +
  3. + BeginPC = begin(tryBlock), EndPC = end(last Handler) and +
  4. +
  5. + BeginPC = A = begin(Tail), EndPC = end(Tail) for the default handler. +
  6. +
+ +

+ Note that from the byte code we cannot deduce the end of the last catch clause in the + absense of a finally clause. This is also manifested by the equivalence of the following + two Java code fracments: +

+
+    foo: {
+        try {
+           A;
+           break foo;
+        } catch(e) {
+           B;
+        }
+        C;
+    }
+    
+    foo: {
+        try {
+           A;
+           break foo;
+        } catch(e) {
+           B;
+           C;
+        }
+    }
+      
+
+ +
+
+ + +Last modified: Sat Oct 15 20:44:54 Westeuropäische Sommerzeit 2005 + + + diff --git a/src/main/java/com/j2js/util/BuilderUtils.java b/src/main/java/com/j2js/util/BuilderUtils.java new file mode 100644 index 0000000..79478e9 --- /dev/null +++ b/src/main/java/com/j2js/util/BuilderUtils.java @@ -0,0 +1,89 @@ +package com.j2js.util; + +import java.io.File; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.FileUtils; + + +public final class BuilderUtils { + + public static String removePrefixPath(File baseDir, File file) { + return baseDir.toURI().relativize(file.toURI()).toString(); + } + + private static String findGreatestCommonPrefix(String a, String b) { + int previousSlashIndex = -1; + int i; + for (i=0; i + + StringBuilder sb = new StringBuilder(); + sb.append("id\\s*=\\s*[\'\"]j2js-loader[\'\"].*src\\s*=\\s*[\'\"]([^\'\"]+)/0\\.js[\'\"]"); + + Pattern p = Pattern.compile(sb.toString(), Pattern.DOTALL | Pattern.CASE_INSENSITIVE); + return p.matcher(htmlContent); + } + + public static String extractScriptSrc(File htmlFile) throws IOException { + String htmlContent = FileUtils.readFileToString(htmlFile); + Matcher matcher = getProtectedRegion(htmlContent); + if (!matcher.find()) return null; + + String scriptSrc = htmlContent.substring(matcher.start(1), matcher.end(1)); + scriptSrc = scriptSrc.trim(); + + if (scriptSrc.length() == 0) return null; + return scriptSrc; + } + +} diff --git a/src/main/java/com/j2js/util/XmlBeanMapper.java b/src/main/java/com/j2js/util/XmlBeanMapper.java new file mode 100644 index 0000000..c048504 --- /dev/null +++ b/src/main/java/com/j2js/util/XmlBeanMapper.java @@ -0,0 +1,1001 @@ +package com.j2js.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.logging.Level; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import java.rmi.activation.ActivationException; + +/** + Class XmlBeanMapper offers a generic way to instanciate and initialize a JavaBean + from an Xml document. + +

Document Object Model

+ The Xml document corresponds to a hierachy of values. A value is + either a JavaBean or a primitive type. Each value except the root value + is contained by its parent value. + + The document root corresponds to the JavaBean. + Now we recursively apply the following rules: + +
    +
  1. An element or an attribute corresponds to a value if the enclosing value is a JavaBean + which has a setter of the same name. The value is created and passed to the setter. + An attribute can only be of primitive type. +
  2. +
  3. An element corresponds to a list of values if the enclosing value + is a JavaBean and has an adder of the same name in singular. +
  4. +
  5. An element corresponds to a value if the enclosing value is a list. + The value is created and passed to the adder of the enclosing value. +
  6. +
  7. The root element corresponds to the requested JavaBean. + The name of the root element is not relevant but should coincide with + the classname of the JavaBean. +
  8. +
+ +

Example

+ +
+  <person age="35">
+    <name>Scott Miller</name>
+    <addresses>
+      <address>
+        <street>Augusta Ave 7</street>
+      </address>
+      <address>
+        <street>Chestnut Grove 53</street>
+      </address>
+    </addresses>
+  </person>
+
+  public class Person {
+    public void setName(String name) {}
+    public void setAge(int age) {}
+    public void addAddress(Address address) {}
+  }
+
+  public class Address {
+    public void setStreet(String street) {};
+  }
+  
+ + It is up to the user of Class XmlBeanMapper to provide a proper implementation + for the mapped classes. + +

Supported Primitive Types

+ Supported primitive types are boolean, int, long, double and String. +

Furthermore the following datatypes are supported

+
    +
  • Dates (java.sql.Date and java.util.Date)
  • +
  • array of long, double, int and string
  • +
  • Double, Long, Integer
  • +
+

+ Valid values are + the same as the valid arguments to the one-parameter constructors + Boolean(String), Integer(String), Long(String), Double(String) and String(String) +

+ +

Advanced Usage

+ + By default, the parameter of the setter or adder determines the class of + the newly instanciated value. Of course, this will not work if the value + has to be a subclass or an implementing class to an interfaces. +

+ In these cases, the target class must be indicated by the metaInfClass + attribute of the corresponding element, for example + +

+  <address country="Germany" metaInfClass="OffshoreAddress">
+    <street>Berliner Platz 11</street>
+  </address>
+
+  public class OffshoreAddress extends Address {
+    public void setCountry(String country) {};
+  }
+  
+

+ +
+  <metaInf>
+    <types>
+      <type elementName="name of element" className="full name of class"/>
+      ...
+    </types>
+  </metaInf>
+  
+ +

Tips and Tricks

+ Furthermore it is possible to use the valuestack for instructing the beanmapper to use + some predefined values (setValueStack). + If you use
id="pop#5"
or +
the beanmapper will use the specified entry (in the example the fifth) of the + given hashmap (setValueStack()) for initializing the corresponding field or attribute. + Reference to constructor + Url trick with recursive mapping; Multiple mapping. + TODO: Complete documentation + + @deprecated + + */ +public class XmlBeanMapper { + + /** + * Structure to hold a key-value pair. + */ + private class BeanInfo { + Object bean; + String key; + + /** + * Constructor for the BeanInfo-Class. + * @param newKey the key + * @param newBean the bean + */ + BeanInfo(String newKey, Object newBean) { + key = newKey; + bean = newBean; + } + } + + public class MetaInf { + + public class Type { + String elementName; // Element name. + String className; + + public Type() { + super(); + } + + public void setElement(String newElementName) { + elementName = newElementName; + } + public void setClass(String newClassName) { + className = newClassName; + } + } + + private HashMap types = new HashMap(); + + public MetaInf() { + super(); + } + + public void addType(Type type) { + log(Level.FINEST, "Mapping element " + type.elementName + " to " + type.className); + types.put(type.elementName, type.className); + } + + public String getType(String elementName) { + return types.get(elementName); + } + + public java.util.Map getTypes() { + return types; + } + } + + private static final int MAX_LINE_LENGTH = 76; + + private HashMap valueStack = new HashMap(); + private Stack stack = new Stack(); + private boolean ignore = false; + private boolean trim = true; + private MetaInf metaInf; + private java.util.logging.Logger logger = null; + private java.io.PrintStream printStream = null; + private Level logLevel; + private SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + + /** + * Creates a new instance of XmlBeanMapper. + * + * @deprecated + */ + public XmlBeanMapper() { + super(); + } + + /** + * Allows to ignore values that cannot be matched. Default is false. + */ + public void setIgnoreMismatches(boolean theIgnore) { + ignore = theIgnore; + } + + /** + * If true, all String-typed values will have their leading and trailing whitespace omitted. + * Default is true. + */ + public void setTrimWhitespace(boolean b) { + trim = b; + } + + /** + * Sets the dateFormat used to parse the date values in the xml file. + * @param dateFormat the date format. Default is "dd.MM.yyyy HH:mm". + */ + public void setDateFormat(String dateFormat) { + dateFormatter = new SimpleDateFormat(dateFormat); + } + + /** + * Sets a specified stream for logging at a specified logging level. + */ + public void setLogStream(PrintStream newPrintStream, Level newLogLevel) { + printStream = newPrintStream; + logLevel = newLogLevel; + } + + /** + * Sets a specified stream for logging at a specified logging level. + */ + public void setLogFile(java.io.File file, Level level) throws IOException { + setLogStream(new java.io.PrintStream(new java.io.FileOutputStream(file)), level); + } + + /** + * Sets a specified logger for logging. + */ + public void setLogger(java.util.logging.Logger theLogger) { + logger = theLogger; + } + + public void setMetaInf(MetaInf theMetaInf) { + metaInf = theMetaInf; + } + + public MetaInf getMetaInf() { + return metaInf; + } + + /** + * See {@link #createBeanFromString(Class, String, Object) createBeanFromString} + */ + public Object createBeanFromString(Class clazz, String xml) + throws IOException, ActivationException { + return createBeanFromString(clazz, xml, null); + } + + /** + * Instanciate a bean for a given class from an Xml-string using a reference. + * If you already have an instance try loadBean. + * + * @param clazz the class to instanciate from + * @param xml the Xml-string + * @param refObject the reference object + * @return the created bean + */ + public Object createBeanFromString(Class clazz, String xml, Object refObject) + throws IOException, ActivationException { + // Log only about MAX_LINE_LENGTH characters wide. + String xmlString = xml; + if (xml.length() > MAX_LINE_LENGTH) { + xml.substring(0, MAX_LINE_LENGTH); + } + log(Level.INFO, "Creating " + clazz + " from string\n" + xmlString + " ..."); + return createBean(refObject, getDocumentElement(new java.io.ByteArrayInputStream(xml.getBytes())), + clazz).bean; + } + + public void loadBeanFromString(Object bean, String xml) + throws IOException, ActivationException { + Class clazz; + if (bean instanceof Class) { + clazz = (Class) bean; + bean = null; + } else { + clazz = bean.getClass(); + } + // Log only about 76 characters wide if string is longer than 76 chars + // otherwise log the whole string + int length = 0; + if (xml.length() >= MAX_LINE_LENGTH) { + length = MAX_LINE_LENGTH; + } else { + length = xml.length(); + } + log(Level.INFO, "Loading" + clazz + + " from string\n" + xml.substring(0, length) + + " ..."); + Element root = getDocumentElement(new java.io.ByteArrayInputStream(xml.getBytes())); + loadBean(clazz, bean, root); + } + + /** + * See {@link #createBeanFromFile(Class, String, Object) createBeanFromFile} + */ + public Object createBeanFromFile(Class clazz, String path) + throws IOException, ActivationException { + return createBeanFromFile(clazz, path, null); + } + + public Object createBeanFromFile(String path) + throws IOException, ActivationException { + return createBeanFromFile(null, path, null); + } + + /** + * Instanciate a bean for a given class from an Xml-file using a reference. + * + * @param clazz the class to instanciate from + * @param path the path to the Xml-file. + * @param refObject the reference object + */ + public Object createBeanFromFile(Class clazz, String path, Object refObject) + throws IOException, ActivationException { + Node document = getDocumentElement(new FileInputStream(path)); + if (clazz == null) { + String className = getMetaInfClass(document); + if (className == null) throw new ActivationException("Need to specify a class name with attribute 'metaInfClass'"); + try { + clazz = Thread.currentThread().getContextClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + throw new ActivationException("Class " + className + " not found"); + } + } + log(Level.INFO, "Creating " + clazz + " from file " + path); + BeanInfo beanInfo = createBean(refObject, document, clazz); + return beanInfo.bean; + } + + /** + * Load a file into a bean. + * + * @param bean the bean to load the file into. + * @param path the path of the file. + */ + public void loadBean(Object bean, String path) throws IOException, ActivationException { + Class clazz; + if (bean instanceof Class) { + clazz = (Class) bean; + bean = null; + } else { + clazz = bean.getClass(); + } + log(Level.INFO, "Loading " + clazz + " from file " + path); + Element root = getDocumentElement(new FileInputStream(path)); + loadBean(clazz, bean, root); + } + + /** + * Load a file into a bean. + * + * @param bean the bean to load the file into. + * @param stream an input stream + */ + public void loadBean(Object bean, java.io.InputStream stream) throws IOException, ActivationException { + Class clazz; + if (bean instanceof Class) { + clazz = (Class) bean; + bean = null; + } else { + clazz = bean.getClass(); + } + Element root = getDocumentElement(stream); + loadBean(clazz, bean, root); + } + + /** + * Load a file into a bean. + * + * @param bean the bean to load the file into. + * @param root the path of the file. + */ + public void loadBean(Object bean, Element root) throws ActivationException { + Class clazz; + if (bean instanceof Class) { + clazz = (Class) bean; + bean = null; + } else { + clazz = bean.getClass(); + } + log(Level.INFO, "Loading " + clazz + " from Dom element"); + loadBean(clazz, bean, root); + } + + /** + * Utility to find a setter for a given set of methods. If a Class is specified, + * then the setter will have an argument of that class. + * + * @param methods the methods + * @param name the name of the setter + * @param clazz the argument of the setter + * + * @return the index of the setter, or -1 if no suitable setter was found. + */ + public int getSetter(Method[] methods, String name, Class clazz) { + //name = name.substring(0,1).toUpperCase() + name.substring(1); + + name = name.toLowerCase(); + String setter = "set" + name; + int i = 0; + int stringSetter = -1; + int anySetter = -1; + + for (i = 0; i < methods.length; i++) { + String methodName = methods[i].getName().toLowerCase(); + if (!methodName.equals(setter)) continue; + + Class[] parameters = methods[i].getParameterTypes(); + if (parameters.length == 1) { + if (clazz != null && parameters[0].isAssignableFrom(clazz)) { + return i; + } + if (parameters[0].equals(String.class)) { + stringSetter = i; + } else { + anySetter = i; + } + } else if (parameters.length == 2 && parameters[0].equals(String.class)) { + // key-value setter + if (clazz != null && parameters[1].isAssignableFrom(clazz)) { + return i; + } + if (parameters[1].equals(String.class)) { + stringSetter = i; + } else { + anySetter = i; + } + } + } + if (stringSetter != -1) return stringSetter; + return anySetter; + } + + /** + * Utility to find an adder for a given set of methods. + * + * @param methods the methods + * @param name the name of the adder + * + * @return the index of the adder, or -1 if no suitable adder was found. + */ + public int getAdder(Method[] methods, String name, Class clazz) { + name = name.toLowerCase(); + String adder1; + String adder2; + /* + * If the name is a plural, then form a singular. For example, map + * DataSources to DataSource or Entries to Entry. + */ + if (name.endsWith("ies")) { + adder1 = name.substring(0, name.length() - 3) + "y"; + adder2 = ""; + } else { + adder1 = name.substring(0, name.length() - 1); + adder2 = name.substring(0, name.length() - 2); + } + int i = 0; + for (i = 0; i < methods.length; i++) { + String methodName = methods[i].getName().toLowerCase(); + if (clazz != null) { + Class[] parameters = methods[i].getParameterTypes(); + if (parameters.length != 1 || !parameters[0].isAssignableFrom(clazz)) { + continue; + } + } + if (methodName.startsWith("add")) { + String s = methodName.substring(3, methodName.length()); + if (s.equals(adder1) || s.equals(adder2)) { + return i; + } + } else if (methodName.equals(adder1) || methodName.equals(adder2)) { + return i; + } + } + return -1; + } + + protected void log(Level level, String msg) { + if (logger != null) { + logger.log(level, msg); + } + if (printStream != null && level.intValue() >= logLevel.intValue()) { + printStream.println(msg); + } + } + + private Element getDocumentElement(java.io.InputStream stream) throws IOException { + DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance(); + dfactory.setNamespaceAware(true); + + BufferedInputStream bStream = new BufferedInputStream(stream); + + Document document = null; + try { + document = dfactory.newDocumentBuilder().parse(bStream); + } catch (javax.xml.parsers.ParserConfigurationException e) { + throw new IOException(e.getMessage()); + } catch (org.xml.sax.SAXParseException e) { + throw new IOException(e.getMessage() + " in (line/column): (" + + e.getLineNumber() + "/" + e.getColumnNumber() + ")"); + } catch (org.xml.sax.SAXException e) { + throw new IOException(e.getMessage()); + } + + return document.getDocumentElement(); + } + + private String getMetaInfClass(Node node) { + if (node.getNodeType() != Node.ELEMENT_NODE) { + return null; + } + Node n = node.getAttributes().getNamedItem("metaInfClass"); + if (n == null) { + return null; + } + return n.getNodeValue(); + } + + /** + * Load an element node into a bean. + * + * @param bean the JavaBean to load into. + * @param element the element to load. + */ + private String loadBean(Class clazz, Object bean, Element element) throws ActivationException { + //Class clazz = bean.getClass(); + Method[] methods = clazz.getMethods(); + String key = null; + + // Load all child-elements. + Element child = getFirstElementChild(element); + while (child != null) { + String name = child.getNodeName(); + int index; + + if (name.equals("metaInf")) { + /* metaInf elements are loaded into this XmlBeanMapper, not + * into the specified bean. + */ + MetaInf mi = new MetaInf(); + loadBean(mi.getClass(), mi, child); + setMetaInf(mi); + } else { + Class c = null; + + String className = getMetaInfClass(child); + if (className != null) { + try { + c = Thread.currentThread().getContextClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + throw new ActivationException("Class " + className + " not found"); + } + } + + if ((index = getSetter(methods, name, c)) >= 0) { + /* Register single node. */ + loadProperty(methods[index], bean, child); + } else if ((index = getAdder(methods, name, c)) >= 0) { + /* Register list of nodes. */ + Element elem = getFirstElementChild(child); + while (elem != null) { + loadProperty(methods[index], bean, elem); + elem = getNextElement(elem); + } + } else { + if (!ignore) { + throw new ActivationException("Could not match element " + name + "(" + + clazz.getName() + ")"); + } + log(Level.WARNING, "Ignoring " + name); + } + } + + child = getNextElement(child); + } + + // Load all attributes. + NamedNodeMap map = element.getAttributes(); + for (int i = 0; i < map.getLength(); i++) { + Node node = map.item(i); + String name = node.getNodeName(); + if (name.equals("metaInfClass")) { + continue; + } + if (name.equals("metaInfKey")) { + key = node.getNodeValue(); + continue; + } + int index = getSetter(methods, name, null); + if (index >= 0) { + loadProperty(methods[index], bean, node); + } else { + if (!ignore) { + throw new ActivationException( + "Could not match attribute " + name + " for " + clazz); + } + log(Level.WARNING, "Ignoring " + name); + } + } + return key; + } + + /** + * Instantiates an object for a given class using a single-argument constructor. + * The argument type of the constructor must match the type of at least one + * reference object on the stack. The top-most references on the stack is then + * passed to the constructor. + *

+ * Warning: If there is more than one suitable constructor, it is undefined which one of + * these constructors are called. + * + * @return the instantiated object or null, if no suitable constructor was found + * @throws ActivationException if a suitable constructor could not be invoked + */ + private Object createBean(Class clazz) throws ActivationException { + Constructor[] constructors = clazz.getConstructors(); + // Loop over all constructors with single argument. + for (int i=0; i= 0; j--) { + Object ref = stack.get(j); + if (ref != null && types[0].equals(ref.getClass())) { + // Found the specific constructor. + try { + return constructors[i].newInstance(new Object[]{ref}); + } catch (Exception e) { + // InstantiationException or IllegalAccessException + throw new ActivationException("Could not instantiate " + clazz + + " using constructor with argument " + ref.getClass()); + } + } + } + } + return null; + } + + private String checkPrimitiveType(Node node) throws ActivationException { + if (node.getNodeType() != Node.ELEMENT_NODE) { + return null; + } + NamedNodeMap map = node.getAttributes(); + if (map.getLength() == 0) { + return null; + } + Node attribute = map.item(0); + if (!ignore + && !attribute.getNodeName().equals("pop") + && !attribute.getNodeName().equals("metaInfKey")) { + throw new ActivationException( + "Could not match " + attribute.getNodeName() + + " of primitive type or Class or String " + + node.getNodeName() + " has attribute "); + } + return attribute.getNodeValue(); + } + + /** + * Instanciates a bean for a given class from a node element using a reference object. + * If the class has a one-parameter constructor of the same type as the reference object, + * then the reference object is passed to that constructor. If the reference object is null, + * or if no suitable one-parameter constructor excists, then the default constructor + * is used. + * Important: java.util.Date will experience special treatment. + * + * @param refObject the reference object (may be null) + * @param node the node element + * @param clazz the class of the bean + * @return the created bean + */ + private BeanInfo createBean(Object refObject, Node node, Class clazz) throws ActivationException { + stack.push(refObject); + + Object bean = null; + String key = null; + + String nodeValue = ""; + NamedNodeMap map = node.getAttributes(); + if (map != null && map.getLength() != 0) { + Node attribute = map.item(0); + if (attribute.getNodeName().equals("pop")) { + nodeValue = (String) valueStack.get(attribute.getNodeValue()); + } else { + nodeValue = getNodeValue(node); + } + } else { + nodeValue = getNodeValue(node); + if (nodeValue.indexOf("pop#") >= 0) { + nodeValue = nodeValue.substring(nodeValue.indexOf("pop#") + 4); + nodeValue = (String) valueStack.get(nodeValue); + } + } + + String className = clazz.toString(); + if (clazz.equals(String.class)) { + key = checkPrimitiveType(node); + bean = nodeValue; + } else if (clazz.equals(Class.class)) { + try { + key = checkPrimitiveType(node); + bean = Thread.currentThread().getContextClassLoader().loadClass(nodeValue); + } catch (ClassNotFoundException e) { + throw new ActivationException("Class " + nodeValue + " not found"); + } + } else if (!className.startsWith("class") && !className.startsWith("interface")) { + // Primitive type. + key = checkPrimitiveType(node); + + if (clazz.equals(String.class)) { + bean = nodeValue; + } else if (className.equals("byte")) { + bean = new Byte(nodeValue); + } else if (className.equals("char")) { + String s = nodeValue; + if (s.length() != 1) { + throw new ActivationException("Character expected, but found '" + s + "'"); + } + bean = new Character(s.charAt(0)); + } else if (className.equals("double")) { + bean = new Double(nodeValue); + } else if (className.equals("float")) { + bean = new Float(nodeValue); + } else if (className.equals("int")) { + bean = new Integer(nodeValue); + } else if (className.equals("long")) { + bean = new Long(nodeValue); + } else if (className.equals("short")) { + bean = new Short(nodeValue); + } else if (className.equals("boolean")) { + bean = new Boolean(nodeValue); + } + // arrays: + // long:class [J + // double:class [D + // int:class [I + // String:class [Ljava.lang.String; + } else if (className.equals("class [J")) { + StringTokenizer tokenizer = new StringTokenizer(nodeValue, ","); + long[] array = new long[tokenizer.countTokens()]; + for (int i = 0; tokenizer.countTokens() > 0; i++) { + array[i] = Long.parseLong(tokenizer.nextToken()); + } + bean = array; + } else if (className.equals("class [D")) { + StringTokenizer tokenizer = new StringTokenizer(nodeValue, ","); + double[] array = new double[tokenizer.countTokens()]; + for (int i = 0; tokenizer.countTokens() > 0; i++) { + array[i] = Double.parseDouble(tokenizer.nextToken()); + } + bean = array; + } else if (className.equals("class [I")) { + StringTokenizer tokenizer = new StringTokenizer(nodeValue, ","); + int[] array = new int[tokenizer.countTokens()]; + for (int i = 0; tokenizer.countTokens() > 0; i++) { + array[i] = Integer.parseInt(tokenizer.nextToken()); + } + bean = array; + } else if (className.equals("class [Ljava.lang.String;")) { + StringTokenizer tokenizer = new StringTokenizer(nodeValue, ","); + String[] array = new String[tokenizer.countTokens()]; + for (int i = 0; tokenizer.countTokens() > 0; i++) { + array[i] = tokenizer.nextToken(); + } + bean = array; + } else if (className.equals("class java.lang.Double")) { + bean = new Double(nodeValue); + } else if (className.equals("class java.lang.Boolean")) { + bean = new Boolean(nodeValue); + } else if (className.equals("class java.lang.Long")) { + bean = new Long(nodeValue); + } else if (className.equals("class java.lang.Integer")) { + bean = new Integer(nodeValue); + } else if (className.indexOf("java.util.Date") > 0) { + // we have a date => some kind of primitive type + // special treating because of all useful methods being deprecated + try { + bean = dateFormatter.parse(nodeValue); + } catch (ParseException e) { + throw new ActivationException( + "Problems parsing a date field.", e); + } + } else if (className.indexOf("java.sql.Date") > 0) { + // we have a date => some kind of primitive type + // special treating because of all useful methods being deprecated + try { + bean = new java.sql.Date(dateFormatter.parse(nodeValue).getTime()); + } catch (ParseException e) { + throw new ActivationException( + "Problems parsing a date field.", e); + } + } else { + int modifiers = clazz.getModifiers(); + if ((modifiers & Modifier.ABSTRACT) != 0) { // Interface or abstract class. + throw new ActivationException( + "Cannot instantiate " + Modifier.toString(modifiers) + " " + clazz.getName()); + } + + if (stack.size() > 0) { + /** + * We have to instantiate the bean. Note that newInstance() will not do when + * a) the class is an inner class, + * b) a non-default constructor should be used. + * In these cases we have to find the specific constructor which has a + * single argument of the same type as the parent bean. + */ + bean = createBean(clazz); + } + + if (bean == null) { + try { + bean = clazz.newInstance(); + } catch (Exception e) { + // InstantiationException or IllegalAccessException + throw new ActivationException("Could not instantiate " + clazz + + " using default constructor"); + } + } + + key = loadBean(bean.getClass(), bean, (Element) node); + } + stack.pop(); + return new BeanInfo(key, bean); + } + + /** + * Load a node into a JavaBean and register it with a parent bean using + * the specified method. If the method does not allow arguments, no node is + * loaded. This corresponds to executing an action. + * + * @param method the setter or adder + * @param obj the parent bean + * @param the node to load + */ + private void loadProperty(Method method, Object obj, Node node) throws ActivationException { + Object[] args = null; + Class[] types = method.getParameterTypes(); + Class type = null; + + if (types.length==1 || types.length==2) { + args = new Object[types.length]; + + String className = getMetaInfClass(node); + if (className==null && metaInf!=null && metaInf.getType(node.getNodeName())!=null) { + className = metaInf.getType(node.getNodeName()); + } + + if (className!=null) { + try { + type = Thread.currentThread().getContextClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + throw new ActivationException("Could not load class " + className + " used by method " + + method.getDeclaringClass() + "." + method.getName()); + } + } else { + type = types[types.length-1]; + } + + BeanInfo beanInfo = createBean(obj, node, type); + args[types.length - 1] = beanInfo.bean; + if (types.length == 2) { + args[0] = beanInfo.key; + log(Level.FINER, "Invoking " + + method.getName() + + "(" + args[0] + ", " + args[1] + ")"); + } else { + log(Level.FINER, "Invoking " + + method.getName() + + "(" + args[0] + ")"); + } + } else if (types.length == 0) { + log(Level.FINER, "Invoking " + method.getName() + "()"); + } else { + throw new ActivationException("Method " + method.getName() + " has too many arguments"); + } + + try { + method.invoke(obj, args); + } catch (IllegalAccessException e) { + throw new ActivationException("Cannot access method " + method.getName() + + " of " + method.getDeclaringClass()); + } catch (IllegalArgumentException e) { + /* + All that can go wrong here is that + 1) an unwrapping conversion for primitive arguments failed, or, + 2) after possible unwrapping, a parameter value cannot be converted to + the corresponding formal parameter type by a method invocation conversion. + */ + throw new ActivationException("Cannot invoke " + method.getName() + + " of " + method.getDeclaringClass() + + (type == null ? " without an argument" : " with an argument of " + type)); + } catch (InvocationTargetException e) { + throw new ActivationException("Method " + method.getName() + + " of " + method.getDeclaringClass() + " has thrown an exception", e); + } + + } + + private String getNodeValue(Node node) { + StringBuffer s = new StringBuffer(); + if (node.getNodeType()==Node.ELEMENT_NODE) { + node = node.getFirstChild(); + while (node != null) { + if (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE) { + s.append(node.getNodeValue()); + } + node = node.getNextSibling(); + } + } else { + s.append(node.getNodeValue()); + } + + if (trim) { + return s.toString().trim(); + } + return s.toString(); + + } + + /** + * Fetches the first child element node of a given node. + */ + private Element getFirstElementChild(Node root) { + Node node = root.getFirstChild(); + while (node!=null) { + if (node.getNodeType()==Node.ELEMENT_NODE) { + return (Element) node; + } + node = node.getNextSibling(); + } + return null; + } + + /** + * Fetches the next sibling element node of a given node. + */ + private Element getNextElement(Node root) { + Node node = root.getNextSibling(); + while (node != null) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + return (Element) node; + } + node = node.getNextSibling(); + } + return null; + } + + /** + * @return Returns the valueStack. + */ + public HashMap getValueStack() { + return valueStack; + } + + /** + * The value is used if you use the attribute pop="NUMBER" or "pop#NUMBER" as attribut value. + * @param newValueStack The valueStack to set. + */ + public void setValueStack(HashMap newValueStack) { + valueStack = newValueStack; + } + +} diff --git a/src/main/java/com/j2js/visitors/AbstractVisitor.java b/src/main/java/com/j2js/visitors/AbstractVisitor.java new file mode 100644 index 0000000..14e588f --- /dev/null +++ b/src/main/java/com/j2js/visitors/AbstractVisitor.java @@ -0,0 +1,206 @@ +/* + * Created on Feb 2, 2005 + */ +package com.j2js.visitors; + +import com.j2js.dom.ASTNode; +import com.j2js.dom.ArrayAccess; +import com.j2js.dom.ArrayCreation; +import com.j2js.dom.ArrayInitializer; +import com.j2js.dom.Assignment; +import com.j2js.dom.Block; +import com.j2js.dom.BooleanLiteral; +import com.j2js.dom.BreakStatement; +import com.j2js.dom.CastExpression; +import com.j2js.dom.CatchClause; +import com.j2js.dom.ClassInstanceCreation; +import com.j2js.dom.ClassLiteral; +import com.j2js.dom.ConditionalExpression; +import com.j2js.dom.ContinueStatement; +import com.j2js.dom.DoStatement; +import com.j2js.dom.FieldAccess; +import com.j2js.dom.IfStatement; +import com.j2js.dom.InfixExpression; +import com.j2js.dom.InstanceofExpression; +import com.j2js.dom.Name; +import com.j2js.dom.PostfixExpression; +import com.j2js.dom.PrefixExpression; +import com.j2js.dom.MethodDeclaration; +import com.j2js.dom.MethodInvocation; +import com.j2js.dom.NullLiteral; +import com.j2js.dom.NumberLiteral; +import com.j2js.dom.PrimitiveCast; +import com.j2js.dom.ReturnStatement; +import com.j2js.dom.StringLiteral; +import com.j2js.dom.SwitchCase; +import com.j2js.dom.SwitchStatement; +import com.j2js.dom.SynchronizedBlock; +import com.j2js.dom.ThisExpression; +import com.j2js.dom.ThrowStatement; +import com.j2js.dom.TryStatement; +import com.j2js.dom.TypeDeclaration; +import com.j2js.dom.VariableBinding; +import com.j2js.dom.VariableDeclaration; +import com.j2js.dom.WhileStatement; + +/** + * Copyright by Wolfgang Kuehn 2005 + */ +public abstract class AbstractVisitor { + + + public abstract void visit(ASTNode node); + + public void visit(TypeDeclaration node) { + visit((ASTNode) node); + } + + public void visit(MethodDeclaration node) { + visit((ASTNode) node); + } + + public void visit(DoStatement node) { + visit((ASTNode) node); + } + + public void visit(WhileStatement node) { + visit((ASTNode) node); + } + + public void visit(IfStatement node) { + visit((ASTNode) node); + } + + public void visit(TryStatement node) { + visit((ASTNode) node); + } + + public void visit(Block node) { + visit((ASTNode) node); + } + + public void visit(InfixExpression node) { + visit((ASTNode) node); + } + + public void visit(PrefixExpression node) { + visit((ASTNode) node); + } + + public void visit(PostfixExpression node) { + visit((ASTNode) node); + } + + public void visit(SwitchStatement node) { + visit((ASTNode) node); + } + + public void visit(SwitchCase node) { + visit((ASTNode) node); + } + + public void visit(CatchClause node) { + visit((ASTNode) node); + } + + public void visit(ReturnStatement node) { + visit((ASTNode) node); + } + + public void visit(Assignment node) { + visit((ASTNode) node); + } + + public void visit(NumberLiteral node) { + visit((ASTNode) node); + } + + public void visit(StringLiteral node) { + visit((ASTNode) node); + } + + public void visit(ClassLiteral node) { + visit((ASTNode) node); + } + + public void visit(NullLiteral node) { + visit((ASTNode) node); + } + + public void visit(MethodInvocation node) { + visit((ASTNode) node); + } + + public void visit(ClassInstanceCreation node) { + visit((ASTNode) node); + } + + public void visit(ArrayInitializer node) { + visit((ASTNode) node); + } + + public void visit(ArrayCreation node) { + visit((ASTNode) node); + } + + public void visit(ArrayAccess node) { + visit((ASTNode) node); + } + + public void visit(VariableDeclaration node) { + visit((ASTNode) node); + } + + public void visit(VariableBinding node) { + visit((ASTNode) node); + } + + public void visit(ThisExpression node) { + visit((ASTNode) node); + } + + public void visit(FieldAccess node) { + visit((ASTNode) node); + } + + public void visit(BreakStatement node) { + visit((ASTNode) node); + } + + public void visit(ContinueStatement node) { + visit((ASTNode) node); + } + + public void visit(CastExpression node) { + visit((ASTNode) node); + } + + public void visit(BooleanLiteral node) { + visit((ASTNode) node); + } + + public void visit(ThrowStatement node) { + visit((ASTNode) node); + } + + public void visit(Name node) { + visit((ASTNode) node); + } + + public void visit(InstanceofExpression node) { + visit((ASTNode) node); + } + + public void visit(ConditionalExpression node) { + visit((ASTNode) node); + } + + public void visit(SynchronizedBlock node) { + visit((ASTNode) node); + } + + public void visit(PrimitiveCast node) { + visit((ASTNode) node); + } + +} \ No newline at end of file diff --git a/src/main/java/com/j2js/visitors/Generator.java b/src/main/java/com/j2js/visitors/Generator.java new file mode 100644 index 0000000..cca1d61 --- /dev/null +++ b/src/main/java/com/j2js/visitors/Generator.java @@ -0,0 +1,80 @@ +package com.j2js.visitors; + +import com.j2js.J2JSCompiler; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +import com.j2js.Log; +import com.j2js.dom.TypeDeclaration; + +public abstract class Generator extends AbstractVisitor { + + final String whiteSpace; + int depth; + char lastChar; + String[] indents; + private PrintStream stream; + + TypeDeclaration typeDecl; + + public Generator() { + if (J2JSCompiler.compiler.isCompression()) { + whiteSpace = ""; + } else { + whiteSpace = " "; + } + } + + public PrintStream getOutputStream() { + return stream; + } + + public void setOutputPath(String path) throws FileNotFoundException { + Log.getLogger().info("Destination file is " + path); + setOutputStream(new PrintStream(new FileOutputStream(path))); + } + + public void setOutputStream(PrintStream theStream) { + stream = theStream; + } + + public void flush() { + stream.flush(); + } + + void indent() { + // No indentation if compression is on. + if (J2JSCompiler.compiler.isCompression()) return; + String INDENT = "\t"; + if (indents == null || depth >= indents.length) { + indents = new String[2 * depth]; + indents[0] = ""; + for (int i=1; i0) lastChar = s.charAt(s.length()-1); + } + + void println(String s) { + print(s); + stream.println(""); + } + + void indentln(String s) { + indent(); + println(s); + } + + void indent(String s) { + indent(); + print(s); + } +} diff --git a/src/main/java/com/j2js/visitors/JavaGenerator.java b/src/main/java/com/j2js/visitors/JavaGenerator.java new file mode 100644 index 0000000..4e7286a --- /dev/null +++ b/src/main/java/com/j2js/visitors/JavaGenerator.java @@ -0,0 +1,105 @@ +package com.j2js.visitors; + +import java.io.PrintStream; +import java.lang.reflect.Modifier; +import java.util.Iterator; + +import com.j2js.Log; +import com.j2js.dom.*; +import com.j2js.Utils; + +/** + * @author kuehn + */ +public class JavaGenerator extends Generator { + + /** Creates a new instance of Generator */ + public JavaGenerator() { + } + + public JavaGenerator(PrintStream targetStream) { + setOutputStream(targetStream); + } + + public void visit(ASTNode node) {} + + /** + * This method must be call first because it sets the global className. + */ + public void visit(TypeDeclaration theTypeDecl) { + Log logger = Log.getLogger(); + logger.info("Generating Java for " + theTypeDecl); + + depth = 0; + typeDecl = theTypeDecl; + + println("package " + theTypeDecl.getPackageName() + ";"); + println(""); + print("public "); + if (Modifier.isFinal(theTypeDecl.getAccess())) print("final "); + + println("class " + theTypeDecl.getUnQualifiedName() + " {"); + + depth++; + + MethodDeclaration[] methods = theTypeDecl.getMethods(); + for (int i=0; i iterator = method.getParameters().iterator(); + while (iterator.hasNext()) { + VariableDeclaration decl = iterator.next(); + decl.visit(this); + print(iterator.hasNext() ? ", " : ""); + } + + println(") {"); + + depth = 1; + + // prefix must be _. + println("var _=j2js;"); + + // Generate local variable declarations. + for (VariableDeclaration decl : method.getLocalVariables()) { + indent(); + decl.visit(this); + println(";"); + } + + depth = 0; + + visit_(method.getBody()); + + println(closingString); + + unit.setData(reset()); + Log.getLogger().debug("Generating JavaScript for " + unit); + } + + public void visit(DoStatement doStmt) { + println("do {"); + visit_(doStmt.getBlock()); + indent("} while ("); + doStmt.getExpression().visit(this); + print(")"); + } + + public void visit(WhileStatement whileStmt) { + print("while ("); + whileStmt.getExpression().visit(this); + println(") {"); + visit_(whileStmt.getBlock()); + indent("}"); + } + + public void visit(IfStatement ifStmt) { + print("if ("); + ifStmt.getExpression().visit(this); + println(") {"); + visit_(ifStmt.getIfBlock()); + indent("}"); + if (ifStmt.getElseBlock()!=null) { + println(" else {"); + visit_(ifStmt.getElseBlock()); + indent("}"); + } + } + + /** + * A try statement has the following structure: + * TryStatement : + * try Block Catch* (Catch | Finally) + * + * Because ECMAScript3 does not support multiple catch clauses, we will generate the following code. + * TryStatement : + * try Block catch(ex) { Catch } Finally? + * try Block catch(ex) { if (ex ...) { Catch } else if(ex ...) ... else throw ex } Finally? + * + */ + public void visit(TryStatement tryStmt) { + println("try {"); + visit_(tryStmt.getTryBlock()); + indent("} "); + Block clauses = tryStmt.getCatchStatements(); + CatchClause clause = (CatchClause) clauses.getFirstChild(); + + // Note: We have to declare variable 'ex_' because we cannot use method local variables due to + // a JavaScript bug in some clients: var l0 = "foo"; try { ... } catch (l0) { /* Now l0 == "foo" */ } + String ex = null; + if (clause != null) { + ex = clause.getException().getName(); + } + + if (clauses.getChildCount()==1) { + print("catch(" + ex + ") "); + clause.visit(this); + } else if (clauses.getChildCount()>1) { + println("catch(" + ex + ") {"); + depth++; + indent(); + while (clause != null) { + if (clause.getException().getType() != null) + print("if (" + prefix + "isInstanceof(" + ex + + ", \"" + Utils.getSignature(clause.getException().getType()) + "\")) "); + clause.visit(this); + clause = (CatchClause) clause.getNextSibling(); + if (clause == null) break; + print(" else "); + } + println(""); + depth--; + indent("}"); + } + + Block finallyBlock = tryStmt.getFinallyBlock(); + if (finallyBlock != null) { // There is a finally clause. + println(" finally {"); + visit_(finallyBlock); + indent("}"); + } + } + + public void visit(CatchClause clause) { + visit((Block) clause); + } + + public void visit_(Block block) { + depth++; + ASTNode node = block.getFirstChild(); + while (node != null) { + currentNode = node; + if (J2JSCompiler.compiler.isGenerateLineNumbers()) { + int lineNumber = currentMethodDeclaration.getLineNumberCursor().getAndMarkLineNumber(node); + if (lineNumber != -1) { + print(prefix + "ln=" + lineNumber + ";\n"); + } + } + + indent(); + if (node instanceof Block && ((Block) node).isLabeled()) { + print(((Block) node).getLabel() + ": "); + } + + node.visit(this); + + if (lastChar == '}') { + println(""); + } else { + println(";"); + } + node = node.getNextSibling(); + } + depth--; + } + + public void visit(Block block) { + println("{"); + visit_(block); + indent("}"); + } + + public void visit(SynchronizedBlock block) { + println("{ // Synchronized."); + visit_(block); + indent("}"); + } + + public void visit(PrefixExpression binOp) { + print(binOp.getOperator().toString() + "("); + binOp.getOperand().visit(this); + print(")"); + } + + public void visit(PostfixExpression binOp) { + // Note that we do not need parenthese here. + binOp.getOperand().visit(this); + print(binOp.getOperator().toString()); + } + + private void bracket(ASTNode node, InfixExpression.Operator op) { + if ((node instanceof InfixExpression && ((InfixExpression) node).getOperator()==op) + || + node instanceof NumberLiteral + || + node instanceof NullLiteral + || + node instanceof FieldAccess + || + node instanceof VariableBinding) { + node.visit(this); + } else { + print("("); + node.visit(this); + print(")"); + } + } + + public void visit(InfixExpression binOp) { + InfixExpression.Operator op = binOp.getOperator(); + Expression left = binOp.getLeftOperand(); + Expression right = binOp.getRightOperand(); + + boolean isTruncate = false; + Type type = binOp.getTypeBinding(); + + /* There is no integral type division in ECMAScript, so we need to + truncate the result. + Note that the % operation works the same in ECMAScript and Java, + i.e. in accepting floating-points. + */ + if (op == InfixExpression.Operator.DIVIDE && + (type.equals(Type.LONG) || type.equals(Type.INT))) { + isTruncate = true; + print(" " + prefix + "trunc("); + } + + bracket(left, op); + print(" " + op + " "); + bracket(right, op); + + if (isTruncate) { + print(")"); + } + } + + public void visit(ConditionalExpression ce) { + ce.getConditionExpression().visit(this); + print("?"); + ce.getThenExpression().visit(this); + print(":"); + ce.getElseExpression().visit(this); + } + + public void visit(InstanceofExpression node) { + print(prefix + "isInstanceof("); + node.getLeftOperand().visit(this); + print(", \""); + Signature signature = Project.getSingleton().getArraySignature(node.getRightOperand()); + print(signature.toString()); + print("\")"); + } + + public void visit(SwitchStatement switchStmt) { + print("switch ("); + switchStmt.getExpression().visit(this); + println(") {"); + ASTNode node = switchStmt.getFirstChild(); + while (node != null) { + SwitchCase sc = (SwitchCase) node; + sc.visit(this); + node = node.getNextSibling(); + } + indentln("}"); + } + + public void visit(SwitchCase switchCase) { + Iterator iter = switchCase.getExpressions().iterator(); + if (iter.hasNext()) { + while (iter.hasNext()) { + NumberLiteral expression = iter.next(); + indent("case "); + expression.visit(this); + println(":"); + } + } else { + indentln("default:"); + } + visit_(switchCase); // Generate switchCase as block! + } + + public void visit(ASTNode stmt) { + print(stmt.toString()); + } + + public void visit(ReturnStatement r) { + print("return"); + if (r.getExpression() != null) { + print(" "); + r.getExpression().visit(this); + } + } + + public void visit(Assignment a) { + Expression rhs = a.getRightHandSide(); + + if (rhs instanceof ClassInstanceCreation) { + ClassInstanceCreation cic = (ClassInstanceCreation) rhs; + if (cic.getTypeBinding().toString().equals("java.lang.String")) { + // Do not generate String creation. + // TODO: Move this to the optimize phase. + return; + } + } + + a.getLeftHandSide().visit(this); + print(" " + a.getOperator() + " "); + if (VariableBinding.isBoolean(a.getLeftHandSide())) { + if (NumberLiteral.isZero(rhs)) { + print("false"); + } if (NumberLiteral.isOne(rhs)) { + print("true"); + } else { + rhs.visit(this); + } + } else { + rhs.visit(this); + } + } + + public void visit(NumberLiteral literal) { + print("" + literal.getValue()); + } + + public void visit(StringLiteral literal) { + print(Utils.escape(literal.getValue())); + } + + public void visit(ClassLiteral literal) { + MethodBinding binding = MethodBinding.lookup("java.lang.Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); + MethodInvocation mi = new MethodInvocation(currentMethodDeclaration, binding); + mi.addArgument(new StringLiteral(literal.getSignature().toString())); + visit(mi); + } + + public void visit(NullLiteral literal) { + consume(literal); + print("null"); + } + + private void generateList(List arguments) { + for (int i=0; i 0, but was" + ac.getDimensions().size()); + } + + if (ac.getInitializer() != null) { + ac.getInitializer().visit(this); + } else { + print("j2js.newArray('"); + Signature signature = Project.getSingleton().getArraySignature(ac.getTypeBinding()); + print(signature.toString()); + print("', ["); + for (int i=0; iget and opt + * methods for accessing the values by index, and put methods for + * adding or replacing values. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the + * JSONObject.NULL object. + *

+ * The constructor can convert a JSON text into a Java object. The + * toString method converts to JSON text. + *

+ * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coersion for you. + *

+ * The texts produced by the toString methods strictly conform to + * JSON syntax rules. The constructors are more forgiving in the texts they will + * accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing bracket.
  • + *
  • The null value will be inserted when there + * is , (comma) elision.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, + * and if they do not contain any of these characters: + * { } [ ] / \ : , = ; # and if they do not look like numbers + * and if they are not the reserved words true, + * false, or null.
  • + *
  • Values can be separated by ; (semicolon) as + * well as by , (comma).
  • + *
  • Numbers may have the 0- (octal) or + * 0x- (hex) prefix.
  • + *
  • Comments written in the slashshlash, slashstar, and hash conventions + * will be ignored.
  • + *
+ + * @author JSON.org + * @version 2 + */ +public class JSONArray { + + + /** + * The arrayList where the JSONArray's properties are kept. + */ + private ArrayList myArrayList; + + + /** + * Construct an empty JSONArray. + */ + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + /** + * Construct a JSONArray from a JSONTokener. + * @param x A JSONTokener + * @throws JSONException If there is a syntax error. + */ + public JSONArray(JSONTokener x) throws JSONException { + this(); + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + if (x.nextClean() == ']') { + return; + } + x.back(); + for (;;) { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(null); + } else { + x.back(); + this.myArrayList.add(x.nextValue()); + } + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == ']') { + return; + } + x.back(); + break; + case ']': + return; + default: + throw x.syntaxError("Expected a ',' or ']'"); + } + } + } + + + /** + * Construct a JSONArray from a source sJSON text. + * @param string A string that begins with + * [ (left bracket) + * and ends with ] (right bracket). + * @throws JSONException If there is a syntax error. + */ + public JSONArray(String string) throws JSONException { + this(new JSONTokener(string)); + } + + + /** + * Construct a JSONArray from a Collection. + * @param collection A Collection. + */ + public JSONArray(Collection collection) { + this.myArrayList = (collection == null) ? + new ArrayList() : + new ArrayList(collection); + } + + + /** + * Construct a JSONArray from an array + * @throws JSONException If not an array. + */ + public JSONArray(Object array) throws JSONException { + this(); + if (array.getClass().isArray()) { + int length = Array.getLength(array); + for (int i = 0; i < length; i += 1) { + this.put(Array.get(array, i)); + } + } else { + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + } + + + /** + * Get the object value associated with an index. + * @param index + * The index must be between 0 and length() - 1. + * @return An object value. + * @throws JSONException If there is no value for the index. + */ + public Object get(int index) throws JSONException { + Object o = opt(index); + if (o == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return o; + } + + + /** + * Get the boolean value associated with an index. + * The string values "true" and "false" are converted to boolean. + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + * @throws JSONException If there is no value for the index or if the + * value is not convertable to boolean. + */ + public boolean getBoolean(int index) throws JSONException { + Object o = get(index); + if (o.equals(Boolean.FALSE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("false"))) { + return false; + } else if (o.equals(Boolean.TRUE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONArray[" + index + "] is not a Boolean."); + } + + + /** + * Get the double value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + */ + public double getDouble(int index) throws JSONException { + Object o = get(index); + try { + return o instanceof Number ? + ((Number)o).doubleValue() : + Double.valueOf((String)o).doubleValue(); + } catch (Exception e) { + throw new JSONException("JSONArray[" + index + + "] is not a number."); + } + } + + + /** + * Get the int value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + * if the value cannot be converted to a number. + */ + public int getInt(int index) throws JSONException { + Object o = get(index); + return o instanceof Number ? + ((Number)o).intValue() : (int)getDouble(index); + } + + + /** + * Get the JSONArray associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A JSONArray value. + * @throws JSONException If there is no value for the index. or if the + * value is not a JSONArray + */ + public JSONArray getJSONArray(int index) throws JSONException { + Object o = get(index); + if (o instanceof JSONArray) { + return (JSONArray)o; + } + throw new JSONException("JSONArray[" + index + + "] is not a JSONArray."); + } + + + /** + * Get the JSONObject associated with an index. + * @param index subscript + * @return A JSONObject value. + * @throws JSONException If there is no value for the index or if the + * value is not a JSONObject + */ + public JSONObject getJSONObject(int index) throws JSONException { + Object o = get(index); + if (o instanceof JSONObject) { + return (JSONObject)o; + } + throw new JSONException("JSONArray[" + index + + "] is not a JSONObject."); + } + + + /** + * Get the long value associated with an index. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + * @throws JSONException If the key is not found or if the value cannot + * be converted to a number. + */ + public long getLong(int index) throws JSONException { + Object o = get(index); + return o instanceof Number ? + ((Number)o).longValue() : (long)getDouble(index); + } + + + /** + * Get the string associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return A string value. + * @throws JSONException If there is no value for the index. + */ + public String getString(int index) throws JSONException { + return get(index).toString(); + } + + + /** + * Determine if the value is null. + * @param index The index must be between 0 and length() - 1. + * @return true if the value at the index is null, or if there is no value. + */ + public boolean isNull(int index) { + return JSONObject.NULL.equals(opt(index)); + } + + + /** + * Make a string from the contents of this JSONArray. The + * separator string is inserted between each element. + * Warning: This method assumes that the data structure is acyclical. + * @param separator A string that will be inserted between the elements. + * @return a string. + * @throws JSONException If the array contains an invalid number. + */ + public String join(String separator) throws JSONException { + int len = length(); + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(separator); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + + /** + * Get the number of elements in the JSONArray, included nulls. + * + * @return The length (or size). + */ + public int length() { + return this.myArrayList.size(); + } + + + /** + * Get the optional object value associated with an index. + * @param index The index must be between 0 and length() - 1. + * @return An object value, or null if there is no + * object at that index. + */ + public Object opt(int index) { + return (index < 0 || index >= length()) ? + null : this.myArrayList.get(index); + } + + + /** + * Get the optional boolean value associated with an index. + * It returns false if there is no value at that index, + * or if the value is not Boolean.TRUE or the String "true". + * + * @param index The index must be between 0 and length() - 1. + * @return The truth. + */ + public boolean optBoolean(int index) { + return optBoolean(index, false); + } + + + /** + * Get the optional boolean value associated with an index. + * It returns the defaultValue if there is no value at that index or if + * it is not a Boolean or the String "true" or "false" (case insensitive). + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. + * @return The truth. + */ + public boolean optBoolean(int index, boolean defaultValue) { + try { + return getBoolean(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional double value associated with an index. + * NaN is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public double optDouble(int index) { + return optDouble(index, Double.NaN); + } + + + /** + * Get the optional double value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index subscript + * @param defaultValue The default value. + * @return The value. + */ + public double optDouble(int index, double defaultValue) { + try { + return getDouble(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional int value associated with an index. + * Zero is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public int optInt(int index) { + return optInt(index, 0); + } + + + /** + * Get the optional int value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public int optInt(int index, int defaultValue) { + try { + return getInt(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional JSONArray associated with an index. + * @param index subscript + * @return A JSONArray value, or null if the index has no value, + * or if the value is not a JSONArray. + */ + public JSONArray optJSONArray(int index) { + Object o = opt(index); + return o instanceof JSONArray ? (JSONArray)o : null; + } + + + /** + * Get the optional JSONObject associated with an index. + * Null is returned if the key is not found, or null if the index has + * no value, or if the value is not a JSONObject. + * + * @param index The index must be between 0 and length() - 1. + * @return A JSONObject value. + */ + public JSONObject optJSONObject(int index) { + Object o = opt(index); + return o instanceof JSONObject ? (JSONObject)o : null; + } + + + /** + * Get the optional long value associated with an index. + * Zero is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * + * @param index The index must be between 0 and length() - 1. + * @return The value. + */ + public long optLong(int index) { + return optLong(index, 0); + } + + + /** + * Get the optional long value associated with an index. + * The defaultValue is returned if there is no value for the index, + * or if the value is not a number and cannot be converted to a number. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return The value. + */ + public long optLong(int index, long defaultValue) { + try { + return getLong(index); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get the optional string value associated with an index. It returns an + * empty string if there is no value at that index. If the value + * is not a string and is not null, then it is coverted to a string. + * + * @param index The index must be between 0 and length() - 1. + * @return A String value. + */ + public String optString(int index) { + return optString(index, ""); + } + + + /** + * Get the optional string associated with an index. + * The defaultValue is returned if the key is not found. + * + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. + * @return A String value. + */ + public String optString(int index, String defaultValue) { + Object o = opt(index); + return o != null ? o.toString() : defaultValue; + } + + + /** + * Append a boolean value. This increases the array's length by one. + * + * @param value A boolean value. + * @return this. + */ + public JSONArray put(boolean value) { + put(value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONArray which is produced from a Collection. + * @param value A Collection value. + * @return this. + */ + public JSONArray put(Collection value) { + put(new JSONArray(value)); + return this; + } + + + /** + * Append a double value. This increases the array's length by one. + * + * @param value A double value. + * @throws JSONException if the value is not finite. + * @return this. + */ + public JSONArray put(double value) throws JSONException { + Double d = new Double(value); + JSONObject.testValidity(d); + put(d); + return this; + } + + + /** + * Append an int value. This increases the array's length by one. + * + * @param value An int value. + * @return this. + */ + public JSONArray put(int value) { + put(new Integer(value)); + return this; + } + + + /** + * Append an long value. This increases the array's length by one. + * + * @param value A long value. + * @return this. + */ + public JSONArray put(long value) { + put(new Long(value)); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONObject which is produced from a Map. + * @param value A Map value. + * @return this. + */ + public JSONArray put(Map value) { + put(new JSONObject(value)); + return this; + } + + + /** + * Append an object value. This increases the array's length by one. + * @param value An object value. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + */ + public JSONArray put(Object value) { + this.myArrayList.add(value); + return this; + } + + + /** + * Put or replace a boolean value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * @param index The subscript. + * @param value A boolean value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, boolean value) throws JSONException { + put(index, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONArray which is produced from a Collection. + * @param index The subscript. + * @param value A Collection value. + * @return this. + * @throws JSONException If the index is negative or if the value is + * not finite. + */ + public JSONArray put(int index, Collection value) throws JSONException { + put(index, new JSONArray(value)); + return this; + } + + + /** + * Put or replace a double value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value A double value. + * @return this. + * @throws JSONException If the index is negative or if the value is + * not finite. + */ + public JSONArray put(int index, double value) throws JSONException { + put(index, new Double(value)); + return this; + } + + + /** + * Put or replace an int value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value An int value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, int value) throws JSONException { + put(index, new Integer(value)); + return this; + } + + + /** + * Put or replace a long value. If the index is greater than the length of + * the JSONArray, then null elements will be added as necessary to pad + * it out. + * @param index The subscript. + * @param value A long value. + * @return this. + * @throws JSONException If the index is negative. + */ + public JSONArray put(int index, long value) throws JSONException { + put(index, new Long(value)); + return this; + } + + + /** + * Put a value in the JSONArray, where the value will be a + * JSONObject which is produced from a Map. + * @param index The subscript. + * @param value The Map value. + * @return this. + * @throws JSONException If the index is negative or if the the value is + * an invalid number. + */ + public JSONArray put(int index, Map value) throws JSONException { + put(index, new JSONObject(value)); + return this; + } + + + /** + * Put or replace an object value in the JSONArray. If the index is greater + * than the length of the JSONArray, then null elements will be added as + * necessary to pad it out. + * @param index The subscript. + * @param value The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. + * @return this. + * @throws JSONException If the index is negative or if the the value is + * an invalid number. + */ + public JSONArray put(int index, Object value) throws JSONException { + JSONObject.testValidity(value); + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < length()) { + this.myArrayList.set(index, value); + } else { + while (index != length()) { + put(JSONObject.NULL); + } + put(value); + } + return this; + } + + + /** + * Produce a JSONObject by combining a JSONArray of names with the values + * of this JSONArray. + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. + * @return A JSONObject, or null if there are no names or if this JSONArray + * has no values. + * @throws JSONException If any of the names are null. + */ + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.length() == 0 || length() == 0) { + return null; + } + JSONObject jo = new JSONObject(); + for (int i = 0; i < names.length(); i += 1) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + + /** + * Make a JSON text of this JSONArray. For compactness, no + * unnecessary whitespace is added. If it is not possible to produce a + * syntactically correct JSON text then null will be returned instead. This + * could occur if the array contains an invalid number. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, transmittable + * representation of the array. + */ + public String toString() { + try { + return '[' + join(",") + ']'; + } catch (Exception e) { + return null; + } + } + + + /** + * Make a prettyprinted JSON text of this JSONArray. + * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with [ (left bracket) and ending + * with ] (right bracket). + * @throws JSONException + */ + public String toString(int indentFactor) throws JSONException { + return toString(indentFactor, 0); + } + + + /** + * Make a prettyprinted JSON text of this JSONArray. + * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indention of the top level. + * @return a printable, displayable, transmittable + * representation of the array. + * @throws JSONException + */ + String toString(int indentFactor, int indent) throws JSONException { + int len = length(); + if (len == 0) { + return "[]"; + } + int i; + StringBuffer sb = new StringBuffer("["); + if (len == 1) { + sb.append(JSONObject.valueToString(this.myArrayList.get(0), + indentFactor, indent)); + } else { + int newindent = indent + indentFactor; + sb.append('\n'); + for (i = 0; i < len; i += 1) { + if (i > 0) { + sb.append(",\n"); + } + for (int j = 0; j < newindent; j += 1) { + sb.append(' '); + } + sb.append(JSONObject.valueToString(this.myArrayList.get(i), + indentFactor, newindent)); + } + sb.append('\n'); + for (i = 0; i < indent; i += 1) { + sb.append(' '); + } + } + sb.append(']'); + return sb.toString(); + } + + + /** + * Write the contents of the JSONArray as JSON text to a writer. + * For compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + try { + boolean b = false; + int len = length(); + + writer.write('['); + + for (int i = 0; i < len; i += 1) { + if (b) { + writer.write(','); + } + Object v = this.myArrayList.get(i); + if (v instanceof JSONObject) { + ((JSONObject)v).write(writer); + } else if (v instanceof JSONArray) { + ((JSONArray)v).write(writer); + } else { + writer.write(JSONObject.valueToString(v)); + } + b = true; + } + writer.write(']'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } +} diff --git a/src/main/java/org/json/JSONException.java b/src/main/java/org/json/JSONException.java new file mode 100644 index 0000000..7d2a78c --- /dev/null +++ b/src/main/java/org/json/JSONException.java @@ -0,0 +1,28 @@ +package org.json; + +/** + * The JSONException is thrown by the JSON.org classes then things are amiss. + * @author JSON.org + * @version 2 + */ +public class JSONException extends Exception { + private Throwable cause; + + /** + * Constructs a JSONException with an explanatory message. + * @param message Detail about the reason for the exception. + */ + public JSONException(String message) { + super(message); + } + + public JSONException(Throwable t) { + super(t.getMessage()); + this.cause = t; + } + + public Throwable getCause() { + return this.cause; + } +} + diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java new file mode 100644 index 0000000..2fbd522 --- /dev/null +++ b/src/main/java/org/json/JSONObject.java @@ -0,0 +1,1327 @@ +package org.json; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A JSONObject is an unordered collection of name/value pairs. Its + * external form is a string wrapped in curly braces with colons between the + * names and values, and commas between the values and names. The internal form + * is an object having get and opt methods for + * accessing the values by name, and put methods for adding or + * replacing values by name. The values can be any of these types: + * Boolean, JSONArray, JSONObject, + * Number, String, or the JSONObject.NULL + * object. A JSONObject constructor can be used to convert an external form + * JSON text into an internal form whose values can be retrieved with the + * get and opt methods, or to convert values into a + * JSON text using the put and toString methods. + * A get method returns a value if one can be found, and throws an + * exception if one cannot be found. An opt method returns a + * default value instead of throwing an exception, and so is useful for + * obtaining optional values. + *

+ * The generic get() and opt() methods return an + * object, which you can cast or query for type. There are also typed + * get and opt methods that do type checking and type + * coersion for you. + *

+ * The put methods adds values to an object. For example,

+ *     myString = new JSONObject().put("JSON", "Hello, World!").toString();
+ * produces the string {"JSON": "Hello, World"}. + *

+ * The texts produced by the toString methods strictly conform to + * the JSON sysntax rules. + * The constructors are more forgiving in the texts they will accept: + *

    + *
  • An extra , (comma) may appear just + * before the closing brace.
  • + *
  • Strings may be quoted with ' (single + * quote).
  • + *
  • Strings do not need to be quoted at all if they do not begin with a quote + * or single quote, and if they do not contain leading or trailing spaces, + * and if they do not contain any of these characters: + * { } [ ] / \ : , = ; # and if they do not look like numbers + * and if they are not the reserved words true, + * false, or null.
  • + *
  • Keys can be followed by = or => as well as + * by :.
  • + *
  • Values can be followed by ; (semicolon) as + * well as by , (comma).
  • + *
  • Numbers may have the 0- (octal) or + * 0x- (hex) prefix.
  • + *
  • Comments written in the slashshlash, slashstar, and hash conventions + * will be ignored.
  • + *
+ * @author JSON.org + * @version 2 + */ +public class JSONObject { + + /** + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. + */ + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * @return NULL. + */ + protected final Object clone() { + return this; + } + + + /** + * A Null object is equal to the null value and to itself. + * @param object An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object + * or null. + */ + public boolean equals(Object object) { + return object == null || object == this; + } + + + /** + * Get the "null" string value. + * @return The string "null". + */ + public String toString() { + return "null"; + } + } + + + /** + * The hash map where the JSONObject's properties are kept. + */ + private HashMap myHashMap; + + + /** + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". + */ + public static final Object NULL = new Null(); + + + /** + * Construct an empty JSONObject. + */ + public JSONObject() { + this.myHashMap = new HashMap(); + } + + + /** + * Construct a JSONObject from a subset of another JSONObject. + * An array of strings is used to identify the keys that should be copied. + * Missing keys are ignored. + * @param jo A JSONObject. + * @param sa An array of strings. + * @exception JSONException If a value is a non-finite number. + */ + public JSONObject(JSONObject jo, String[] sa) throws JSONException { + this(); + for (int i = 0; i < sa.length; i += 1) { + putOpt(sa[i], jo.opt(sa[i])); + } + } + + + /** + * Construct a JSONObject from a JSONTokener. + * @param x A JSONTokener object containing the source string. + * @throws JSONException If there is a syntax error in the source string. + */ + public JSONObject(JSONTokener x) throws JSONException { + this(); + char c; + String key; + + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + for (;;) { + c = x.nextClean(); + switch (c) { + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); + } + + /* + * The key is followed by ':'. We will also tolerate '=' or '=>'. + */ + + c = x.nextClean(); + if (c == '=') { + if (x.next() != '>') { + x.back(); + } + } else if (c != ':') { + throw x.syntaxError("Expected a ':' after a key"); + } + put(key, x.nextValue()); + + /* + * Pairs are separated by ','. We will also tolerate ';'. + */ + + switch (x.nextClean()) { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': + return; + default: + throw x.syntaxError("Expected a ',' or '}'"); + } + } + } + + + /** + * Construct a JSONObject from a Map. + * @param map A map object that can be used to initialize the contents of + * the JSONObject. + */ + public JSONObject(Map map) { + this.myHashMap = (map == null) ? + new HashMap() : + new HashMap(map); + } + + + /** + * Construct a JSONObject from an Object using bean getters. + * It reflects on all of the public methods of the object. + * For each of the methods with no parameters and a name starting + * with "get" or "is" followed by an uppercase letter, + * the method is invoked, and a key and the value returned from the getter method + * are put into the new JSONObject. + * + * The key is formed by removing the "get" or "is" prefix. If the second remaining + * character is not upper case, then the first + * character is converted to lower case. + * + * For example, if an object has a method named "getName", and + * if the result of calling object.getName() is "Larry Fine", + * then the JSONObject will contain "name": "Larry Fine". + * + * @param bean An object that has getter methods that should be used + * to make a JSONObject. + */ + public JSONObject(Object bean) { + this(); + Class c = bean.getClass(); + Method[] methods = c.getMethods(); + for (int i = 0; i < methods.length; i += 1) { + try { + Method method = methods[i]; + String name = method.getName(); + String key = ""; + if (name.startsWith("get")) { + key = name.substring(3); + } else if (name.startsWith("is")) { + key = name.substring(2); + } + if (key.length() > 0 && + Character.isUpperCase(key.charAt(0)) && + method.getParameterTypes().length == 0) { + if (key.length() == 1) { + key = key.toLowerCase(); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase() + + key.substring(1); + } + this.put(key, method.invoke(bean, null)); + } + } catch (Exception e) { + /* forget about it */ + } + } + } + + /** + * Construct a JSONObject from an Object, using reflection to find the + * public members. The resulting JSONObject's keys will be the strings + * from the names array, and the values will be the field values associated + * with those keys in the object. If a key is not found or not visible, + * then it will not be copied into the new JSONObject. + * @param object An object that has fields that should be used to make a + * JSONObject. + * @param names An array of strings, the names of the fields to be obtained + * from the object. + */ + public JSONObject(Object object, String names[]) { + this(); + Class c = object.getClass(); + for (int i = 0; i < names.length; i += 1) { + try { + String name = names[i]; + Field field = c.getField(name); + Object value = field.get(object); + this.put(name, value); + } catch (Exception e) { + /* forget about it */ + } + } + } + + + /** + * Construct a JSONObject from a string. + * This is the most commonly used JSONObject constructor. + * @param string A string beginning + * with { (left brace) and ending + * with } (right brace). + * @exception JSONException If there is a syntax error in the source string. + */ + public JSONObject(String string) throws JSONException { + this(new JSONTokener(string)); + } + + + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a + * JSONArray is stored under the key to hold all of the accumulated values. + * If there is already a JSONArray, then the new value is appended to it. + * In contrast, the put method replaces the previous value. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the value is an invalid number + * or if the key is null. + */ + public JSONObject accumulate(String key, Object value) + throws JSONException { + testValidity(value); + Object o = opt(key); + if (o == null) { + put(key, value instanceof JSONArray ? + new JSONArray().put(value) : + value); + } else if (o instanceof JSONArray) { + ((JSONArray)o).put(value); + } else { + put(key, new JSONArray().put(o).put(value)); + } + return this; + } + + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the key is null or if the current value + * associated with the key is not a JSONArray. + */ + public JSONObject append(String key, Object value) + throws JSONException { + testValidity(value); + Object o = opt(key); + if (o == null) { + put(key, new JSONArray().put(value)); + } else if (o instanceof JSONArray) { + put(key, ((JSONArray)o).put(value)); + } else { + throw new JSONException("JSONObject[" + key + + "] is not a JSONArray."); + } + return this; + } + + + /** + * Produce a string from a double. The string "null" will be returned if + * the number is not finite. + * @param d A double. + * @return A String. + */ + static public String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + +// Shave off trailing zeros and decimal point, if possible. + + String s = Double.toString(d); + if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { + while (s.endsWith("0")) { + s = s.substring(0, s.length() - 1); + } + if (s.endsWith(".")) { + s = s.substring(0, s.length() - 1); + } + } + return s; + } + + + /** + * Get the value object associated with a key. + * + * @param key A key string. + * @return The object associated with the key. + * @throws JSONException if the key is not found. + */ + public Object get(String key) throws JSONException { + Object o = opt(key); + if (o == null) { + throw new JSONException("JSONObject[" + quote(key) + + "] not found."); + } + return o; + } + + + /** + * Get the boolean value associated with a key. + * + * @param key A key string. + * @return The truth. + * @throws JSONException + * if the value is not a Boolean or the String "true" or "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object o = get(key); + if (o.equals(Boolean.FALSE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("false"))) { + return false; + } else if (o.equals(Boolean.TRUE) || + (o instanceof String && + ((String)o).equalsIgnoreCase("true"))) { + return true; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a Boolean."); + } + + + /** + * Get the double value associated with a key. + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or + * if the value is not a Number object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + Object o = get(key); + try { + return o instanceof Number ? + ((Number)o).doubleValue() : + Double.valueOf((String)o).doubleValue(); + } catch (Exception e) { + throw new JSONException("JSONObject[" + quote(key) + + "] is not a number."); + } + } + + + /** + * Get the int value associated with a key. If the number value is too + * large for an int, it will be clipped. + * + * @param key A key string. + * @return The integer value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to an integer. + */ + public int getInt(String key) throws JSONException { + Object o = get(key); + return o instanceof Number ? + ((Number)o).intValue() : (int)getDouble(key); + } + + + /** + * Get the JSONArray value associated with a key. + * + * @param key A key string. + * @return A JSONArray which is the value. + * @throws JSONException if the key is not found or + * if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object o = get(key); + if (o instanceof JSONArray) { + return (JSONArray)o; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONArray."); + } + + + /** + * Get the JSONObject value associated with a key. + * + * @param key A key string. + * @return A JSONObject which is the value. + * @throws JSONException if the key is not found or + * if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object o = get(key); + if (o instanceof JSONObject) { + return (JSONObject)o; + } + throw new JSONException("JSONObject[" + quote(key) + + "] is not a JSONObject."); + } + + + /** + * Get the long value associated with a key. If the number value is too + * long for a long, it will be clipped. + * + * @param key A key string. + * @return The long value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to a long. + */ + public long getLong(String key) throws JSONException { + Object o = get(key); + return o instanceof Number ? + ((Number)o).longValue() : (long)getDouble(key); + } + + + /** + * Get the string associated with a key. + * + * @param key A key string. + * @return A string which is the value. + * @throws JSONException if the key is not found. + */ + public String getString(String key) throws JSONException { + return get(key).toString(); + } + + + /** + * Determine if the JSONObject contains a specific key. + * @param key A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.myHashMap.containsKey(key); + } + + + /** + * Determine if the value associated with the key is null or if there is + * no value. + * @param key A key string. + * @return true if there is no value associated with the key or if + * the value is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(opt(key)); + } + + + /** + * Get an enumeration of the keys of the JSONObject. + * + * @return An iterator of the keys. + */ + public Iterator keys() { + return this.myHashMap.keySet().iterator(); + } + + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.myHashMap.size(); + } + + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + JSONArray ja = new JSONArray(); + Iterator keys = keys(); + while (keys.hasNext()) { + ja.put(keys.next()); + } + return ja.length() == 0 ? null : ja; + } + + /** + * Produce a string from a Number. + * @param n A Number + * @return A String. + * @throws JSONException If n is a non-finite number. + */ + static public String numberToString(Number n) + throws JSONException { + if (n == null) { + throw new JSONException("Null pointer"); + } + testValidity(n); + +// Shave off trailing zeros and decimal point, if possible. + + String s = n.toString(); + if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) { + while (s.endsWith("0")) { + s = s.substring(0, s.length() - 1); + } + if (s.endsWith(".")) { + s = s.substring(0, s.length() - 1); + } + } + return s; + } + + + /** + * Get an optional value associated with a key. + * @param key A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.myHashMap.get(key); + } + + + /** + * Get an optional boolean associated with a key. + * It returns false if there is no such key, or if the value is not + * Boolean.TRUE or the String "true". + * + * @param key A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return optBoolean(key, false); + } + + + /** + * Get an optional boolean associated with a key. + * It returns the defaultValue if there is no such key, or if it is not + * a Boolean or the String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + try { + return getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * @param key A key string. + * @param value A Collection value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Collection value) throws JSONException { + put(key, new JSONArray(value)); + return this; + } + + + /** + * Get an optional double associated with a key, + * or NaN if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return optDouble(key, Double.NaN); + } + + + /** + * Get an optional double associated with a key, or the + * defaultValue if there is no such key or if its value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + try { + Object o = opt(key); + return o instanceof Number ? ((Number)o).doubleValue() : + new Double((String)o).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional int value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return optInt(key, 0); + } + + + /** + * Get an optional int value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + try { + return getInt(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional JSONArray associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONArray. + * + * @param key A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + Object o = opt(key); + return o instanceof JSONArray ? (JSONArray)o : null; + } + + + /** + * Get an optional JSONObject associated with a key. + * It returns null if there is no such key, or if its value is not a + * JSONObject. + * + * @param key A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { + Object o = opt(key); + return o instanceof JSONObject ? (JSONObject)o : null; + } + + + /** + * Get an optional long value associated with a key, + * or zero if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return optLong(key, 0); + } + + + /** + * Get an optional long value associated with a key, + * or the default if there is no such key or if the value is not a number. + * If the value is a string, an attempt will be made to evaluate it as + * a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + try { + return getLong(key); + } catch (Exception e) { + return defaultValue; + } + } + + + /** + * Get an optional string associated with a key. + * It returns an empty string if there is no such key. If the value is not + * a string and is not null, then it is coverted to a string. + * + * @param key A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return optString(key, ""); + } + + + /** + * Get an optional string associated with a key. + * It returns the defaultValue if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object o = opt(key); + return o != null ? o.toString() : defaultValue; + } + + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key A key string. + * @param value A boolean which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + put(key, value ? Boolean.TRUE : Boolean.FALSE); + return this; + } + + + /** + * Put a key/double pair in the JSONObject. + * + * @param key A key string. + * @param value A double which is the value. + * @return this. + * @throws JSONException If the key is null or if the number is invalid. + */ + public JSONObject put(String key, double value) throws JSONException { + put(key, new Double(value)); + return this; + } + + + /** + * Put a key/int pair in the JSONObject. + * + * @param key A key string. + * @param value An int which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + put(key, new Integer(value)); + return this; + } + + + /** + * Put a key/long pair in the JSONObject. + * + * @param key A key string. + * @param value A long which is the value. + * @return this. + * @throws JSONException If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + put(key, new Long(value)); + return this; + } + + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * @param key A key string. + * @param value A Map value. + * @return this. + * @throws JSONException + */ + public JSONObject put(String key, Map value) throws JSONException { + put(key, new JSONObject(value)); + return this; + } + + + /** + * Put a key/value pair in the JSONObject. If the value is null, + * then the key will be removed from the JSONObject if it is present. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, + * or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is non-finite number + * or if the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + if (value != null) { + testValidity(value); + this.myHashMap.put(key, value); + } else { + remove(key); + } + return this; + } + + + /** + * Put a key/value pair in the JSONObject, but only if the + * key and the value are both non-null. + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String, + * or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + put(key, value); + } + return this; + } + + + /** + * Produce a string in double quotes with backslash sequences in all the + * right places. A backslash will be inserted within = '\u0080' && c < '\u00a0') || + (c >= '\u2000' && c < '\u2100')) { + t = "000" + Integer.toHexString(c); + sb.append("\\u" + t.substring(t.length() - 4)); + } else { + sb.append(c); + } + } + } + sb.append('"'); + return sb.toString(); + } + + /** + * Remove a name and its value, if present. + * @param key The name to be removed. + * @return The value that was associated with the name, + * or null if there was no value. + */ + public Object remove(String key) { + return this.myHashMap.remove(key); + } + + + /** + * Throw an exception if the object is an NaN or infinite number. + * @param o The object to test. + * @throws JSONException If o is a non-finite number. + */ + static void testValidity(Object o) throws JSONException { + if (o != null) { + if (o instanceof Double) { + if (((Double)o).isInfinite() || ((Double)o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } else if (o instanceof Float) { + if (((Float)o).isInfinite() || ((Float)o).isNaN()) { + throw new JSONException( + "JSON does not allow non-finite numbers."); + } + } + } + } + + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * @param names A JSONArray containing a list of key strings. This + * determines the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace + * is added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + */ + public String toString() { + try { + Iterator keys = keys(); + StringBuffer sb = new StringBuffer("{"); + + while (keys.hasNext()) { + if (sb.length() > 1) { + sb.append(','); + } + Object o = keys.next(); + sb.append(quote(o.toString())); + sb.append(':'); + sb.append(valueToString(this.myHashMap.get(o))); + } + sb.append('}'); + return sb.toString(); + } catch (Exception e) { + return null; + } + } + + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @return a printable, displayable, portable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + public String toString(int indentFactor) throws JSONException { + return toString(indentFactor, 0); + } + + + /** + * Make a prettyprinted JSON text of this JSONObject. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + String toString(int indentFactor, int indent) throws JSONException { + int i; + int n = length(); + if (n == 0) { + return "{}"; + } + Iterator keys = keys(); + StringBuffer sb = new StringBuffer("{"); + int newindent = indent + indentFactor; + Object o; + if (n == 1) { + o = keys.next(); + sb.append(quote(o.toString())); + sb.append(": "); + sb.append(valueToString(this.myHashMap.get(o), indentFactor, + indent)); + } else { + while (keys.hasNext()) { + o = keys.next(); + if (sb.length() > 1) { + sb.append(",\n"); + } else { + sb.append('\n'); + } + for (i = 0; i < newindent; i += 1) { + sb.append(' '); + } + sb.append(quote(o.toString())); + sb.append(": "); + sb.append(valueToString(this.myHashMap.get(o), indentFactor, + newindent)); + } + if (sb.length() > 1) { + sb.append('\n'); + for (i = 0; i < indent; i += 1) { + sb.append(' '); + } + } + } + sb.append('}'); + return sb.toString(); + } + + + /** + * Make a JSON text of an Object value. If the object has an + * value.toJSONString() method, then that method will be used to produce + * the JSON text. The method is required to produce a strictly + * conforming text. If the object does not contain a toJSONString + * method (which is the most common case), then a text will be + * produced by other means. If the value is an array or Collection, + * then a JSONArray will be made from it and its toJSONString method + * will be called. If the value is a MAP, then a JSONObject will be made + * from it and its toJSONString method will be called. Otherwise, the + * value's toString method will be called, and the result will be quoted. + * + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the value is or contains an invalid number. + */ + static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + Object o; + try { + o = ((JSONString)value).toJSONString(); + } catch (Exception e) { + throw new JSONException(e); + } + if (o instanceof String) { + return (String)o; + } + throw new JSONException("Bad value from toJSONString: " + o); + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean || value instanceof JSONObject || + value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + return new JSONObject((Map)value).toString(); + } + if (value instanceof Collection) { + return new JSONArray((Collection)value).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + return quote(value.toString()); + } + + + /** + * Make a prettyprinted JSON text of an object value. + *

+ * Warning: This method assumes that the data structure is acyclical. + * @param value The value to be serialized. + * @param indentFactor The number of spaces to add to each level of + * indentation. + * @param indent The indentation of the top level. + * @return a printable, displayable, transmittable + * representation of the object, beginning + * with { (left brace) and ending + * with } (right brace). + * @throws JSONException If the object contains an invalid number. + */ + static String valueToString(Object value, int indentFactor, int indent) + throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + try { + if (value instanceof JSONString) { + Object o = ((JSONString)value).toJSONString(); + if (o instanceof String) { + return (String)o; + } + } + } catch (Exception e) { + /* forget about it */ + } + if (value instanceof Number) { + return numberToString((Number) value); + } + if (value instanceof Boolean) { + return value.toString(); + } + if (value instanceof JSONObject) { + return ((JSONObject)value).toString(indentFactor, indent); + } + if (value instanceof JSONArray) { + return ((JSONArray)value).toString(indentFactor, indent); + } + return quote(value.toString()); + } + + + /** + * Write the contents of the JSONObject as JSON text to a writer. + * For compactness, no whitespace is added. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + try { + boolean b = false; + Iterator keys = keys(); + writer.write('{'); + + while (keys.hasNext()) { + if (b) { + writer.write(','); + } + Object k = keys.next(); + writer.write(quote(k.toString())); + writer.write(':'); + Object v = this.myHashMap.get(k); + if (v instanceof JSONObject) { + ((JSONObject)v).write(writer); + } else if (v instanceof JSONArray) { + ((JSONArray)v).write(writer); + } else { + writer.write(valueToString(v)); + } + b = true; + } + writer.write('}'); + return writer; + } catch (IOException e) { + throw new JSONException(e); + } + } +} diff --git a/src/main/java/org/json/JSONString.java b/src/main/java/org/json/JSONString.java new file mode 100644 index 0000000..3aba6b7 --- /dev/null +++ b/src/main/java/org/json/JSONString.java @@ -0,0 +1,19 @@ +package org.json; +/** + * The JSONString interface allows a toJSONString() + * method so that a class can change the behavior of + * JSONObject.toString(), JSONArray.toString(), + * and JSONWriter.value(Object). The + * toJSONString method will be used instead of the default behavior + * of using the Object's toString() method and quoting the result. + */ +public interface JSONString { + /** + * The toJSONString method allows a class to produce its own JSON + * serialization. + * + * @return A strictly syntactically correct JSON text. + */ + public String toJSONString(); +} + diff --git a/src/main/java/org/json/JSONStringer.java b/src/main/java/org/json/JSONStringer.java new file mode 100644 index 0000000..0382801 --- /dev/null +++ b/src/main/java/org/json/JSONStringer.java @@ -0,0 +1,79 @@ +package org.json; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import java.io.StringWriter; + +/** + * JSONStringer provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONStringer can produce one JSON text. + *

+ * A JSONStringer instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting cascade style. For example,

+ * myString = new JSONStringer()
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject()
+ *     .toString();
which produces the string
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONStringer adds them for + * you. Objects and arrays can be nested up to 20 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2 + */ +public class JSONStringer extends JSONWriter { + /** + * Make a fresh JSONStringer. It can be used to build one JSON text. + */ + public JSONStringer() { + super(new StringWriter()); + } + + /** + * Return the JSON text. This method is used to obtain the product of the + * JSONStringer instance. It will return null if there was a + * problem in the construction of the JSON text (such as the calls to + * array were not properly balanced with calls to + * endArray). + * @return The JSON text. + */ + public String toString() { + return this.mode == 'd' ? this.writer.toString() : null; + } +} + diff --git a/src/main/java/org/json/JSONTokener.java b/src/main/java/org/json/JSONTokener.java new file mode 100644 index 0000000..a1b54d9 --- /dev/null +++ b/src/main/java/org/json/JSONTokener.java @@ -0,0 +1,462 @@ +package org.json; + +/* +Copyright (c) 2002 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * A JSONTokener takes a source string and extracts characters and tokens from + * it. It is used by the JSONObject and JSONArray constructors to parse + * JSON source strings. + * @author JSON.org + * @version 2 + */ +public class JSONTokener { + + /** + * The index of the next character. + */ + private int myIndex; + + + /** + * The source string being tokenized. + */ + private String mySource; + + + /** + * Construct a JSONTokener from a string. + * + * @param s A source string. + */ + public JSONTokener(String s) { + this.myIndex = 0; + this.mySource = s; + } + + + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + */ + public void back() { + if (this.myIndex > 0) { + this.myIndex -= 1; + } + } + + + + /** + * Get the hex value of a character (base16). + * @param c A character between '0' and '9' or between 'A' and 'F' or + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. + */ + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'A' && c <= 'F') { + return c - ('A' - 10); + } + if (c >= 'a' && c <= 'f') { + return c - ('a' - 10); + } + return -1; + } + + + /** + * Determine if the source string still contains characters that next() + * can consume. + * @return true if not yet at the end of the source. + */ + public boolean more() { + return this.myIndex < this.mySource.length(); + } + + + /** + * Get the next character in the source string. + * + * @return The next character, or 0 if past the end of the source string. + */ + public char next() { + if (more()) { + char c = this.mySource.charAt(this.myIndex); + this.myIndex += 1; + return c; + } + return 0; + } + + + /** + * Consume the next character, and check that it matches a specified + * character. + * @param c The character to match. + * @return The character. + * @throws JSONException if the character does not match. + */ + public char next(char c) throws JSONException { + char n = next(); + if (n != c) { + throw syntaxError("Expected '" + c + "' and instead saw '" + + n + "'"); + } + return n; + } + + + /** + * Get the next n characters. + * + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException + * Substring bounds error if there are not + * n characters remaining in the source string. + */ + public String next(int n) throws JSONException { + int i = this.myIndex; + int j = i + n; + if (j >= this.mySource.length()) { + throw syntaxError("Substring bounds error"); + } + this.myIndex += n; + return this.mySource.substring(i, j); + } + + + /** + * Get the next char in the string, skipping whitespace + * and comments (slashslash, slashstar, and hash). + * @throws JSONException + * @return A character, or 0 if there are no more characters. + */ + public char nextClean() throws JSONException { + for (;;) { + char c = next(); + if (c == '/') { + switch (next()) { + case '/': + do { + c = next(); + } while (c != '\n' && c != '\r' && c != 0); + break; + case '*': + for (;;) { + c = next(); + if (c == 0) { + throw syntaxError("Unclosed comment"); + } + if (c == '*') { + if (next() == '/') { + break; + } + back(); + } + } + break; + default: + back(); + return '/'; + } + } else if (c == '#') { + do { + c = next(); + } while (c != '\n' && c != '\r' && c != 0); + } else if (c == 0 || c > ' ') { + return c; + } + } + } + + + /** + * Return the characters up to the next close quote character. + * Backslash processing is done. The formal JSON format does not + * allow strings in single quotes, but an implementation is allowed to + * accept them. + * @param quote The quoting character, either + * " (double quote) or + * ' (single quote). + * @return A String. + * @throws JSONException Unterminated string. + */ + public String nextString(char quote) throws JSONException { + char c; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = next(); + switch (c) { + case 0: + case '\n': + case '\r': + throw syntaxError("Unterminated string"); + case '\\': + c = next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char)Integer.parseInt(next(4), 16)); + break; + case 'x' : + sb.append((char) Integer.parseInt(next(2), 16)); + break; + default: + sb.append(c); + } + break; + default: + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + } + + + /** + * Get the text up but not including the specified character or the + * end of line, whichever comes first. + * @param d A delimiter character. + * @return A string. + */ + public String nextTo(char d) { + StringBuffer sb = new StringBuffer(); + for (;;) { + char c = next(); + if (c == d || c == 0 || c == '\n' || c == '\r') { + if (c != 0) { + back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the text up but not including one of the specified delimeter + * characters or the end of line, whichever comes first. + * @param delimiters A set of delimiter characters. + * @return A string, trimmed. + */ + public String nextTo(String delimiters) { + char c; + StringBuffer sb = new StringBuffer(); + for (;;) { + c = next(); + if (delimiters.indexOf(c) >= 0 || c == 0 || + c == '\n' || c == '\r') { + if (c != 0) { + back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + + /** + * Get the next value. The value can be a Boolean, Double, Integer, + * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. + * @throws JSONException If syntax error. + * + * @return An object. + */ + public Object nextValue() throws JSONException { + char c = nextClean(); + String s; + + switch (c) { + case '"': + case '\'': + return nextString(c); + case '{': + back(); + return new JSONObject(this); + case '[': + back(); + return new JSONArray(this); + } + + /* + * Handle unquoted text. This could be the values true, false, or + * null, or it can be a number. An implementation (such as this one) + * is allowed to also accept non-standard forms. + * + * Accumulate characters until we reach the end of the text or a + * formatting character. + */ + + StringBuffer sb = new StringBuffer(); + char b = c; + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = next(); + } + back(); + + /* + * If it is true, false, or null, return the proper value. + */ + + s = sb.toString().trim(); + if (s.equals("")) { + throw syntaxError("Missing value"); + } + if (s.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + if (s.equalsIgnoreCase("false")) { + return Boolean.FALSE; + } + if (s.equalsIgnoreCase("null")) { + return JSONObject.NULL; + } + + /* + * If it might be a number, try converting it. We support the 0- and 0x- + * conventions. If a number cannot be produced, then the value will just + * be a string. Note that the 0-, 0x-, plus, and implied string + * conventions are non-standard. A JSON parser is free to accept + * non-JSON forms as long as it accepts all correct JSON forms. + */ + + if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') { + if (b == '0') { + if (s.length() > 2 && + (s.charAt(1) == 'x' || s.charAt(1) == 'X')) { + try { + return new Integer(Integer.parseInt(s.substring(2), + 16)); + } catch (Exception e) { + /* Ignore the error */ + } + } else { + try { + return new Integer(Integer.parseInt(s, 8)); + } catch (Exception e) { + /* Ignore the error */ + } + } + } + try { + return new Integer(s); + } catch (Exception e) { + try { + return new Long(s); + } catch (Exception f) { + try { + return new Double(s); + } catch (Exception g) { + return s; + } + } + } + } + return s; + } + + + /** + * Skip characters until the next character is the requested character. + * If the requested character is not found, no characters are skipped. + * @param to A character to skip to. + * @return The requested character, or zero if the requested character + * is not found. + */ + public char skipTo(char to) { + char c; + int index = this.myIndex; + do { + c = next(); + if (c == 0) { + this.myIndex = index; + return c; + } + } while (c != to); + back(); + return c; + } + + + /** + * Skip characters until past the requested string. + * If it is not found, we are left at the end of the source. + * @param to A string to skip past. + */ + public boolean skipPast(String to) { + this.myIndex = this.mySource.indexOf(to, this.myIndex); + if (this.myIndex < 0) { + this.myIndex = this.mySource.length(); + return false; + } + this.myIndex += to.length(); + return true; + + } + + + /** + * Make a JSONException to signal a syntax error. + * + * @param message The error message. + * @return A JSONException object, suitable for throwing + */ + public JSONException syntaxError(String message) { + return new JSONException(message + toString()); + } + + + /** + * Make a printable string of this JSONTokener. + * + * @return " at character [this.myIndex] of [this.mySource]" + */ + public String toString() { + return " at character " + this.myIndex + " of " + this.mySource; + } +} diff --git a/src/main/java/org/json/JSONWriter.java b/src/main/java/org/json/JSONWriter.java new file mode 100644 index 0000000..c3fc4c0 --- /dev/null +++ b/src/main/java/org/json/JSONWriter.java @@ -0,0 +1,319 @@ +package org.json; + +import java.io.IOException; +import java.io.Writer; + +/* +Copyright (c) 2006 JSON.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * JSONWriter provides a quick and convenient way of producing JSON text. + * The texts produced strictly conform to JSON syntax rules. No whitespace is + * added, so the results are ready for transmission or storage. Each instance of + * JSONWriter can produce one JSON text. + *

+ * A JSONWriter instance provides a value method for appending + * values to the + * text, and a key + * method for adding keys before values in objects. There are array + * and endArray methods that make and bound array values, and + * object and endObject methods which make and bound + * object values. All of these methods return the JSONWriter instance, + * permitting a cascade style. For example,

+ * new JSONWriter(myWriter)
+ *     .object()
+ *         .key("JSON")
+ *         .value("Hello, World!")
+ *     .endObject();
which writes
+ * {"JSON":"Hello, World!"}
+ *

+ * The first method called must be array or object. + * There are no methods for adding commas or colons. JSONWriter adds them for + * you. Objects and arrays can be nested up to 20 levels deep. + *

+ * This can sometimes be easier than using a JSONObject to build a string. + * @author JSON.org + * @version 2 + */ +public class JSONWriter { + private static final int maxdepth = 20; + + /** + * The comma flag determines if a comma should be output before the next + * value. + */ + private boolean comma; + + /** + * The current mode. Values: + * 'a' (array), + * 'd' (done), + * 'i' (initial), + * 'k' (key), + * 'o' (object). + */ + protected char mode; + + /** + * The object/array stack. + */ + private char stack[]; + + /** + * The stack top index. A value of 0 indicates that the stack is empty. + */ + private int top; + + /** + * The writer that will receive the output. + */ + protected Writer writer; + + /** + * Make a fresh JSONWriter. It can be used to build one JSON text. + */ + public JSONWriter(Writer w) { + this.comma = false; + this.mode = 'i'; + this.stack = new char[maxdepth]; + this.top = 0; + this.writer = w; + } + + /** + * Append a value. + * @param s A string value. + * @return this + * @throws JSONException If the value is out of sequence. + */ + private JSONWriter append(String s) throws JSONException { + if (s == null) { + throw new JSONException("Null pointer"); + } + if (this.mode == 'o' || this.mode == 'a') { + try { + if (this.comma && this.mode == 'a') { + this.writer.write(','); + } + this.writer.write(s); + } catch (IOException e) { + throw new JSONException(e); + } + if (this.mode == 'o') { + this.mode = 'k'; + } + this.comma = true; + return this; + } + throw new JSONException("Value out of sequence."); + } + + /** + * Begin appending a new array. All values until the balancing + * endArray will be appended to this array. The + * endArray method must be called to mark the array's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter array() throws JSONException { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { + this.push('a'); + this.append("["); + this.comma = false; + return this; + } + throw new JSONException("Misplaced array."); + } + + /** + * End something. + * @param m Mode + * @param c Closing character + * @return this + * @throws JSONException If unbalanced. + */ + private JSONWriter end(char m, char c) throws JSONException { + if (this.mode != m) { + throw new JSONException(m == 'o' ? "Misplaced endObject." : + "Misplaced endArray."); + } + this.pop(m); + try { + this.writer.write(c); + } catch (IOException e) { + throw new JSONException(e); + } + this.comma = true; + return this; + } + + /** + * End an array. This method most be called to balance calls to + * array. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endArray() throws JSONException { + return this.end('a', ']'); + } + + /** + * End an object. This method most be called to balance calls to + * object. + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endObject() throws JSONException { + return this.end('k', '}'); + } + + /** + * Append a key. The key will be associated with the next value. In an + * object, every value must be preceded by a key. + * @param s A key string. + * @return this + * @throws JSONException If the key is out of place. For example, keys + * do not belong in arrays or if the key is null. + */ + public JSONWriter key(String s) throws JSONException { + if (s == null) { + throw new JSONException("Null key."); + } + if (this.mode == 'k') { + try { + if (this.comma) { + this.writer.write(','); + } + this.writer.write(JSONObject.quote(s)); + this.writer.write(':'); + this.comma = false; + this.mode = 'o'; + return this; + } catch (IOException e) { + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + + + /** + * Begin appending a new object. All keys and values until the balancing + * endObject will be appended to this object. The + * endObject method must be called to mark the object's end. + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter object() throws JSONException { + if (this.mode == 'i') { + this.mode = 'o'; + } + if (this.mode == 'o' || this.mode == 'a') { + this.append("{"); + this.push('k'); + this.comma = false; + return this; + } + throw new JSONException("Misplaced object."); + + } + + + /** + * Pop an array or object scope. + * @param c The scope to close. + * @throws JSONException If nesting is wrong. + */ + private void pop(char c) throws JSONException { + if (this.top <= 0 || this.stack[this.top - 1] != c) { + throw new JSONException("Nesting error."); + } + this.top -= 1; + this.mode = this.top == 0 ? 'd' : this.stack[this.top - 1]; + } + + /** + * Push an array or object scope. + * @param c The scope to open. + * @throws JSONException If nesting is too deep. + */ + private void push(char c) throws JSONException { + if (this.top >= maxdepth) { + throw new JSONException("Nesting too deep."); + } + this.stack[this.top] = c; + this.mode = c; + this.top += 1; + } + + + /** + * Append either the value true or the value + * false. + * @param b A boolean. + * @return this + * @throws JSONException + */ + public JSONWriter value(boolean b) throws JSONException { + return this.append(b ? "true" : "false"); + } + + /** + * Append a double value. + * @param d A double. + * @return this + * @throws JSONException If the number is not finite. + */ + public JSONWriter value(double d) throws JSONException { + return this.value(new Double(d)); + } + + /** + * Append a long value. + * @param l A long. + * @return this + * @throws JSONException + */ + public JSONWriter value(long l) throws JSONException { + return this.append(Long.toString(l)); + } + + + /** + * Append an object value. + * @param o The object to append. It can be null, or a Boolean, Number, + * String, JSONObject, or JSONArray, or an object with a toJSONString() + * method. + * @return this + * @throws JSONException If the value is out of sequence. + */ + public JSONWriter value(Object o) throws JSONException { + return this.append(JSONObject.valueToString(o)); + } +} + diff --git a/src/main/java/scratch.jpage b/src/main/java/scratch.jpage new file mode 100644 index 0000000..47c4ead --- /dev/null +++ b/src/main/java/scratch.jpage @@ -0,0 +1,16 @@ +System.getProperty("user.dir")(java.lang.String) D:\workspace\j2js-compiler new java.io.File(System.getProperty("user.dir")).getName()(java.lang.String) j2js-compiler String s = "aaas\nsdsd"; s.replaceAll("\\n", "\\\\n") false == false new java.io.File("d:\\data").isDirectory() java.net.URLEncoder.encode("foo/ bar")(java.lang.String) foo%2F+bar(java.lang.String) foo%2Fbar java.io.File file = new java.io.File("data/foo"); file.getCanonicalPath()(java.lang.String) D:\workspace\j2js-Compiler\data\foo java.net.URI base = new java.net.URI("file:docs/tutorial1.html"); java.net.URI uri = new java.net.URI("file:projects/j2js-Demos/src/site/HelloWorld1.html.html"); base.relativize(uri) java.io.File baseFile = new java.io.File("src"); baseFile.getCanonicalPath() (java.lang.String) D:\workspace\j2js-Compiler\src int i; double d = 1; for (i=0; i<30; i++) { d = d*(365-i)/365; } 1-d new Character('|').toString() "a^b:c,b".split("[:,]")(java.lang.String[]) [a^b, c, b] "01".matches("0*[1-9]\\d*")(boolean) true "foo\r\nbar".replaceAll("\\r", "").split("\n"); "%a".split("%")(java.lang.String[]) [, a] java.util.Formatter f = new java.util.Formatter(); String.format("%1$ta, %1$td %1$tb %1$tY %1$tT %1$tZ", new java.util.Date()); "j2js.demo.powerdash.Shapes#compare(java.lang.Object,java.lang.Object)".matches(".*#compare\\(java\\.lang\\.Object,java\\.lang\\.Object\\)");(boolean) true String pattern = "*#compare(java.lang.Object,java.lang.Object)"; pattern = pattern.replaceAll("\\.", "\\\\."); pattern = pattern.replaceAll("\\*", ".*"); pattern = pattern.replaceAll("\\(", "\\\\("); pattern = pattern.replaceAll("\\)", "\\\\)"); "j2js.demo.powerdash.Shapes#compare(java.lang.Object,java.lang.Object)".matches(pattern) "foo(bar".replaceAll("\\(", "\\\\\\\\(") "foo.bar".split("\\.") "fo.o.bar".replaceAll("\\.[^.]*$", "") java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("id\\s*=\\s*[\'\"]j2js-loader[\'\"].*src\\s*=\\s*[\'\"]([^\'\"]+)/0\\.js[\'\"]", java.util.regex.Pattern.DOTALL | java.util.regex.Pattern.CASE_INSENSITIVE); java.util.regex.Matcher matcher = pattern.matcher("\n\n"); matcher.find(); matcher.group(1) matcher.find(); matcher.group(0); matcher.find(); matcher.group(0); "foo\n\\bar".replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\\\\\\", "\\\\")(java.lang.String) foo +\bar " // foo".matches("\\s*//.*") " /*dsd".matches("\\s*/\\*.*") "+NaN ".matches("(\\+|\\-)?NaN") java.net.URI uri = new java.net.URI("http://foo.bar/foo/skin/text.txt"); java.net.URI root = new java.net.URI("http://foo.bar/foo/print?a=b"); root.resolve("../").relativize(uri)(java.net.URI) foo/skin/text.txt java.net.URI rel = root.relativize(uri1); if (!rel.isAbsolute()) { int i = rel.getPath().split("/").length - 1; String s = ""; for (int j=0; j + + + + + + + + + +

Generic J2JS Automation Client

+ +
+ + + + + + diff --git a/src/main/resources/instructions.xml b/src/main/resources/instructions.xml new file mode 100644 index 0000000..e1d5afa --- /dev/null +++ b/src/main/resources/instructions.xml @@ -0,0 +1,1920 @@ + + + + aaload + 50 +
+ arrayref + index + objectref +
+
+ + + aastore + 83 +
+ arrayref + index + value +
+
+ + + aconst_null + 1 +
+ null +
+
+ + + aload + 25 +
+ index + objectref +
+
+ + + aload_0 + 42 +
+ objectref +
+
+ + + aload_1 + 43 +
+ objectref +
+
+ + + aload_2 + 44 +
+ objectref +
+
+ + + aload_3 + 45 +
+ objectref +
+
+ + + anewarray + 189 +
+ index + count + arrayref +
+
+ + + areturn + 176 +
+ objectref + + +
+ + + arraylength + 190 +
+ arrayref + length +
+
+ + + astore + 58 +
+ index + objectref +
+
+ + + astore_0 + 75 +
+ objectref +
+
+ + + astore_1 + 76 +
+ objectref +
+
+ + + astore_2 + 77 +
+ objectref +
+
+ + + astore_3 + 78 +
+ objectref +
+
+ + + athrow + 191 +
+ objectref + objectref +
+
+ + + baload + 51 +
+ arrayref + index + value +
+
+ + + bastore + 84 +
+ arrayref + index + value +
+
+ + + bipush + 16 +
+ byte + value +
+
+ + + + new + 187 +
+ index + objectref +
+
+ + + newarray + 188 +
+ atype + count + arrayref +
+
+ + + nop + 0 +
+
+
+ + + pop + 87 +
+ value +
+
+ + + pop2 + 88 +
+ value +
+
+ value2 + value1 +
+
+ + + putfield + 181 +
+ index + objectref + value +
+
+ + + putstatic + 179 +
+ index + value +
+
+ + + ret + 169 +
+ index +
+
+ + + return + 177 +
+ + +
+ + + saload + 53 +
+ arrayref + index + value +
+
+ + + sastore + 86 +
+ array + index + value +
+
+ + + sipush + 17 +
+ byte byte1 + byte byte2 + value +
+
+ + + swap + 95 +
+ value2 + value1 + value1 + value2 +
+
+ + + tableswitch + 170 +
+ padding + padding + padding + default + low + high + + index +
+
+ + + wide + 196 +
+
+
+ + + caload + 52 +
+ arrayref + index + value +
+
+ + + castore + 85 +
+ arrayref + index + value +
+
+ + + checkcast + 192 +
+ index + objectref + objectref +
+
+ + + d2f + 144 +
+ value + result +
+
+ + + d2i + 142 +
+ value + result +
+
+ + + d2l + 143 +
+ value + result +
+
+ + + dadd + 99 +
+ value1 + value2 + result +
+
+ + + daload + 49 +
+ arrayref + index + value +
+
+ + + dastore + 82 +
+ arrayref + index + value +
+
+ + + dcmpl + 151 +
+ value1 + value2 + result +
+
+ + + dcmpg + 152 +
+ value1 + value2 + result +
+
+ + + dconst_0 + 14 +
+ d +
+
+ + + dconst_1 + 15 +
+ d +
+
+ + + ddiv + 111 +
+ value1 + value2 + result +
+
+ + + dload + 24 +
+ index + value +
+
+ + + dload_0 + 38 +
+ value +
+
+ + + dload_1 + 39 +
+ value +
+
+ + + dload_2 + 40 +
+ value +
+
+ + + dload_3 + 41 +
+ value +
+
+ + + dmul + 107 +
+ value1 + value2 + result +
+
+ + + dneg + 119 +
+ value + result +
+
+ + + drem + 115 +
+ value1 + value2 + result +
+
+ + + dreturn + 175 +
+ value + + +
+ + + dstore + 57 +
+ index + value +
+
+ + + dstore_0 + 71 +
+ value +
+
+ + + dstore_1 + 72 +
+ value +
+
+ + + dstore_2 + 73 +
+ value +
+
+ + + dstore_3 + 74 +
+ value +
+
+ + + dsub + 103 +
+ value1 + value2 + result +
+
+ + + dup + 89 +
+ value + value + value +
+
+ + + dup_x1 + 90 +
+ value2 + value1 + value1 + value2 + value1 +
+
+ + + dup_x2 + 91 +
+ value3 + value2 + value1 + value1 + value3 + value2 + value1 +
+
+ value2 + value1 + value1 + value2 + value1 +
+
+ + + dup2 + 92 +
+ value2 + value1 + value2 + value1 + value2 + value1 +
+
+ value + value + value +
+
+ + + dup2_x1 + 93 +
+ value3 + value2 + value1 + value2 + value1 + value3 + value2 + value1 +
+
+ value2 + value1 + value1 + value2 + value1 +
+
+ + + dup2_x2 + 94 +
+ value4 + value3 + value2 + value1 + value2 + value1 + value4 + value3 + value2 + value1 +
+
+ value3 + value2 + value1 + value1 + value3 + value2 + value1 +
+
+ value3 + value2 + value1 + value2 + value1 + value3 + value2 + value1 +
+
+ value2 + value1 + value1 + value2 + value1 +
+
+ + + f2d + 141 +
+ value + result +
+
+ + + f2i + 139 +
+ value + result +
+
+ + + f2l + 140 +
+ value + result +
+
+ + + fadd + 98 +
+ value1 + value2 + result +
+
+ + + faload + 48 +
+ arrayref + index + value +
+
+ + + fastore + 81 +
+ arrayref + index + value +
+
+ + + fcmpl + 149 +
+ value1 + value2 + result +
+
+ + + fcmpg + 150 +
+ value1 + value2 + result +
+
+ + + fconst_0 + 11 +
+ f +
+
+ + + fconst_1 + 12 +
+ f +
+
+ + + fconst_2 + 13 +
+ f +
+
+ + + fdiv + 110 +
+ value1 + value2 + result +
+
+ + + fload + 23 +
+ index + value +
+
+ + + fload_0 + 34 +
+ value +
+
+ + + fload_1 + 35 +
+ value +
+
+ + + fload_2 + 36 +
+ value +
+
+ + + fload_3 + 37 +
+ value +
+
+ + + fmul + 106 +
+ value1 + value2 + result +
+
+ + + fneg + 118 +
+ value + result +
+
+ + + frem + 114 +
+ value1 + value2 + result +
+
+ + + freturn + 174 +
+ value + + +
+ + + fstore + 56 +
+ index + value +
+
+ + + fstore_0 + 67 +
+ value +
+
+ + + fstore_1 + 68 +
+ value +
+
+ + + fstore_2 + 69 +
+ value +
+
+ + + fstore_3 + 70 +
+ value +
+
+ + + fsub + 102 +
+ value1 + value2 + result +
+
+ + + getfield + 180 +
+ index + objectref + value +
+
+ + + getstatic + 178 +
+ index + value +
+
+ + + goto + 167 +
+ branch +
+
+ + + goto_w + 200 +
+ branch +
+
+ + + i2b + 145 +
+ value + result +
+
+ + + i2c + 146 +
+ value + result +
+
+ + + i2d + 135 +
+ value + result +
+
+ + + i2f + 134 +
+ value + result +
+
+ + + i2l + 133 +
+ value + result +
+
+ + + i2s + 147 +
+ value + result +
+
+ + + iadd + 96 +
+ value1 + value2 + result +
+
+ + + iaload + 46 +
+ arrayref + index + value +
+
+ + + iand + 126 +
+ value1 + value2 + result +
+
+ + + iastore + 79 +
+ arrayref + index + value +
+
+ + + iconst_m1 + 2 +
+ i +
+
+ + + iconst_0 + 3 +
+ i +
+
+ + + iconst_1 + 4 +
+ i +
+
+ + + iconst_2 + 5 +
+ i +
+
+ + + iconst_3 + 6 +
+ i +
+
+ + + iconst_4 + 7 +
+ i +
+
+ + + iconst_5 + 8 +
+ i +
+
+ + + idiv + 108 +
+ value1 + value2 + result +
+
+ + + if_acmpeq + 165 +
+ branch + value1 + value2 +
+
+ + + if_acmpne + 166 +
+ branch + value1 + value2 +
+
+ + + if_icmpeq + 159 +
+ branch + value1 + value2 +
+
+ + + if_icmpne + 160 +
+ branch + value1 + value2 +
+
+ + if_icmplt + 161 +
+ branch + value1 + value2 +
+
+ + if_icmpge + 162 +
+ branch + value1 + value2 +
+
+ + if_icmpgt + 163 +
+ branch + value1 + value2 +
+
+ + if_icmple + 164 +
+ branch + value1 + value2 +
+
+ + + ifeq + 153 +
+ branch + value +
+
+ + + ifne + 154 +
+ branch + value +
+
+ + + iflt + 155 +
+ branch + value +
+
+ + + ifge + 156 +
+ branch + value +
+
+ + + ifgt + 157 +
+ branch + value +
+
+ + + ifle + 158 +
+ branch + value +
+
+ + + ifnonnull + 199 +
+ branch + value +
+
+ + + ifnull + 198 +
+ branch + value +
+
+ + + iinc + 132 +
+ index + const +
+
+ + + iload + 21 +
+ index + value +
+
+ + + iload_0 + 26 +
+ value +
+
+ + + iload_1 + 27 +
+ value +
+
+ + + iload_2 + 28 +
+ value +
+
+ + + iload_3 + 29 +
+ value +
+
+ + + imul + 104 +
+ value1 + value2 + result +
+
+ + + ineg + 116 +
+ value + result +
+
+ + + instanceof + 193 +
+ index + objectref + result +
+
+ + + invokeinterface + 185 +
+ index + count + 0 + objectref + arg1 + ... + argN +
+
+ + + index + invokespecial + 183 +
+ objectref + arg1 + ... + argN +
+
+ + + invokestatic + 184 +
+ index + arg1 + ... + argN +
+
+ + + invokevirtual + 182 +
+ objectref + arg1 + ... + argN +
+
+ + + ior + 128 +
+ value1 + value2 + result +
+
+ + + irem + 112 +
+ value1 + value2 + result +
+
+ + + ireturn + 172 +
+ value + + +
+ + + ishl + 120 +
+ value1 + value2 + result +
+
+ + + ishr + 122 +
+ value1 + value2 + result +
+
+ + + istore + 54 +
+ index + value +
+
+ + + istore_0 + 59 +
+ value +
+
+ + + istore_1 + 60 +
+ value +
+
+ + + istore_2 + 61 +
+ value +
+
+ + + istore_3 + 62 +
+ value +
+
+ + + isub + 100 +
+ value1 + value2 + result +
+
+ + + iushr + 124 +
+ value1 + value2 + result +
+
+ + + ixor + 130 +
+ value1 + value2 + result +
+
+ + + jsr + 168 +
+ branch + address +
+
+ + + jsr_w + 201 +
+ branch + address +
+
+ + + l2d + 138 +
+ value + result +
+
+ + + l2f + 137 +
+ value + result +
+
+ + + l2i + 136 +
+ value + result +
+
+ + + ladd + 97 +
+ value1 + value2 + result +
+
+ + + laload + 47 +
+ arrayref + index + value +
+
+ + + land + 127 +
+ value1 + value2 + result +
+
+ + + lastore + 80 +
+ arrayref + index + value +
+
+ + + lcmp + 148 +
+ value1 + value2 + result +
+
+ + + lconst_0 + 9 +
+ l +
+
+ + + lconst_1 + 10 +
+ l +
+
+ + + ldc + 18 +
+ index + value +
+
+ + + ldc_w + 19 +
+ value +
+
+ + + ldc2_w + 20 +
+ value +
+
+ + + ldiv + 109 +
+ value1 + value2 + result +
+
+ + + lload + 22 +
+ index + value +
+
+ + + lload_0 + 30 +
+ value +
+
+ + + lload_1 + 31 +
+ value +
+
+ + + lload_2 + 32 +
+ value +
+
+ + + lload_3 + 33 +
+ value +
+
+ + + lmul + 105 +
+ value1 + value2 + result +
+
+ + + lneg + 117 +
+ value + result +
+
+ + + lookupswitch + 171 +
+ padding + padding + padding + default + npairs + + key +
+
+ + + lor + 129 +
+ value1 + value2 + result +
+
+ + + lrem + 113 +
+ value1 + value2 + result +
+
+ + + lreturn + 173 +
+ value + + +
+ + + lshl + 121 +
+ value1 + value2 + result +
+
+ + + lshr + 123 +
+ value1 + value2 + result +
+
+ + + lstore + 55 +
+ index + value +
+
+ + + lstore_0 + 63 +
+ value +
+
+ + + lstore_1 + 64 +
+ value +
+
+ + + lstore_2 + 65 +
+ value +
+
+ + + lstore_3 + 66 +
+ value +
+
+ + + lsub + 101 +
+ value1 + value2 + result +
+
+ + + lushr + 125 +
+ value1 + value2 + result +
+
+ + + lxor + 131 +
+ value1 + value2 + result +
+
+ + + monitorenter + 194 +
+ objectref +
+
+ + + monitorexit + 195 +
+ objectref +
+
+ + + multianewarray + 197 +
+ index + dimension N + count1 + ... + countN + arrayref +
+
+ + + xxxunusedxxx + 186 +
+
+
+ +
diff --git a/src/main/resources/instructions.xsl b/src/main/resources/instructions.xsl new file mode 100644 index 0000000..fd40da1 --- /dev/null +++ b/src/main/resources/instructions.xsl @@ -0,0 +1,50 @@ + + + + + + + package com.j2js; + public class Const { + + public static InstructionType[] instructionTypes = new InstructionType[]; + + + + static { + InstructionType i; + Form f; + + + + } + } + + + + + i = new InstructionType((short), "", ); + + instructionTypes[] = i; + + + + f = new Form(); + f.setIns(new Form.Value[]{}); + f.setOuts(new Form.Value[]{}); + f.setOperands(new Form.Value[]{}); + i.setForm(f, ); + + + + public static final int = ; + + + new Form.Value("",""), + + new Form.Value("",""), + + new Form.Value("",""), + + diff --git a/src/main/resources/j2js.properties b/src/main/resources/j2js.properties new file mode 100644 index 0000000..2a76496 --- /dev/null +++ b/src/main/resources/j2js.properties @@ -0,0 +1,25 @@ +j2js.version = 1.2 + +# See documentation in file compile.html. +# TODO: Add classes needed for annotation support! +# Begin preTaintedSignatures +j2js.preTaintedSignatures = \ + java.lang.NullPointerException#(java.lang.String)void; \ + java.lang.RuntimeException#(java.lang.String)void; \ + java.lang.Throwable#printStackTrace()void; \ + java.lang.Throwable#fillInStackTrace()java.lang.Throwable; \ + java.lang.Throwable#toString()java.lang.String; \ + com.j2js.prodmode.ContextImpl#premain(java.lang.String,java.lang.String)void; \ + java.lang.reflect.Proxy#invoke(\ + java.lang.reflect.Proxy,\ + java.lang.String,java.lang.Object[])java.lang.Object; \ + j2js.client.Engine#getEngine()j2js.client.Engine; +# End preTaintedSignatures + +# See documentation in file compile.html. +# Begin taintIfInstantiated +j2js.taintIfInstantiated = \ + *#handleEvent(*)void; \ + *#compare(java.lang.Object,java.lang.Object)int; \ + *#invoke(*)void +# End taintIfInstantiated \ No newline at end of file diff --git a/src/main/resources/notfound.html b/src/main/resources/notfound.html new file mode 100644 index 0000000..c8d2499 --- /dev/null +++ b/src/main/resources/notfound.html @@ -0,0 +1,9 @@ + + +404 Not Found + +

Not Found

+

The requested URL $target was not found on this server.

+
+
Embedded Jetty at $ServerName Port $port
+ diff --git a/src/main/resources/smalllogo.gif b/src/main/resources/smalllogo.gif new file mode 100644 index 0000000..7b25eaf Binary files /dev/null and b/src/main/resources/smalllogo.gif differ