[NOTHING CHANGED] Move to maven and massive clean and fixup #93

Closed
farmboy0 wants to merge 43 commits from farmboy0/master into master
144 changed files with 17201 additions and 14311 deletions
Showing only changes of commit e7e6190f9c - Show all commits

390
.ide/formatter.xml Normal file
View File

@@ -0,0 +1,390 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="21">
<profile kind="CodeFormatterProfile" name="LuaJ" version="21">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_preserve"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

View File

@@ -21,61 +21,64 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* String buffer for use in string library methods, optimized for production * String buffer for use in string library methods, optimized for production of
* of StrValue instances. * StrValue instances.
* <p> * <p>
* The buffer can begin initially as a wrapped {@link LuaValue} * The buffer can begin initially as a wrapped {@link LuaValue} and only when
* and only when concatenation actually occurs are the bytes first copied. * concatenation actually occurs are the bytes first copied.
* <p> * <p>
* To convert back to a {@link LuaValue} again, * To convert back to a {@link LuaValue} again, the function
* the function {@link Buffer#value()} is used. * {@link Buffer#value()} is used.
*
* @see LuaValue * @see LuaValue
* @see LuaValue#buffer() * @see LuaValue#buffer()
* @see LuaString * @see LuaString
*/ */
public final class Buffer { public final class Buffer {
/** Default capacity for a buffer: 64 */ /** Default capacity for a buffer: 64 */
private static final int DEFAULT_CAPACITY = 64; private static final int DEFAULT_CAPACITY = 64;
/** Shared static array with no bytes */ /** Shared static array with no bytes */
private static final byte[] NOBYTES = {}; private static final byte[] NOBYTES = {};
/** Bytes in this buffer */ /** Bytes in this buffer */
private byte[] bytes; private byte[] bytes;
/** Length of this buffer */ /** Length of this buffer */
private int length; private int length;
/** Offset into the byte array */ /** Offset into the byte array */
private int offset; private int offset;
/** Value of this buffer, when not represented in bytes */ /** Value of this buffer, when not represented in bytes */
private LuaValue value; private LuaValue value;
/** /**
* Create buffer with default capacity * Create buffer with default capacity
*
* @see #DEFAULT_CAPACITY * @see #DEFAULT_CAPACITY
*/ */
public Buffer() { public Buffer() {
this(DEFAULT_CAPACITY); this(DEFAULT_CAPACITY);
} }
/** /**
* Create buffer with specified initial capacity * Create buffer with specified initial capacity
*
* @param initialCapacity the initial capacity * @param initialCapacity the initial capacity
*/ */
public Buffer( int initialCapacity ) { public Buffer(int initialCapacity) {
bytes = new byte[ initialCapacity ]; bytes = new byte[initialCapacity];
length = 0; length = 0;
offset = 0; offset = 0;
value = null; value = null;
} }
/** /**
* Create buffer with specified initial value * Create buffer with specified initial value
*
* @param value the initial value * @param value the initial value
*/ */
public Buffer(LuaValue value) { public Buffer(LuaValue value) {
@@ -84,16 +87,18 @@ public final class Buffer {
this.value = value; this.value = value;
} }
/** /**
* Get buffer contents as a {@link LuaValue} * Get buffer contents as a {@link LuaValue}
*
* @return value as a {@link LuaValue}, converting as necessary * @return value as a {@link LuaValue}, converting as necessary
*/ */
public LuaValue value() { public LuaValue value() {
return value != null? value: this.tostring(); return value != null? value: this.tostring();
} }
/** /**
* Set buffer contents as a {@link LuaValue} * Set buffer contents as a {@link LuaValue}
*
* @param value value to set * @param value value to set
*/ */
public Buffer setvalue(LuaValue value) { public Buffer setvalue(LuaValue value) {
@@ -102,145 +107,170 @@ public final class Buffer {
this.value = value; this.value = value;
return this; return this;
} }
/** /**
* Convert the buffer to a {@link LuaString} * Convert the buffer to a {@link LuaString}
*
* @return the value as a {@link LuaString} * @return the value as a {@link LuaString}
*/ */
public final LuaString tostring() { public final LuaString tostring() {
realloc( length, 0 ); realloc(length, 0);
return LuaString.valueOf( bytes, offset, length ); return LuaString.valueOf(bytes, offset, length);
} }
/** /**
* Convert the buffer to a Java String * Convert the buffer to a Java String
*
* @return the value as a Java String * @return the value as a Java String
*/ */
public String tojstring() { public String tojstring() {
return value().tojstring(); return value().tojstring();
} }
/** /**
* Convert the buffer to a Java String * Convert the buffer to a Java String
*
* @return the value as a Java String * @return the value as a Java String
*/ */
public String toString() { public String toString() {
return tojstring(); return tojstring();
} }
/** /**
* Append a single byte to the buffer. * Append a single byte to the buffer.
*
* @return {@code this} to allow call chaining * @return {@code this} to allow call chaining
*/ */
public final Buffer append( byte b ) { public final Buffer append(byte b) {
makeroom( 0, 1 ); makeroom(0, 1);
bytes[ offset + length++ ] = b; bytes[offset+length++] = b;
return this; return this;
} }
/** /**
* Append a {@link LuaValue} to the buffer. * Append a {@link LuaValue} to the buffer.
*
* @return {@code this} to allow call chaining * @return {@code this} to allow call chaining
*/ */
public final Buffer append( LuaValue val ) { public final Buffer append(LuaValue val) {
append( val.strvalue() ); append(val.strvalue());
return this; return this;
} }
/** /**
* Append a {@link LuaString} to the buffer. * Append a {@link LuaString} to the buffer.
*
* @return {@code this} to allow call chaining * @return {@code this} to allow call chaining
*/ */
public final Buffer append( LuaString str ) { public final Buffer append(LuaString str) {
final int n = str.m_length; final int n = str.m_length;
makeroom( 0, n ); makeroom(0, n);
str.copyInto( 0, bytes, offset + length, n ); str.copyInto(0, bytes, offset+length, n);
length += n; length += n;
return this; return this;
} }
/** /**
* Append a Java String to the buffer. * Append a Java String to the buffer. The Java string will be converted to
* The Java string will be converted to bytes using the UTF8 encoding. * bytes using the UTF8 encoding.
*
* @return {@code this} to allow call chaining * @return {@code this} to allow call chaining
* @see LuaString#encodeToUtf8(char[], int, byte[], int) * @see LuaString#encodeToUtf8(char[], int, byte[], int)
*/ */
public final Buffer append( String str ) { public final Buffer append(String str) {
char[] c = str.toCharArray(); char[] c = str.toCharArray();
final int n = LuaString.lengthAsUtf8( c ); final int n = LuaString.lengthAsUtf8(c);
makeroom( 0, n ); makeroom(0, n);
LuaString.encodeToUtf8( c, c.length, bytes, offset + length ); LuaString.encodeToUtf8(c, c.length, bytes, offset+length);
length += n; length += n;
return this; return this;
} }
/** Concatenate this buffer onto a {@link LuaValue} /**
* @param lhs the left-hand-side value onto which we are concatenating {@code this} * Concatenate this buffer onto a {@link LuaValue}
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining. * @return {@link Buffer} for use in call chaining.
*/ */
public Buffer concatTo(LuaValue lhs) { public Buffer concatTo(LuaValue lhs) {
return setvalue(lhs.concat(value())); return setvalue(lhs.concat(value()));
} }
/** Concatenate this buffer onto a {@link LuaString} /**
* @param lhs the left-hand-side value onto which we are concatenating {@code this} * Concatenate this buffer onto a {@link LuaString}
*
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining. * @return {@link Buffer} for use in call chaining.
*/ */
public Buffer concatTo(LuaString lhs) { public Buffer concatTo(LuaString lhs) {
return value!=null&&!value.isstring()? setvalue(lhs.concat(value)): prepend(lhs); return value != null && !value.isstring()? setvalue(lhs.concat(value)): prepend(lhs);
} }
/** Concatenate this buffer onto a {@link LuaNumber} /**
* Concatenate this buffer onto a {@link LuaNumber}
* <p> * <p>
* The {@link LuaNumber} will be converted to a string before concatenating. * The {@link LuaNumber} will be converted to a string before concatenating.
* @param lhs the left-hand-side value onto which we are concatenating {@code this} *
* @param lhs the left-hand-side value onto which we are concatenating
* {@code this}
* @return {@link Buffer} for use in call chaining. * @return {@link Buffer} for use in call chaining.
*/ */
public Buffer concatTo(LuaNumber lhs) { public Buffer concatTo(LuaNumber lhs) {
return value!=null&&!value.isstring()? setvalue(lhs.concat(value)): prepend(lhs.strvalue()); return value != null && !value.isstring()? setvalue(lhs.concat(value)): prepend(lhs.strvalue());
} }
/** Concatenate bytes from a {@link LuaString} onto the front of this buffer /**
* @param s the left-hand-side value which we will concatenate onto the front of {@code this} * Concatenate bytes from a {@link LuaString} onto the front of this buffer
*
* @param s the left-hand-side value which we will concatenate onto the
* front of {@code this}
* @return {@link Buffer} for use in call chaining. * @return {@link Buffer} for use in call chaining.
*/ */
public Buffer prepend(LuaString s) { public Buffer prepend(LuaString s) {
int n = s.m_length; int n = s.m_length;
makeroom( n, 0 ); makeroom(n, 0);
System.arraycopy( s.m_bytes, s.m_offset, bytes, offset-n, n ); System.arraycopy(s.m_bytes, s.m_offset, bytes, offset-n, n);
offset -= n; offset -= n;
length += n; length += n;
value = null; value = null;
return this; return this;
} }
/** Ensure there is enough room before and after the bytes. /**
* @param nbefore number of unused bytes which must precede the data after this completes * Ensure there is enough room before and after the bytes.
* @param nafter number of unused bytes which must follow the data after this completes *
* @param nbefore number of unused bytes which must precede the data after
* this completes
* @param nafter number of unused bytes which must follow the data after
* this completes
*/ */
public final void makeroom( int nbefore, int nafter ) { public final void makeroom(int nbefore, int nafter) {
if ( value != null ) { if (value != null) {
LuaString s = value.strvalue(); LuaString s = value.strvalue();
value = null; value = null;
length = s.m_length; length = s.m_length;
offset = nbefore; offset = nbefore;
bytes = new byte[nbefore+length+nafter]; bytes = new byte[nbefore+length+nafter];
System.arraycopy(s.m_bytes, s.m_offset, bytes, offset, length); System.arraycopy(s.m_bytes, s.m_offset, bytes, offset, length);
} else if ( offset+length+nafter > bytes.length || offset<nbefore ) { } else if (offset+length+nafter > bytes.length || offset < nbefore) {
int n = nbefore+length+nafter; int n = nbefore+length+nafter;
int m = n<32? 32: n<length*2? length*2: n; int m = n < 32? 32: n < length*2? length*2: n;
realloc( m, nbefore==0? 0: m-length-nafter ); realloc(m, nbefore == 0? 0: m-length-nafter);
} }
} }
/** Reallocate the internal storage for the buffer /**
* @param newSize the size of the buffer to use * Reallocate the internal storage for the buffer
* @param newOffset the offset to use *
* @param newSize the size of the buffer to use
* @param newOffset the offset to use
*/ */
private final void realloc( int newSize, int newOffset ) { private final void realloc(int newSize, int newOffset) {
if ( newSize != bytes.length ) { if (newSize != bytes.length) {
byte[] newBytes = new byte[ newSize ]; byte[] newBytes = new byte[newSize];
System.arraycopy( bytes, offset, newBytes, newOffset, length ); System.arraycopy(bytes, offset, newBytes, newOffset, length);
bytes = newBytes; bytes = newBytes;
offset = newOffset; offset = newOffset;
} }

View File

@@ -33,73 +33,94 @@ import org.luaj.vm2.lib.PackageLib;
import org.luaj.vm2.lib.ResourceFinder; import org.luaj.vm2.lib.ResourceFinder;
/** /**
* Global environment used by luaj. Contains global variables referenced by executing lua. * Global environment used by luaj. Contains global variables referenced by
* executing lua.
* <p> * <p>
* *
* <h3>Constructing and Initializing Instances</h3> * <h3>Constructing and Initializing Instances</h3> Typically, this is
* Typically, this is constructed indirectly by a call to * constructed indirectly by a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}, * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}, and then used to
* and then used to load lua scripts for execution as in the following example. * load lua scripts for execution as in the following example.
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call(); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* The creates a complete global environment with the standard libraries loaded. * The creates a complete global environment with the standard libraries loaded.
* <p> * <p>
* For specialized circumstances, the Globals may be constructed directly and loaded * For specialized circumstances, the Globals may be constructed directly and
* with only those libraries that are needed, for example. * loaded with only those libraries that are needed, for example.
* <pre> {@code
* Globals globals = new Globals();
* globals.load( new BaseLib() );
* } </pre>
* *
* <h3>Loading and Executing Lua Code</h3> * <pre>
* Globals contains convenience functions to load and execute lua source code given a Reader. * {
* A simple example is: * &#64;code
* <pre> {@code * Globals globals = new Globals();
* globals.load(new BaseLib());
* }
* </pre>
*
* <h3>Loading and Executing Lua Code</h3> Globals contains convenience
* functions to load and execute lua source code given a Reader. A simple
* example is:
*
* <pre>
* {@code
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call(); * globals.load( new StringReader("print 'hello'"), "main.lua" ).call();
* } </pre> * }
* </pre>
* *
* <h3>Fine-Grained Control of Compiling and Loading Lua</h3> * <h3>Fine-Grained Control of Compiling and Loading Lua</h3> Executable
* Executable LuaFunctions are created from lua code in several steps * LuaFunctions are created from lua code in several steps
* <ul> * <ul>
* <li>find the resource using the platform's {@link ResourceFinder} * <li>find the resource using the platform's {@link ResourceFinder}
* <li>compile lua to lua bytecode using {@link Compiler} * <li>compile lua to lua bytecode using {@link Compiler}
* <li>load lua bytecode to a {@link Prototype} using {@link Undumper} * <li>load lua bytecode to a {@link Prototype} using {@link Undumper}
* <li>construct {@link LuaClosure} from {@link Prototype} with {@link Globals} using {@link Loader} * <li>construct {@link LuaClosure} from {@link Prototype} with {@link Globals}
* using {@link Loader}
* </ul> * </ul>
* <p> * <p>
* There are alternate flows when the direct lua-to-Java bytecode compiling {@link org.luaj.vm2.luajc.LuaJC} is used. * There are alternate flows when the direct lua-to-Java bytecode compiling
* {@link org.luaj.vm2.luajc.LuaJC} is used.
* <ul> * <ul>
* <li>compile lua to lua bytecode using {@link Compiler} or load precompiled code using {@link Undumper} * <li>compile lua to lua bytecode using {@link Compiler} or load precompiled
* <li>convert lua bytecode to equivalent Java bytecode using {@link org.luaj.vm2.luajc.LuaJC} that implements {@link Loader} directly * code using {@link Undumper}
* <li>convert lua bytecode to equivalent Java bytecode using
* {@link org.luaj.vm2.luajc.LuaJC} that implements {@link Loader} directly
* </ul> * </ul>
* *
* <h3>Java Field</h3> * <h3>Java Field</h3> Certain public fields are provided that contain the
* Certain public fields are provided that contain the current values of important global state: * current values of important global state:
* <ul> * <ul>
* <li>{@link #STDIN} Current value for standard input in the laaded {@link IoLib}, if any. * <li>{@link #STDIN} Current value for standard input in the laaded
* <li>{@link #STDOUT} Current value for standard output in the loaded {@link IoLib}, if any. * {@link IoLib}, if any.
* <li>{@link #STDERR} Current value for standard error in the loaded {@link IoLib}, if any. * <li>{@link #STDOUT} Current value for standard output in the loaded
* {@link IoLib}, if any.
* <li>{@link #STDERR} Current value for standard error in the loaded
* {@link IoLib}, if any.
* <li>{@link #finder} Current loaded {@link ResourceFinder}, if any. * <li>{@link #finder} Current loaded {@link ResourceFinder}, if any.
* <li>{@link #compiler} Current loaded {@link Compiler}, if any. * <li>{@link #compiler} Current loaded {@link Compiler}, if any.
* <li>{@link #undumper} Current loaded {@link Undumper}, if any. * <li>{@link #undumper} Current loaded {@link Undumper}, if any.
* <li>{@link #loader} Current loaded {@link Loader}, if any. * <li>{@link #loader} Current loaded {@link Loader}, if any.
* </ul> * </ul>
* *
* <h3>Lua Environment Variables</h3> * <h3>Lua Environment Variables</h3> When using
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} or {@link org.luaj.vm2.lib.jme.JmePlatform}, * {@link org.luaj.vm2.lib.jse.JsePlatform} or
* these environment variables are created within the Globals. * {@link org.luaj.vm2.lib.jme.JmePlatform}, these environment variables are
* created within the Globals.
* <ul> * <ul>
* <li>"_G" Pointer to this Globals. * <li>"_G" Pointer to this Globals.
* <li>"_VERSION" String containing the version of luaj. * <li>"_VERSION" String containing the version of luaj.
* </ul> * </ul>
* *
* <h3>Use in Multithreaded Environments</h3> * <h3>Use in Multithreaded Environments</h3> In a multi-threaded server
* In a multi-threaded server environment, each server thread should create one Globals instance, * environment, each server thread should create one Globals instance, which
* which will be logically distinct and not interfere with each other, but share certain * will be logically distinct and not interfere with each other, but share
* static immutable resources such as class data and string data. * certain static immutable resources such as class data and string data.
* <p> * <p>
* *
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
@@ -115,7 +136,7 @@ import org.luaj.vm2.lib.ResourceFinder;
public class Globals extends LuaTable { public class Globals extends LuaTable {
/** The current default input stream. */ /** The current default input stream. */
public InputStream STDIN = null; public InputStream STDIN = null;
/** The current default output stream. */ /** The current default output stream. */
public PrintStream STDOUT = System.out; public PrintStream STDOUT = System.out;
@@ -125,28 +146,42 @@ public class Globals extends LuaTable {
/** The installed ResourceFinder for looking files by name. */ /** The installed ResourceFinder for looking files by name. */
public ResourceFinder finder; public ResourceFinder finder;
/** The currently running thread. Should not be changed by non-library code. */ /**
* The currently running thread. Should not be changed by non-library code.
*/
public LuaThread running = new LuaThread(this); public LuaThread running = new LuaThread(this);
/** The BaseLib instance loaded into this Globals */ /** The BaseLib instance loaded into this Globals */
public BaseLib baselib; public BaseLib baselib;
/** The PackageLib instance loaded into this Globals */ /** The PackageLib instance loaded into this Globals */
public PackageLib package_; public PackageLib package_;
/** The DebugLib instance loaded into this Globals, or null if debugging is not enabled */ /**
* The DebugLib instance loaded into this Globals, or null if debugging is
* not enabled
*/
public DebugLib debuglib; public DebugLib debuglib;
/** Interface for module that converts a Prototype into a LuaFunction with an environment. */ /**
* Interface for module that converts a Prototype into a LuaFunction with an
* environment.
*/
public interface Loader { public interface Loader {
/** Convert the prototype into a LuaFunction with the supplied environment. */ /**
* Convert the prototype into a LuaFunction with the supplied
* environment.
*/
LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException; LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException;
} }
/** Interface for module that converts lua source text into a prototype. */ /** Interface for module that converts lua source text into a prototype. */
public interface Compiler { public interface Compiler {
/** Compile lua source into a Prototype. The InputStream is assumed to be in UTF-8. */ /**
* Compile lua source into a Prototype. The InputStream is assumed to be
* in UTF-8.
*/
Prototype compile(InputStream stream, String chunkname) throws IOException; Prototype compile(InputStream stream, String chunkname) throws IOException;
} }
@@ -155,100 +190,142 @@ public class Globals extends LuaTable {
/** Load the supplied input stream into a prototype. */ /** Load the supplied input stream into a prototype. */
Prototype undump(InputStream stream, String chunkname) throws IOException; Prototype undump(InputStream stream, String chunkname) throws IOException;
} }
/** Check that this object is a Globals object, and return it, otherwise throw an error. */ /**
* Check that this object is a Globals object, and return it, otherwise
* throw an error.
*/
public Globals checkglobals() { public Globals checkglobals() {
return this; return this;
} }
/** The installed loader. /**
* @see Loader */ * The installed loader.
*
* @see Loader
*/
public Loader loader; public Loader loader;
/** The installed compiler. /**
* @see Compiler */ * The installed compiler.
*
* @see Compiler
*/
public Compiler compiler; public Compiler compiler;
/** The installed undumper. /**
* @see Undumper */ * The installed undumper.
*
* @see Undumper
*/
public Undumper undumper; public Undumper undumper;
/** Convenience function for loading a file that is either binary lua or lua source. /**
* Convenience function for loading a file that is either binary lua or lua
* source.
*
* @param filename Name of the file to load. * @param filename Name of the file to load.
* @return LuaValue that can be call()'ed or invoke()'ed. * @return LuaValue that can be call()'ed or invoke()'ed.
* @throws LuaError if the file could not be loaded. * @throws LuaError if the file could not be loaded.
*/ */
public LuaValue loadfile(String filename) { public LuaValue loadfile(String filename) {
try { try {
return load(finder.findResource(filename), "@"+filename, "bt", this); return load(finder.findResource(filename), "@" + filename, "bt", this);
} catch (Exception e) { } catch (Exception e) {
return error("load "+filename+": "+e); return error("load " + filename + ": " + e);
} }
} }
/** Convenience function to load a string value as a script. Must be lua source. /**
* @param script Contents of a lua script, such as "print 'hello, world.'" * Convenience function to load a string value as a script. Must be lua
* source.
*
* @param script Contents of a lua script, such as "print 'hello,
* world.'"
* @param chunkname Name that will be used within the chunk as the source. * @param chunkname Name that will be used within the chunk as the source.
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled. * @throws LuaError if the script could not be compiled.
*/ */
public LuaValue load(String script, String chunkname) { public LuaValue load(String script, String chunkname) {
return load(new StrReader(script), chunkname); return load(new StrReader(script), chunkname);
} }
/** Convenience function to load a string value as a script. Must be lua source. /**
* Convenience function to load a string value as a script. Must be lua
* source.
*
* @param script Contents of a lua script, such as "print 'hello, world.'" * @param script Contents of a lua script, such as "print 'hello, world.'"
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled. * @throws LuaError if the script could not be compiled.
*/ */
public LuaValue load(String script) { public LuaValue load(String script) {
return load(new StrReader(script), script); return load(new StrReader(script), script);
} }
/** Convenience function to load a string value as a script with a custom environment. /**
* Must be lua source. * Convenience function to load a string value as a script with a custom
* @param script Contents of a lua script, such as "print 'hello, world.'" * environment. Must be lua source.
* @param chunkname Name that will be used within the chunk as the source. *
* @param environment LuaTable to be used as the environment for the loaded function. * @param script Contents of a lua script, such as "print 'hello,
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded
* function.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled. * @throws LuaError if the script could not be compiled.
*/ */
public LuaValue load(String script, String chunkname, LuaTable environment) { public LuaValue load(String script, String chunkname, LuaTable environment) {
return load(new StrReader(script), chunkname, environment); return load(new StrReader(script), chunkname, environment);
} }
/** Load the content form a reader as a text file. Must be lua source. /**
* The source is converted to UTF-8, so any characters appearing in quoted literals * Load the content form a reader as a text file. Must be lua source. The
* above the range 128 will be converted into multiple bytes. * source is converted to UTF-8, so any characters appearing in quoted
* @param reader Reader containing text of a lua script, such as "print 'hello, world.'" * literals above the range 128 will be converted into multiple bytes.
*
* @param reader Reader containing text of a lua script, such as "print
* 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source. * @param chunkname Name that will be used within the chunk as the source.
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled. * @throws LuaError if the script could not be compiled.
*/ */
public LuaValue load(Reader reader, String chunkname) { public LuaValue load(Reader reader, String chunkname) {
return load(new UTF8Stream(reader), chunkname, "t", this); return load(new UTF8Stream(reader), chunkname, "t", this);
} }
/** Load the content form a reader as a text file, supplying a custom environment. /**
* Must be lua source. The source is converted to UTF-8, so any characters * Load the content form a reader as a text file, supplying a custom
* appearing in quoted literals above the range 128 will be converted into * environment. Must be lua source. The source is converted to UTF-8, so any
* multiple bytes. * characters appearing in quoted literals above the range 128 will be
* @param reader Reader containing text of a lua script, such as "print 'hello, world.'" * converted into multiple bytes.
* @param chunkname Name that will be used within the chunk as the source. *
* @param environment LuaTable to be used as the environment for the loaded function. * @param reader Reader containing text of a lua script, such as "print
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * 'hello, world.'"
* @param chunkname Name that will be used within the chunk as the source.
* @param environment LuaTable to be used as the environment for the loaded
* function.
* @return LuaValue that may be executed via .call(), .invoke(), or
* .method() calls.
* @throws LuaError if the script could not be compiled. * @throws LuaError if the script could not be compiled.
*/ */
public LuaValue load(Reader reader, String chunkname, LuaTable environment) { public LuaValue load(Reader reader, String chunkname, LuaTable environment) {
return load(new UTF8Stream(reader), chunkname, "t", environment); return load(new UTF8Stream(reader), chunkname, "t", environment);
} }
/** Load the content form an input stream as a binary chunk or text file. /**
* @param is InputStream containing a lua script or compiled lua" * Load the content form an input stream as a binary chunk or text file.
* @param chunkname Name that will be used within the chunk as the source. *
* @param mode String containing 'b' or 't' or both to control loading as binary or text or either. * @param is InputStream containing a lua script or compiled lua"
* @param environment LuaTable to be used as the environment for the loaded function. * @param chunkname Name that will be used within the chunk as the source.
* */ * @param mode String containing 'b' or 't' or both to control
* loading as binary or text or either.
* @param environment LuaTable to be used as the environment for the loaded
* function.
*/
public LuaValue load(InputStream is, String chunkname, String mode, LuaValue environment) { public LuaValue load(InputStream is, String chunkname, String mode, LuaValue environment) {
try { try {
Prototype p = loadPrototype(is, chunkname, mode); Prototype p = loadPrototype(is, chunkname, mode);
@@ -256,16 +333,20 @@ public class Globals extends LuaTable {
} catch (LuaError l) { } catch (LuaError l) {
throw l; throw l;
} catch (Exception e) { } catch (Exception e) {
return error("load "+chunkname+": "+e); return error("load " + chunkname + ": " + e);
} }
} }
/** Load lua source or lua binary from an input stream into a Prototype. /**
* The InputStream is either a binary lua chunk starting with the lua binary chunk signature, * Load lua source or lua binary from an input stream into a Prototype. The
* or a text input file. If it is a text input file, it is interpreted as a UTF-8 byte sequence. * InputStream is either a binary lua chunk starting with the lua binary
* @param is Input stream containing a lua script or compiled lua" * chunk signature, or a text input file. If it is a text input file, it is
* interpreted as a UTF-8 byte sequence.
*
* @param is Input stream containing a lua script or compiled lua"
* @param chunkname Name that will be used within the chunk as the source. * @param chunkname Name that will be used within the chunk as the source.
* @param mode String containing 'b' or 't' or both to control loading as binary or text or either. * @param mode String containing 'b' or 't' or both to control loading
* as binary or text or either.
*/ */
public Prototype loadPrototype(InputStream is, String chunkname, String mode) throws IOException { public Prototype loadPrototype(InputStream is, String chunkname, String mode) throws IOException {
if (mode.indexOf('b') >= 0) { if (mode.indexOf('b') >= 0) {
@@ -282,21 +363,25 @@ public class Globals extends LuaTable {
if (mode.indexOf('t') >= 0) { if (mode.indexOf('t') >= 0) {
return compilePrototype(is, chunkname); return compilePrototype(is, chunkname);
} }
error("Failed to load prototype "+chunkname+" using mode '"+mode+"'"); error("Failed to load prototype " + chunkname + " using mode '" + mode + "'");
return null; return null;
} }
/** Compile lua source from a Reader into a Prototype. The characters in the reader /**
* are converted to bytes using the UTF-8 encoding, so a string literal containing * Compile lua source from a Reader into a Prototype. The characters in the
* characters with codepoints 128 or above will be converted into multiple bytes. * reader are converted to bytes using the UTF-8 encoding, so a string
* literal containing characters with codepoints 128 or above will be
* converted into multiple bytes.
*/ */
public Prototype compilePrototype(Reader reader, String chunkname) throws IOException { public Prototype compilePrototype(Reader reader, String chunkname) throws IOException {
return compilePrototype(new UTF8Stream(reader), chunkname); return compilePrototype(new UTF8Stream(reader), chunkname);
} }
/** Compile lua source from an InputStream into a Prototype. /**
* The input is assumed to be UTf-8, but since bytes in the range 128-255 are passed along as * Compile lua source from an InputStream into a Prototype. The input is
* literal bytes, any ASCII-compatible encoding such as ISO 8859-1 may also be used. * assumed to be UTf-8, but since bytes in the range 128-255 are passed
* along as literal bytes, any ASCII-compatible encoding such as ISO 8859-1
* may also be used.
*/ */
public Prototype compilePrototype(InputStream stream, String chunkname) throws IOException { public Prototype compilePrototype(InputStream stream, String chunkname) throws IOException {
if (compiler == null) if (compiler == null)
@@ -304,9 +389,13 @@ public class Globals extends LuaTable {
return compiler.compile(stream, chunkname); return compiler.compile(stream, chunkname);
} }
/** Function which yields the current thread. /**
* @param args Arguments to supply as return values in the resume function of the resuming thread. * Function which yields the current thread.
* @return Values supplied as arguments to the resume() call that reactivates this thread. *
* @param args Arguments to supply as return values in the resume function
* of the resuming thread.
* @return Values supplied as arguments to the resume() call that
* reactivates this thread.
*/ */
public Varargs yield(Varargs args) { public Varargs yield(Varargs args) {
if (running == null || running.isMainThread()) if (running == null || running.isMainThread())
@@ -318,23 +407,27 @@ public class Globals extends LuaTable {
/** Reader implementation to read chars from a String in JME or JSE. */ /** Reader implementation to read chars from a String in JME or JSE. */
static class StrReader extends Reader { static class StrReader extends Reader {
final String s; final String s;
int i = 0; int i = 0;
final int n; final int n;
StrReader(String s) { StrReader(String s) {
this.s = s; this.s = s;
n = s.length(); n = s.length();
} }
public void close() throws IOException { public void close() throws IOException {
i = n; i = n;
} }
public int read() throws IOException { public int read() throws IOException {
return i < n ? s.charAt(i++) : -1; return i < n? s.charAt(i++): -1;
} }
public int read(char[] cbuf, int off, int len) throws IOException { public int read(char[] cbuf, int off, int len) throws IOException {
int j = 0; int j = 0;
for (; j < len && i < n; ++j, ++i) for (; j < len && i < n; ++j, ++i)
cbuf[off+j] = s.charAt(i); cbuf[off+j] = s.charAt(i);
return j > 0 || len == 0 ? j : -1; return j > 0 || len == 0? j: -1;
} }
} }
@@ -343,49 +436,61 @@ public class Globals extends LuaTable {
*/ */
abstract static class AbstractBufferedStream extends InputStream { abstract static class AbstractBufferedStream extends InputStream {
protected byte[] b; protected byte[] b;
protected int i = 0, j = 0; protected int i = 0, j = 0;
protected AbstractBufferedStream(int buflen) { protected AbstractBufferedStream(int buflen) {
this.b = new byte[buflen]; this.b = new byte[buflen];
} }
abstract protected int avail() throws IOException; abstract protected int avail() throws IOException;
public int read() throws IOException { public int read() throws IOException {
int a = avail(); int a = avail();
return (a <= 0 ? -1 : 0xff & b[i++]); return (a <= 0? -1: 0xff & b[i++]);
} }
public int read(byte[] b) throws IOException { public int read(byte[] b) throws IOException {
return read(b, 0, b.length); return read(b, 0, b.length);
} }
public int read(byte[] b, int i0, int n) throws IOException { public int read(byte[] b, int i0, int n) throws IOException {
int a = avail(); int a = avail();
if (a <= 0) return -1; if (a <= 0)
return -1;
final int n_read = Math.min(a, n); final int n_read = Math.min(a, n);
System.arraycopy(this.b, i, b, i0, n_read); System.arraycopy(this.b, i, b, i0, n_read);
i += n_read; i += n_read;
return n_read; return n_read;
} }
public long skip(long n) throws IOException { public long skip(long n) throws IOException {
final long k = Math.min(n, j - i); final long k = Math.min(n, j-i);
i += k; i += k;
return k; return k;
} }
public int available() throws IOException { public int available() throws IOException {
return j - i; return j-i;
} }
} }
/** Simple converter from Reader to InputStream using UTF8 encoding that will work /**
* on both JME and JSE. * Simple converter from Reader to InputStream using UTF8 encoding that will
* This class may be moved to its own package in the future. * work on both JME and JSE. This class may be moved to its own package in
* the future.
*/ */
static class UTF8Stream extends AbstractBufferedStream { static class UTF8Stream extends AbstractBufferedStream {
private final char[] c = new char[32]; private final char[] c = new char[32];
private final Reader r; private final Reader r;
UTF8Stream(Reader r) { UTF8Stream(Reader r) {
super(96); super(96);
this.r = r; this.r = r;
} }
protected int avail() throws IOException { protected int avail() throws IOException {
if (i < j) return j - i; if (i < j)
return j-i;
int n = r.read(c); int n = r.read(c);
if (n < 0) if (n < 0)
return -1; return -1;
@@ -399,31 +504,38 @@ public class Globals extends LuaTable {
j = LuaString.encodeToUtf8(c, n, b, i = 0); j = LuaString.encodeToUtf8(c, n, b, i = 0);
return j; return j;
} }
public void close() throws IOException { public void close() throws IOException {
r.close(); r.close();
} }
} }
/** Simple buffered InputStream that supports mark. /**
* Used to examine an InputStream for a 4-byte binary lua signature, * Simple buffered InputStream that supports mark. Used to examine an
* and fall back to text input when the signature is not found, * InputStream for a 4-byte binary lua signature, and fall back to text
* as well as speed up normal compilation and reading of lua scripts. * input when the signature is not found, as well as speed up normal
* This class may be moved to its own package in the future. * compilation and reading of lua scripts. This class may be moved to its
* own package in the future.
*/ */
static class BufferedStream extends AbstractBufferedStream { static class BufferedStream extends AbstractBufferedStream {
private final InputStream s; private final InputStream s;
public BufferedStream(InputStream s) { public BufferedStream(InputStream s) {
this(128, s); this(128, s);
} }
BufferedStream(int buflen, InputStream s) { BufferedStream(int buflen, InputStream s) {
super(buflen); super(buflen);
this.s = s; this.s = s;
} }
protected int avail() throws IOException { protected int avail() throws IOException {
if (i < j) return j - i; if (i < j)
if (j >= b.length) i = j = 0; return j-i;
if (j >= b.length)
i = j = 0;
// leave previous bytes in place to implement mark()/reset(). // leave previous bytes in place to implement mark()/reset().
int n = s.read(b, j, b.length - j); int n = s.read(b, j, b.length-j);
if (n < 0) if (n < 0)
return -1; return -1;
if (n == 0) { if (n == 0) {
@@ -436,21 +548,25 @@ public class Globals extends LuaTable {
j += n; j += n;
return n; return n;
} }
public void close() throws IOException { public void close() throws IOException {
s.close(); s.close();
} }
public synchronized void mark(int n) { public synchronized void mark(int n) {
if (i > 0 || n > b.length) { if (i > 0 || n > b.length) {
byte[] dest = n > b.length ? new byte[n] : b; byte[] dest = n > b.length? new byte[n]: b;
System.arraycopy(b, i, dest, 0, j - i); System.arraycopy(b, i, dest, 0, j-i);
j -= i; j -= i;
i = 0; i = 0;
b = dest; b = dest;
} }
} }
public boolean markSupported() { public boolean markSupported() {
return true; return true;
} }
public synchronized void reset() throws IOException { public synchronized void reset() throws IOException {
i = 0; i = 0;
} }

View File

@@ -25,116 +25,141 @@ import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* Class to undump compiled lua bytecode into a {@link Prototype} instances. * Class to undump compiled lua bytecode into a {@link Prototype} instances.
* <p> * <p>
* The {@link LoadState} class provides the default {@link Globals.Undumper} * The {@link LoadState} class provides the default {@link Globals.Undumper}
* which is used to undump a string of bytes that represent a lua binary file * which is used to undump a string of bytes that represent a lua binary file
* using either the C-based lua compiler, or luaj's * using either the C-based lua compiler, or luaj's
* {@link org.luaj.vm2.compiler.LuaC} compiler. * {@link org.luaj.vm2.compiler.LuaC} compiler.
* <p> * <p>
* The canonical method to load and execute code is done * The canonical method to load and execute code is done indirectly using the
* indirectly using the Globals: * Globals:
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* LuaValue chunk = globasl.load("print('hello, world')", "main.lua"); * {
* chunk.call(); * &#64;code
* } </pre> * Globals globals = JsePlatform.standardGlobals();
* This should work regardless of which {@link Globals.Compiler} or {@link Globals.Undumper} * LuaValue chunk = globasl.load("print('hello, world')", "main.lua");
* have been installed. * chunk.call();
* <p> * }
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or * </pre>
* {@link org.luaj.vm2.lib.jme.JmePlatform} *
* to construct globals, the {@link LoadState} default undumper is installed * This should work regardless of which {@link Globals.Compiler} or
* as the default {@link Globals.Undumper}. * {@link Globals.Undumper} have been installed.
* <p> * <p>
* * By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState} class * {@link org.luaj.vm2.lib.jme.JmePlatform} to construct globals, the
: * {@link LoadState} default undumper is installed as the default
* <pre> {@code * {@link Globals.Undumper}.
* Globals globals = JsePlatform.standardGlobals(); * <p>
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua"); *
* ByteArrayOutputStream o = new ByteArrayOutputStream(); * A lua binary file is created via the {@link org.luaj.vm2.compiler.DumpState}
* org.luaj.vm2.compiler.DumpState.dump(p, o, false); * class :
* byte[] lua_binary_file_bytes = o.toByteArray(); *
* } </pre> * <pre>
* * {
* The {@link LoadState}'s default undumper {@link #instance} * &#64;code
* may be used directly to undump these bytes: * Globals globals = JsePlatform.standardGlobals();
* <pre> {@code * Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream();
* org.luaj.vm2.compiler.DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
* </pre>
*
* The {@link LoadState}'s default undumper {@link #instance} may be used
* directly to undump these bytes:
*
* <pre>
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua"); * Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
* LuaClosure c = new LuaClosure(p, globals); * LuaClosure c = new LuaClosure(p, globals);
* c.call(); * c.call();
* } </pre> * }
* * </pre>
* *
* More commonly, the {@link Globals.Undumper} may be used to undump them: *
* <pre> {@code * More commonly, the {@link Globals.Undumper} may be used to undump them:
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b"); *
* LuaClosure c = new LuaClosure(p, globals); * <pre>
* c.call(); * {
* } </pre> * &#64;code
* * Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* @see Globals.Compiler * LuaClosure c = new LuaClosure(p, globals);
* @see Globals.Undumper * c.call();
* @see LuaClosure * }
* @see LuaFunction * </pre>
* @see org.luaj.vm2.compiler.LuaC *
* @see org.luaj.vm2.luajc.LuaJC * @see Globals.Compiler
* @see Globals#compiler * @see Globals.Undumper
* @see Globals#load(InputStream, String, LuaValue) * @see LuaClosure
*/ * @see LuaFunction
* @see org.luaj.vm2.compiler.LuaC
* @see org.luaj.vm2.luajc.LuaJC
* @see Globals#compiler
* @see Globals#load(InputStream, String, LuaValue)
*/
public class LoadState { public class LoadState {
/** Shared instance of Globals.Undumper to use loading prototypes from binary lua files */ /**
* Shared instance of Globals.Undumper to use loading prototypes from binary
* lua files
*/
public static final Globals.Undumper instance = new GlobalsUndumper(); public static final Globals.Undumper instance = new GlobalsUndumper();
/** format corresponding to non-number-patched lua, all numbers are floats or doubles */ /**
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; * format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/** format corresponding to non-number-patched lua, all numbers are ints */ /** format corresponding to non-number-patched lua, all numbers are ints */
public static final int NUMBER_FORMAT_INTS_ONLY = 1; public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */ /**
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; * format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
// type constants // type constants
public static final int LUA_TINT = (-2); public static final int LUA_TINT = (-2);
public static final int LUA_TNONE = (-1); public static final int LUA_TNONE = (-1);
public static final int LUA_TNIL = 0; public static final int LUA_TNIL = 0;
public static final int LUA_TBOOLEAN = 1; public static final int LUA_TBOOLEAN = 1;
public static final int LUA_TLIGHTUSERDATA = 2; public static final int LUA_TLIGHTUSERDATA = 2;
public static final int LUA_TNUMBER = 3; public static final int LUA_TNUMBER = 3;
public static final int LUA_TSTRING = 4; public static final int LUA_TSTRING = 4;
public static final int LUA_TTABLE = 5; public static final int LUA_TTABLE = 5;
public static final int LUA_TFUNCTION = 6; public static final int LUA_TFUNCTION = 6;
public static final int LUA_TUSERDATA = 7; public static final int LUA_TUSERDATA = 7;
public static final int LUA_TTHREAD = 8; public static final int LUA_TTHREAD = 8;
public static final int LUA_TVALUE = 9; public static final int LUA_TVALUE = 9;
/** The character encoding to use for file encoding. Null means the default encoding */ /**
* The character encoding to use for file encoding. Null means the default
* encoding
*/
public static String encoding = null; public static String encoding = null;
/** Signature byte indicating the file is a compiled binary chunk */ /** Signature byte indicating the file is a compiled binary chunk */
public static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' }; public static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' };
/** Data to catch conversion errors */ /** Data to catch conversion errors */
public static final byte[] LUAC_TAIL = { (byte) 0x19, (byte) 0x93, '\r', '\n', (byte) 0x1a, '\n', }; public static final byte[] LUAC_TAIL = { (byte) 0x19, (byte) 0x93, '\r', '\n', (byte) 0x1a, '\n', };
/** Name for compiled chunks */ /** Name for compiled chunks */
public static final String SOURCE_BINARY_STRING = "binary string"; public static final String SOURCE_BINARY_STRING = "binary string";
/** for header of binary files -- this is Lua 5.2 */ /** for header of binary files -- this is Lua 5.2 */
public static final int LUAC_VERSION = 0x52; public static final int LUAC_VERSION = 0x52;
/** for header of binary files -- this is the official format */ /** for header of binary files -- this is the official format */
public static final int LUAC_FORMAT = 0; public static final int LUAC_FORMAT = 0;
/** size of header of binary files */ /** size of header of binary files */
public static final int LUAC_HEADERSIZE = 12; public static final int LUAC_HEADERSIZE = 12;
// values read from the header // values read from the header
private int luacVersion; private int luacVersion;
@@ -144,7 +169,7 @@ public class LoadState {
private int luacSizeofSizeT; private int luacSizeofSizeT;
private int luacSizeofInstruction; private int luacSizeofInstruction;
private int luacSizeofLuaNumber; private int luacSizeofLuaNumber;
private int luacNumberFormat; private int luacNumberFormat;
/** input stream from which we are loading */ /** input stream from which we are loading */
public final DataInputStream is; public final DataInputStream is;
@@ -152,127 +177,141 @@ public class LoadState {
/** Name of what is being loaded? */ /** Name of what is being loaded? */
String name; String name;
private static final LuaValue[] NOVALUES = {}; private static final LuaValue[] NOVALUES = {};
private static final Prototype[] NOPROTOS = {}; private static final Prototype[] NOPROTOS = {};
private static final LocVars[] NOLOCVARS = {}; private static final LocVars[] NOLOCVARS = {};
private static final Upvaldesc[] NOUPVALDESCS = {}; private static final Upvaldesc[] NOUPVALDESCS = {};
private static final int[] NOINTS = {}; private static final int[] NOINTS = {};
/** Read buffer */ /** Read buffer */
private byte[] buf = new byte[512]; private byte[] buf = new byte[512];
/** Install this class as the standard Globals.Undumper for the supplied Globals */ /**
* Install this class as the standard Globals.Undumper for the supplied
* Globals
*/
public static void install(Globals globals) { public static void install(Globals globals) {
globals.undumper = instance; globals.undumper = instance;
} }
/** Load a 4-byte int value from the input stream /**
* Load a 4-byte int value from the input stream
*
* @return the int value laoded. * @return the int value laoded.
**/ **/
int loadInt() throws IOException { int loadInt() throws IOException {
is.readFully(buf,0,4); is.readFully(buf, 0, 4);
return luacLittleEndian? return luacLittleEndian? (buf[3]<<24) | ((0xff & buf[2])<<16) | ((0xff & buf[1])<<8) | (0xff & buf[0])
(buf[3] << 24) | ((0xff & buf[2]) << 16) | ((0xff & buf[1]) << 8) | (0xff & buf[0]): : (buf[0]<<24) | ((0xff & buf[1])<<16) | ((0xff & buf[2])<<8) | (0xff & buf[3]);
(buf[0] << 24) | ((0xff & buf[1]) << 16) | ((0xff & buf[2]) << 8) | (0xff & buf[3]);
} }
/** Load an array of int values from the input stream /**
* Load an array of int values from the input stream
*
* @return the array of int values laoded. * @return the array of int values laoded.
**/ **/
int[] loadIntArray() throws IOException { int[] loadIntArray() throws IOException {
int n = loadInt(); int n = loadInt();
if ( n == 0 ) if (n == 0)
return NOINTS; return NOINTS;
// read all data at once // read all data at once
int m = n << 2; int m = n<<2;
if ( buf.length < m ) if (buf.length < m)
buf = new byte[m]; buf = new byte[m];
is.readFully(buf,0,m); is.readFully(buf, 0, m);
int[] array = new int[n]; int[] array = new int[n];
for ( int i=0, j=0; i<n; ++i, j+=4 ) for (int i = 0, j = 0; i < n; ++i, j += 4)
array[i] = luacLittleEndian? array[i] = luacLittleEndian
(buf[j+3] << 24) | ((0xff & buf[j+2]) << 16) | ((0xff & buf[j+1]) << 8) | (0xff & buf[j+0]): ? (buf[j+3]<<24) | ((0xff & buf[j+2])<<16) | ((0xff & buf[j+1])<<8) | (0xff & buf[j+0])
(buf[j+0] << 24) | ((0xff & buf[j+1]) << 16) | ((0xff & buf[j+2]) << 8) | (0xff & buf[j+3]); : (buf[j+0]<<24) | ((0xff & buf[j+1])<<16) | ((0xff & buf[j+2])<<8) | (0xff & buf[j+3]);
return array; return array;
} }
/** Load a long value from the input stream /**
* Load a long value from the input stream
*
* @return the long value laoded. * @return the long value laoded.
**/ **/
long loadInt64() throws IOException { long loadInt64() throws IOException {
int a,b; int a, b;
if ( this.luacLittleEndian ) { if (this.luacLittleEndian) {
a = loadInt(); a = loadInt();
b = loadInt(); b = loadInt();
} else { } else {
b = loadInt(); b = loadInt();
a = loadInt(); a = loadInt();
} }
return (((long)b)<<32) | (((long)a)&0xffffffffL); return (((long) b)<<32) | (((long) a) & 0xffffffffL);
} }
/** Load a lua strin gvalue from the input stream /**
* Load a lua strin gvalue from the input stream
*
* @return the {@link LuaString} value laoded. * @return the {@link LuaString} value laoded.
**/ **/
LuaString loadString() throws IOException { LuaString loadString() throws IOException {
int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt(); int size = this.luacSizeofSizeT == 8? (int) loadInt64(): loadInt();
if ( size == 0 ) if (size == 0)
return null; return null;
byte[] bytes = new byte[size]; byte[] bytes = new byte[size];
is.readFully( bytes, 0, size ); is.readFully(bytes, 0, size);
return LuaString.valueUsing( bytes, 0, bytes.length - 1 ); return LuaString.valueUsing(bytes, 0, bytes.length-1);
} }
/** /**
* Convert bits in a long value to a {@link LuaValue}. * Convert bits in a long value to a {@link LuaValue}.
*
* @param bits long value containing the bits * @param bits long value containing the bits
* @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds to the bits provided. * @return {@link LuaInteger} or {@link LuaDouble} whose value corresponds
* to the bits provided.
*/ */
public static LuaValue longBitsToLuaNumber( long bits ) { public static LuaValue longBitsToLuaNumber(long bits) {
if ( ( bits & ( ( 1L << 63 ) - 1 ) ) == 0L ) { if ((bits & ((1L<<63)-1)) == 0L) {
return LuaValue.ZERO; return LuaValue.ZERO;
} }
int e = (int)((bits >> 52) & 0x7ffL) - 1023; int e = (int) ((bits>>52) & 0x7ffL)-1023;
if ( e >= 0 && e < 31 ) { if (e >= 0 && e < 31) {
long f = bits & 0xFFFFFFFFFFFFFL; long f = bits & 0xFFFFFFFFFFFFFL;
int shift = 52 - e; int shift = 52-e;
long intPrecMask = ( 1L << shift ) - 1; long intPrecMask = (1L<<shift)-1;
if ( ( f & intPrecMask ) == 0 ) { if ((f & intPrecMask) == 0) {
int intValue = (int)( f >> shift ) | ( 1 << e ); int intValue = (int) (f>>shift) | (1<<e);
return LuaInteger.valueOf( ( ( bits >> 63 ) != 0 ) ? -intValue : intValue ); return LuaInteger.valueOf(((bits>>63) != 0)? -intValue: intValue);
} }
} }
return LuaValue.valueOf( Double.longBitsToDouble(bits) ); return LuaValue.valueOf(Double.longBitsToDouble(bits));
} }
/** /**
* Load a number from a binary chunk * Load a number from a binary chunk
*
* @return the {@link LuaValue} loaded * @return the {@link LuaValue} loaded
* @throws IOException if an i/o exception occurs * @throws IOException if an i/o exception occurs
*/ */
LuaValue loadNumber() throws IOException { LuaValue loadNumber() throws IOException {
if ( luacNumberFormat == NUMBER_FORMAT_INTS_ONLY ) { if (luacNumberFormat == NUMBER_FORMAT_INTS_ONLY) {
return LuaInteger.valueOf( loadInt() ); return LuaInteger.valueOf(loadInt());
} else { } else {
return longBitsToLuaNumber( loadInt64() ); return longBitsToLuaNumber(loadInt64());
} }
} }
/** /**
* Load a list of constants from a binary chunk * Load a list of constants from a binary chunk
*
* @param f the function prototype * @param f the function prototype
* @throws IOException if an i/o exception occurs * @throws IOException if an i/o exception occurs
*/ */
void loadConstants(Prototype f) throws IOException { void loadConstants(Prototype f) throws IOException {
int n = loadInt(); int n = loadInt();
LuaValue[] values = n>0? new LuaValue[n]: NOVALUES; LuaValue[] values = n > 0? new LuaValue[n]: NOVALUES;
for ( int i=0; i<n; i++ ) { for (int i = 0; i < n; i++) {
switch ( is.readByte() ) { switch (is.readByte()) {
case LUA_TNIL: case LUA_TNIL:
values[i] = LuaValue.NIL; values[i] = LuaValue.NIL;
break; break;
@@ -280,7 +319,7 @@ public class LoadState {
values[i] = (0 != is.readUnsignedByte()? LuaValue.TRUE: LuaValue.FALSE); values[i] = (0 != is.readUnsignedByte()? LuaValue.TRUE: LuaValue.FALSE);
break; break;
case LUA_TINT: case LUA_TINT:
values[i] = LuaInteger.valueOf( loadInt() ); values[i] = LuaInteger.valueOf(loadInt());
break; break;
case LUA_TNUMBER: case LUA_TNUMBER:
values[i] = loadNumber(); values[i] = loadNumber();
@@ -293,19 +332,18 @@ public class LoadState {
} }
} }
f.k = values; f.k = values;
n = loadInt(); n = loadInt();
Prototype[] protos = n>0? new Prototype[n]: NOPROTOS; Prototype[] protos = n > 0? new Prototype[n]: NOPROTOS;
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
protos[i] = loadFunction(f.source); protos[i] = loadFunction(f.source);
f.p = protos; f.p = protos;
} }
void loadUpvalues(Prototype f) throws IOException { void loadUpvalues(Prototype f) throws IOException {
int n = loadInt(); int n = loadInt();
f.upvalues = n>0? new Upvaldesc[n]: NOUPVALDESCS; f.upvalues = n > 0? new Upvaldesc[n]: NOUPVALDESCS;
for (int i=0; i<n; i++) { for (int i = 0; i < n; i++) {
boolean instack = is.readByte() != 0; boolean instack = is.readByte() != 0;
int idx = ((int) is.readByte()) & 0xff; int idx = ((int) is.readByte()) & 0xff;
f.upvalues[i] = new Upvaldesc(null, instack, idx); f.upvalues[i] = new Upvaldesc(null, instack, idx);
@@ -314,28 +352,30 @@ public class LoadState {
/** /**
* Load the debug info for a function prototype * Load the debug info for a function prototype
*
* @param f the function Prototype * @param f the function Prototype
* @throws IOException if there is an i/o exception * @throws IOException if there is an i/o exception
*/ */
void loadDebug( Prototype f ) throws IOException { void loadDebug(Prototype f) throws IOException {
f.source = loadString(); f.source = loadString();
f.lineinfo = loadIntArray(); f.lineinfo = loadIntArray();
int n = loadInt(); int n = loadInt();
f.locvars = n>0? new LocVars[n]: NOLOCVARS; f.locvars = n > 0? new LocVars[n]: NOLOCVARS;
for ( int i=0; i<n; i++ ) { for (int i = 0; i < n; i++) {
LuaString varname = loadString(); LuaString varname = loadString();
int startpc = loadInt(); int startpc = loadInt();
int endpc = loadInt(); int endpc = loadInt();
f.locvars[i] = new LocVars(varname, startpc, endpc); f.locvars[i] = new LocVars(varname, startpc, endpc);
} }
n = loadInt(); n = loadInt();
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
f.upvalues[i].name = loadString(); f.upvalues[i].name = loadString();
} }
/** /**
* Load a function prototype from the input stream * Load a function prototype from the input stream
*
* @param p name of the source * @param p name of the source
* @return {@link Prototype} instance that was loaded * @return {@link Prototype} instance that was loaded
* @throws IOException * @throws IOException
@@ -355,17 +395,18 @@ public class LoadState {
loadConstants(f); loadConstants(f);
loadUpvalues(f); loadUpvalues(f);
loadDebug(f); loadDebug(f);
// TODO: add check here, for debugging purposes, I believe // TODO: add check here, for debugging purposes, I believe
// see ldebug.c // see ldebug.c
// IF (!luaG_checkcode(f), "bad code"); // IF (!luaG_checkcode(f), "bad code");
// this.L.pop(); // this.L.pop();
return f; return f;
} }
/** /**
* Load the lua chunk header values. * Load the lua chunk header values.
*
* @throws IOException if an i/o exception occurs. * @throws IOException if an i/o exception occurs.
*/ */
public void loadHeader() throws IOException { public void loadHeader() throws IOException {
@@ -377,33 +418,35 @@ public class LoadState {
luacSizeofInstruction = is.readByte(); luacSizeofInstruction = is.readByte();
luacSizeofLuaNumber = is.readByte(); luacSizeofLuaNumber = is.readByte();
luacNumberFormat = is.readByte(); luacNumberFormat = is.readByte();
for (int i=0; i < LUAC_TAIL.length; ++i) for (int i = 0; i < LUAC_TAIL.length; ++i)
if (is.readByte() != LUAC_TAIL[i]) if (is.readByte() != LUAC_TAIL[i])
throw new LuaError("Unexpeted byte in luac tail of header, index="+i); throw new LuaError("Unexpeted byte in luac tail of header, index=" + i);
} }
/** /**
* Load input stream as a lua binary chunk if the first 4 bytes are the lua binary signature. * Load input stream as a lua binary chunk if the first 4 bytes are the lua
* @param stream InputStream to read, after having read the first byte already * binary signature.
*
* @param stream InputStream to read, after having read the first byte
* already
* @param chunkname Name to apply to the loaded chunk * @param chunkname Name to apply to the loaded chunk
* @return {@link Prototype} that was loaded, or null if the first 4 bytes were not the lua signature. * @return {@link Prototype} that was loaded, or null if the first 4 bytes
* were not the lua signature.
* @throws IOException if an IOException occurs * @throws IOException if an IOException occurs
*/ */
public static Prototype undump(InputStream stream, String chunkname) throws IOException { public static Prototype undump(InputStream stream, String chunkname) throws IOException {
// check rest of signature // check rest of signature
if ( stream.read() != LUA_SIGNATURE[0] if (stream.read() != LUA_SIGNATURE[0] || stream.read() != LUA_SIGNATURE[1] || stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[1] || stream.read() != LUA_SIGNATURE[3])
|| stream.read() != LUA_SIGNATURE[2]
|| stream.read() != LUA_SIGNATURE[3] )
return null; return null;
// load file as a compiled chunk // load file as a compiled chunk
String sname = getSourceName(chunkname); String sname = getSourceName(chunkname);
LoadState s = new LoadState( stream, sname ); LoadState s = new LoadState(stream, sname);
s.loadHeader(); s.loadHeader();
// check format // check format
switch ( s.luacNumberFormat ) { switch (s.luacNumberFormat) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES: case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY: case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32: case NUMBER_FORMAT_NUM_PATCH_INT32:
@@ -411,33 +454,33 @@ public class LoadState {
default: default:
throw new LuaError("unsupported int size"); throw new LuaError("unsupported int size");
} }
return s.loadFunction( LuaString.valueOf(sname) ); return s.loadFunction(LuaString.valueOf(sname));
} }
/** /**
* Construct a source name from a supplied chunk name * Construct a source name from a supplied chunk name
*
* @param name String name that appears in the chunk * @param name String name that appears in the chunk
* @return source file name * @return source file name
*/ */
public static String getSourceName(String name) { public static String getSourceName(String name) {
String sname = name; String sname = name;
if ( name.startsWith("@") || name.startsWith("=") ) if (name.startsWith("@") || name.startsWith("="))
sname = name.substring(1); sname = name.substring(1);
else if ( name.startsWith("\033") ) else if (name.startsWith("\033"))
sname = SOURCE_BINARY_STRING; sname = SOURCE_BINARY_STRING;
return sname; return sname;
} }
/** Private constructor for create a load state */ /** Private constructor for create a load state */
private LoadState( InputStream stream, String name ) { private LoadState(InputStream stream, String name) {
this.name = name; this.name = name;
this.is = new DataInputStream( stream ); this.is = new DataInputStream(stream);
} }
private static final class GlobalsUndumper implements Globals.Undumper { private static final class GlobalsUndumper implements Globals.Undumper {
public Prototype undump(InputStream stream, String chunkname) public Prototype undump(InputStream stream, String chunkname) throws IOException {
throws IOException { return LoadState.undump(stream, chunkname);
return LoadState.undump(stream, chunkname);
} }
} }
} }

View File

@@ -22,31 +22,33 @@
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Data class to hold debug information relating to local variables for a {@link Prototype} * Data class to hold debug information relating to local variables for a
* {@link Prototype}
*/ */
public class LocVars { public class LocVars {
/** The local variable name */ /** The local variable name */
public LuaString varname; public LuaString varname;
/** The instruction offset when the variable comes into scope */ /** The instruction offset when the variable comes into scope */
public int startpc; public int startpc;
/** The instruction offset when the variable goes out of scope */ /** The instruction offset when the variable goes out of scope */
public int endpc; public int endpc;
/** /**
* Construct a LocVars instance. * Construct a LocVars instance.
*
* @param varname The local variable name * @param varname The local variable name
* @param startpc The instruction offset when the variable comes into scope * @param startpc The instruction offset when the variable comes into scope
* @param endpc The instruction offset when the variable goes out of scope * @param endpc The instruction offset when the variable goes out of scope
*/ */
public LocVars(LuaString varname, int startpc, int endpc) { public LocVars(LuaString varname, int startpc, int endpc) {
this.varname = varname; this.varname = varname;
this.startpc = startpc; this.startpc = startpc;
this.endpc = endpc; this.endpc = endpc;
} }
public String tojstring() { public String tojstring() {
return varname+" "+startpc+"-"+endpc; return varname + " " + startpc + "-" + endpc;
} }
} }

View File

@@ -21,17 +21,16 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Constants for lua limits and opcodes. * Constants for lua limits and opcodes.
* <p> * <p>
* This is a direct translation of C lua distribution header file constants * This is a direct translation of C lua distribution header file constants for
* for bytecode creation and processing. * bytecode creation and processing.
*/ */
public class Lua { public class Lua {
/** version is supplied by ant build task */ /** version is supplied by ant build task */
public static final String _VERSION = "Luaj 0.0"; public static final String _VERSION = "Luaj 0.0";
/** use return values from previous op */ /** use return values from previous op */
public static final int LUA_MULTRET = -1; public static final int LUA_MULTRET = -1;
@@ -46,7 +45,7 @@ public class Lua {
`C' : 9 bits `C' : 9 bits
`Bx' : 18 bits (`B' and `C' together) `Bx' : 18 bits (`B' and `C' together)
`sBx' : signed Bx `sBx' : signed Bx
A signed argument is represented in excess K; that is, the number A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is for that argument (so that -max is represented by 0, and +max is
@@ -54,39 +53,37 @@ public class Lua {
unsigned argument. unsigned argument.
===========================================================================*/ ===========================================================================*/
/* basic instruction format */ /* basic instruction format */
public static final int iABC = 0; public static final int iABC = 0;
public static final int iABx = 1; public static final int iABx = 1;
public static final int iAsBx = 2; public static final int iAsBx = 2;
public static final int iAx = 3; public static final int iAx = 3;
/* /*
** size and position of opcode arguments. ** size and position of opcode arguments.
*/ */
public static final int SIZE_C = 9; public static final int SIZE_C = 9;
public static final int SIZE_B = 9; public static final int SIZE_B = 9;
public static final int SIZE_Bx = (SIZE_C + SIZE_B); public static final int SIZE_Bx = (SIZE_C+SIZE_B);
public static final int SIZE_A = 8; public static final int SIZE_A = 8;
public static final int SIZE_Ax = (SIZE_C + SIZE_B + SIZE_A); public static final int SIZE_Ax = (SIZE_C+SIZE_B+SIZE_A);
public static final int SIZE_OP = 6; public static final int SIZE_OP = 6;
public static final int POS_OP = 0; public static final int POS_OP = 0;
public static final int POS_A = (POS_OP + SIZE_OP); public static final int POS_A = (POS_OP+SIZE_OP);
public static final int POS_C = (POS_A + SIZE_A); public static final int POS_C = (POS_A+SIZE_A);
public static final int POS_B = (POS_C + SIZE_C); public static final int POS_B = (POS_C+SIZE_C);
public static final int POS_Bx = POS_C; public static final int POS_Bx = POS_C;
public static final int POS_Ax = POS_A; public static final int POS_Ax = POS_A;
public static final int MAX_OP = ((1<<SIZE_OP)-1); public static final int MAX_OP = ((1<<SIZE_OP)-1);
public static final int MAXARG_A = ((1<<SIZE_A)-1); public static final int MAXARG_A = ((1<<SIZE_A)-1);
public static final int MAXARG_B = ((1<<SIZE_B)-1); public static final int MAXARG_B = ((1<<SIZE_B)-1);
public static final int MAXARG_C = ((1<<SIZE_C)-1); public static final int MAXARG_C = ((1<<SIZE_C)-1);
public static final int MAXARG_Bx = ((1<<SIZE_Bx)-1); public static final int MAXARG_Bx = ((1<<SIZE_Bx)-1);
public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */ public static final int MAXARG_sBx = (MAXARG_Bx>>1); /* `sBx' is signed */
public static final int MAXARG_Ax = ((1<<SIZE_Ax)-1); public static final int MAXARG_Ax = ((1<<SIZE_Ax)-1);
public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP; public static final int MASK_OP = ((1<<SIZE_OP)-1)<<POS_OP;
public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A; public static final int MASK_A = ((1<<SIZE_A)-1)<<POS_A;
@@ -105,40 +102,39 @@ public class Lua {
** the following macros help to manipulate instructions ** the following macros help to manipulate instructions
*/ */
public static int GET_OPCODE(int i) { public static int GET_OPCODE(int i) {
return (i >> POS_OP) & MAX_OP; return (i>>POS_OP) & MAX_OP;
} }
public static int GETARG_A(int i) { public static int GETARG_A(int i) {
return (i >> POS_A) & MAXARG_A; return (i>>POS_A) & MAXARG_A;
} }
public static int GETARG_Ax(int i) { public static int GETARG_Ax(int i) {
return (i >> POS_Ax) & MAXARG_Ax; return (i>>POS_Ax) & MAXARG_Ax;
} }
public static int GETARG_B(int i) { public static int GETARG_B(int i) {
return (i >> POS_B) & MAXARG_B; return (i>>POS_B) & MAXARG_B;
} }
public static int GETARG_C(int i) { public static int GETARG_C(int i) {
return (i >> POS_C) & MAXARG_C; return (i>>POS_C) & MAXARG_C;
} }
public static int GETARG_Bx(int i) { public static int GETARG_Bx(int i) {
return (i >> POS_Bx) & MAXARG_Bx; return (i>>POS_Bx) & MAXARG_Bx;
} }
public static int GETARG_sBx(int i) { public static int GETARG_sBx(int i) {
return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; return ((i>>POS_Bx) & MAXARG_Bx)-MAXARG_sBx;
} }
/* /*
** Macros to operate RK indices ** Macros to operate RK indices
*/ */
/** this bit 1 means constant (0 means register) */ /** this bit 1 means constant (0 means register) */
public static final int BITRK = (1 << (SIZE_B - 1)); public static final int BITRK = (1<<(SIZE_B-1));
/** test whether value is a constant */ /** test whether value is a constant */
public static boolean ISK(int x) { public static boolean ISK(int x) {
@@ -147,22 +143,20 @@ public class Lua {
/** gets the index of the constant */ /** gets the index of the constant */
public static int INDEXK(int r) { public static int INDEXK(int r) {
return ((int)(r) & ~BITRK); return ((int) (r) & ~BITRK);
} }
public static final int MAXINDEXRK = (BITRK - 1); public static final int MAXINDEXRK = (BITRK-1);
/** code a constant index as a RK value */ /** code a constant index as a RK value */
public static int RKASK(int x) { public static int RKASK(int x) {
return ((x) | BITRK); return ((x) | BITRK);
} }
/** /**
** invalid register that fits in 8 bits ** invalid register that fits in 8 bits
*/ */
public static final int NO_REG = MAXARG_A; public static final int NO_REG = MAXARG_A;
/* /*
** R(x) - register ** R(x) - register
@@ -170,7 +164,6 @@ public class Lua {
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x) ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/ */
/* /*
** grep "ORDER OP" if you change these enums ** grep "ORDER OP" if you change these enums
*/ */
@@ -178,18 +171,18 @@ public class Lua {
/*---------------------------------------------------------------------- /*----------------------------------------------------------------------
name args description name args description
------------------------------------------------------------------------*/ ------------------------------------------------------------------------*/
public static final int OP_MOVE = 0;/* A B R(A) := R(B) */ public static final int OP_MOVE = 0; /* A B R(A) := R(B) */
public static final int OP_LOADK = 1;/* A Bx R(A) := Kst(Bx) */ public static final int OP_LOADK = 1; /* A Bx R(A) := Kst(Bx) */
public static final int OP_LOADKX = 2;/* A R(A) := Kst(extra arg) */ public static final int OP_LOADKX = 2; /* A R(A) := Kst(extra arg) */
public static final int OP_LOADBOOL = 3;/* A B C R(A) := (Bool)B; if (C) pc++ */ public static final int OP_LOADBOOL = 3; /* A B C R(A) := (Bool)B; if (C) pc++ */
public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(A+B) := nil */ public static final int OP_LOADNIL = 4; /* A B R(A) := ... := R(A+B) := nil */
public static final int OP_GETUPVAL = 5; /* A B R(A) := UpValue[B] */ public static final int OP_GETUPVAL = 5; /* A B R(A) := UpValue[B] */
public static final int OP_GETTABUP = 6; /* A B C R(A) := UpValue[B][RK(C)] */ public static final int OP_GETTABUP = 6; /* A B C R(A) := UpValue[B][RK(C)] */
public static final int OP_GETTABLE = 7; /* A B C R(A) := R(B)[RK(C)] */ public static final int OP_GETTABLE = 7; /* A B C R(A) := R(B)[RK(C)] */
public static final int OP_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */ public static final int OP_SETTABUP = 8; /* A B C UpValue[A][RK(B)] := RK(C) */
public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */ public static final int OP_SETUPVAL = 9; /* A B UpValue[B] := R(A) */
public static final int OP_SETTABLE = 10; /* A B C R(A)[RK(B)] := RK(C) */ public static final int OP_SETTABLE = 10; /* A B C R(A)[RK(B)] := RK(C) */
public static final int OP_NEWTABLE = 11; /* A B C R(A) := {} (size = B,C) */ public static final int OP_NEWTABLE = 11; /* A B C R(A) := {} (size = B,C) */
@@ -209,24 +202,24 @@ public class Lua {
public static final int OP_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */ public static final int OP_CONCAT = 22; /* A B C R(A) := R(B).. ... ..R(C) */
public static final int OP_JMP = 23; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ public static final int OP_JMP = 23; /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ public static final int OP_EQ = 24; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ public static final int OP_LT = 25; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ public static final int OP_LE = 26; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */ public static final int OP_TEST = 27; /* A C if not (R(A) <=> C) then pc++ */
public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ public static final int OP_TESTSET = 28; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ public static final int OP_CALL = 29; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_TAILCALL = 30; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ public static final int OP_TAILCALL = 30; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */ public static final int OP_RETURN = 31; /* A B return R(A), ... ,R(A+B-2) (see note) */
public static final int OP_FORLOOP = 32; /* A sBx R(A)+=R(A+2); public static final int OP_FORLOOP = 32; /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/ if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
public static final int OP_FORPREP = 33; /* A sBx R(A)-=R(A+2); pc+=sBx */ public static final int OP_FORPREP = 33; /* A sBx R(A)-=R(A+2); pc+=sBx */
public static final int OP_TFORCALL = 34; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ public static final int OP_TFORCALL = 34; /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
public static final int OP_TFORLOOP = 35; /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */ public static final int OP_TFORLOOP = 35; /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ public static final int OP_SETLIST = 36; /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ public static final int OP_CLOSURE = 37; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
@@ -234,7 +227,7 @@ public class Lua {
public static final int OP_EXTRAARG = 39; /* Ax extra (larger) argument for previous opcode */ public static final int OP_EXTRAARG = 39; /* Ax extra (larger) argument for previous opcode */
public static final int NUM_OPCODES = OP_EXTRAARG + 1; public static final int NUM_OPCODES = OP_EXTRAARG+1;
/* pseudo-opcodes used in parsing only. */ /* pseudo-opcodes used in parsing only. */
public static final int OP_GT = 63; // > public static final int OP_GT = 63; // >
@@ -242,28 +235,27 @@ public class Lua {
public static final int OP_NEQ = 61; // ~= public static final int OP_NEQ = 61; // ~=
public static final int OP_AND = 60; // and public static final int OP_AND = 60; // and
public static final int OP_OR = 59; // or public static final int OP_OR = 59; // or
/*=========================================================================== /*===========================================================================
Notes: Notes:
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0). set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to `top' (*) In OP_RETURN, if (B == 0) then return up to `top'
(*) In OP_SETLIST, if (B == 0) then B = `top'; (*) In OP_SETLIST, if (B == 0) then B = `top';
if (C == 0) then next `instruction' is real C if (C == 0) then next `instruction' is real C
(*) For comparisons, A specifies what condition the test should accept (*) For comparisons, A specifies what condition the test should accept
(true or false). (true or false).
(*) All `skips' (pc++) assume that next instruction is a jump (*) All `skips' (pc++) assume that next instruction is a jump
===========================================================================*/ ===========================================================================*/
/* /*
** masks for instruction properties. The format is: ** masks for instruction properties. The format is:
** bits 0-1: op mode ** bits 0-1: op mode
@@ -273,69 +265,73 @@ public class Lua {
** bit 7: operator is a test ** bit 7: operator is a test
*/ */
public static final int OpArgN = 0; /* argument is not used */ public static final int OpArgN = 0; /* argument is not used */
public static final int OpArgU = 1; /* argument is used */ public static final int OpArgU = 1; /* argument is used */
public static final int OpArgR = 2; /* argument is a register or a jump offset */ public static final int OpArgR = 2; /* argument is a register or a jump offset */
public static final int OpArgK = 3; /* argument is a constant or register/constant */ public static final int OpArgK = 3; /* argument is a constant or register/constant */
public static final int[] luaP_opmodes = { public static final int[] luaP_opmodes = {
/* T A B C mode opcode */ /* T A B C mode opcode */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */
(0<<7) | (1<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABx), /* OP_LOADKX */ (0<<7) | (1<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABx), /* OP_LOADKX */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABUP */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABUP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABUP */ (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABUP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */ (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */
(0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */ (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */
(0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */ (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */
(0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */ (0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */ (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */ (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */
(1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */ (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */
(1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */ (1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */ (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */ (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */
(0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */ (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */
(0<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORCALL */ (0<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORCALL */
(1<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_TFORLOOP */ (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_TFORLOOP */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */ (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */
(0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */ (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */
(0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iAx), /* OP_EXTRAARG */ (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iAx), /* OP_EXTRAARG */
}; };
public static int getOpMode(int m) { public static int getOpMode(int m) {
return luaP_opmodes[m] & 3; return luaP_opmodes[m] & 3;
} }
public static int getBMode(int m) { public static int getBMode(int m) {
return (luaP_opmodes[m] >> 4) & 3; return (luaP_opmodes[m]>>4) & 3;
} }
public static int getCMode(int m) { public static int getCMode(int m) {
return (luaP_opmodes[m] >> 2) & 3; return (luaP_opmodes[m]>>2) & 3;
} }
public static boolean testAMode(int m) { public static boolean testAMode(int m) {
return 0 != (luaP_opmodes[m] & (1 << 6)); return 0 != (luaP_opmodes[m] & (1<<6));
} }
public static boolean testTMode(int m) { public static boolean testTMode(int m) {
return 0 != (luaP_opmodes[m] & (1 << 7)); return 0 != (luaP_opmodes[m] & (1<<7));
} }
/* number of list items to accumulate before a SETLIST instruction */ /* number of list items to accumulate before a SETLIST instruction */
@@ -343,19 +339,19 @@ public class Lua {
private static final int MAXSRC = 80; private static final int MAXSRC = 80;
public static String chunkid( String source ) { public static String chunkid(String source) {
if ( source.startsWith("=") ) if (source.startsWith("="))
return source.substring(1); return source.substring(1);
String end = ""; String end = "";
if ( source.startsWith("@") ) { if (source.startsWith("@")) {
source = source.substring(1); source = source.substring(1);
} else { } else {
source = "[string \""+source; source = "[string \"" + source;
end = "\"]"; end = "\"]";
} }
int n = source.length() + end.length(); int n = source.length()+end.length();
if ( n > MAXSRC ) if (n > MAXSRC)
source = source.substring(0,MAXSRC-end.length()-3) + "..."; source = source.substring(0, MAXSRC-end.length()-3) + "...";
return source + end; return source+end;
} }
} }

View File

@@ -22,18 +22,18 @@
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Extension of {@link LuaValue} which can hold a Java boolean as its value. * Extension of {@link LuaValue} which can hold a Java boolean as its value.
* <p> * <p>
* These instance are not instantiated directly by clients. * These instance are not instantiated directly by clients. Instead, there are
* Instead, there are exactly twon instances of this class, * exactly twon instances of this class, {@link LuaValue#TRUE} and
* {@link LuaValue#TRUE} and {@link LuaValue#FALSE} * {@link LuaValue#FALSE} representing the lua values {@code true} and
* representing the lua values {@code true} and {@code false}. * {@code false}. The function {@link LuaValue#valueOf(boolean)} will always
* The function {@link LuaValue#valueOf(boolean)} will always * return one of these two values.
* return one of these two values.
* <p> * <p>
* Any {@link LuaValue} can be converted to its equivalent * Any {@link LuaValue} can be converted to its equivalent boolean
* boolean representation using {@link LuaValue#toboolean()} * representation using {@link LuaValue#toboolean()}
* <p> * <p>
*
* @see LuaValue * @see LuaValue
* @see LuaValue#valueOf(boolean) * @see LuaValue#valueOf(boolean)
* @see LuaValue#TRUE * @see LuaValue#TRUE
@@ -43,10 +43,10 @@ public final class LuaBoolean extends LuaValue {
/** The singleton instance representing lua {@code true} */ /** The singleton instance representing lua {@code true} */
static final LuaBoolean _TRUE = new LuaBoolean(true); static final LuaBoolean _TRUE = new LuaBoolean(true);
/** The singleton instance representing lua {@code false} */ /** The singleton instance representing lua {@code false} */
static final LuaBoolean _FALSE = new LuaBoolean(false); static final LuaBoolean _FALSE = new LuaBoolean(false);
/** Shared static metatable for boolean values represented in lua. */ /** Shared static metatable for boolean values represented in lua. */
public static LuaValue s_metatable; public static LuaValue s_metatable;
@@ -70,11 +70,12 @@ public final class LuaBoolean extends LuaValue {
} }
public LuaValue not() { public LuaValue not() {
return v ? FALSE : LuaValue.TRUE; return v? FALSE: LuaValue.TRUE;
} }
/** /**
* Return the boolean value for this boolean * Return the boolean value for this boolean
*
* @return value as a Java boolean * @return value as a Java boolean
*/ */
public boolean booleanValue() { public boolean booleanValue() {
@@ -86,18 +87,18 @@ public final class LuaBoolean extends LuaValue {
} }
public String tojstring() { public String tojstring() {
return v ? "true" : "false"; return v? "true": "false";
} }
public boolean optboolean(boolean defval) { public boolean optboolean(boolean defval) {
return this.v; return this.v;
} }
public boolean checkboolean() { public boolean checkboolean() {
return v; return v;
} }
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
} }

View File

@@ -26,44 +26,56 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
/** /**
* Extension of {@link LuaFunction} which executes lua bytecode. * Extension of {@link LuaFunction} which executes lua bytecode.
* <p> * <p>
* A {@link LuaClosure} is a combination of a {@link Prototype} * A {@link LuaClosure} is a combination of a {@link Prototype} and a
* and a {@link LuaValue} to use as an environment for execution. * {@link LuaValue} to use as an environment for execution. Normally the
* Normally the {@link LuaValue} is a {@link Globals} in which case the environment * {@link LuaValue} is a {@link Globals} in which case the environment will
* will contain standard lua libraries. * contain standard lua libraries.
* *
* <p> * <p>
* There are three main ways {@link LuaClosure} instances are created: * There are three main ways {@link LuaClosure} instances are created:
* <ul> * <ul>
* <li>Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}</li> * <li>Construct an instance using {@link #LuaClosure(Prototype, LuaValue)}</li>
* <li>Construct it indirectly by loading a chunk via {@link Globals#load(java.io.Reader, String)} * <li>Construct it indirectly by loading a chunk via
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode processing * {@link Globals#load(java.io.Reader, String)}
* <li>Execute the lua bytecode {@link Lua#OP_CLOSURE} as part of bytecode
* processing
* </ul> * </ul>
* <p> * <p>
* To construct it directly, the {@link Prototype} is typically created via a compiler such as * To construct it directly, the {@link Prototype} is typically created via a
* {@link org.luaj.vm2.compiler.LuaC}: * compiler such as {@link org.luaj.vm2.compiler.LuaC}:
* <pre> {@code *
* String script = "print( 'hello, world' )"; * <pre>
* InputStream is = new ByteArrayInputStream(script.getBytes()); * {
* Prototype p = LuaC.instance.compile(is, "script"); * &#64;code
* LuaValue globals = JsePlatform.standardGlobals(); * String script = "print( 'hello, world' )";
* LuaClosure f = new LuaClosure(p, globals); * InputStream is = new ByteArrayInputStream(script.getBytes());
* f.call(); * Prototype p = LuaC.instance.compile(is, "script");
* }</pre> * LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }
* </pre>
* <p> * <p>
* To construct it indirectly, the {@link Globals#load(java.io.Reader, String)} method may be used: * To construct it indirectly, the {@link Globals#load(java.io.Reader, String)}
* <pre> {@code * method may be used:
* Globals globals = JsePlatform.standardGlobals(); *
* LuaFunction f = globals.load(new StringReader(script), "script"); * <pre>
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed. * {
* c.call(); * &#64;code
* }</pre> * Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script");
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed.
* c.call();
* }
* </pre>
* <p> * <p>
* In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode * In this example, the "checkclosure()" may fail if direct lua-to-java-bytecode
* compiling using LuaJC is installed, because no LuaClosure is created in that case * compiling using LuaJC is installed, because no LuaClosure is created in that
* and the value returned is a {@link LuaFunction} but not a {@link LuaClosure}. * case and the value returned is a {@link LuaFunction} but not a
* {@link LuaClosure}.
* <p> * <p>
* Since a {@link LuaClosure} is a {@link LuaFunction} which is a {@link LuaValue}, * Since a {@link LuaClosure} is a {@link LuaFunction} which is a
* all the value operations can be used directly such as: * {@link LuaValue}, all the value operations can be used directly such as:
* <ul> * <ul>
* <li>{@link LuaValue#call()}</li> * <li>{@link LuaValue#call()}</li>
* <li>{@link LuaValue#call(LuaValue)}</li> * <li>{@link LuaValue#call(LuaValue)}</li>
@@ -73,8 +85,9 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
* <li>{@link LuaValue#method(String,LuaValue)}</li> * <li>{@link LuaValue#method(String,LuaValue)}</li>
* <li>{@link LuaValue#invokemethod(String)}</li> * <li>{@link LuaValue#invokemethod(String)}</li>
* <li>{@link LuaValue#invokemethod(String,Varargs)}</li> * <li>{@link LuaValue#invokemethod(String,Varargs)}</li>
* <li> ...</li> * <li>...</li>
* </ul> * </ul>
*
* @see LuaValue * @see LuaValue
* @see LuaFunction * @see LuaFunction
* @see LuaValue#isclosure() * @see LuaValue#isclosure()
@@ -85,16 +98,19 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
*/ */
public class LuaClosure extends LuaFunction { public class LuaClosure extends LuaFunction {
private static final UpValue[] NOUPVALUES = new UpValue[0]; private static final UpValue[] NOUPVALUES = new UpValue[0];
public final Prototype p; public final Prototype p;
public UpValue[] upValues; public UpValue[] upValues;
final Globals globals; final Globals globals;
/** Create a closure around a Prototype with a specific environment. /**
* If the prototype has upvalues, the environment will be written into the first upvalue. * Create a closure around a Prototype with a specific environment. If the
* @param p the Prototype to construct this Closure for. * prototype has upvalues, the environment will be written into the first
* upvalue.
*
* @param p the Prototype to construct this Closure for.
* @param env the environment to associate with the closure. * @param env the environment to associate with the closure.
*/ */
public LuaClosure(Prototype p, LuaValue env) { public LuaClosure(Prototype p, LuaValue env) {
@@ -102,21 +118,20 @@ public class LuaClosure extends LuaFunction {
this.initupvalue1(env); this.initupvalue1(env);
globals = env instanceof Globals? (Globals) env: null; globals = env instanceof Globals? (Globals) env: null;
} }
public void initupvalue1(LuaValue env) { public void initupvalue1(LuaValue env) {
if (p.upvalues == null || p.upvalues.length == 0) if (p.upvalues == null || p.upvalues.length == 0)
this.upValues = NOUPVALUES; this.upValues = NOUPVALUES;
else { else {
this.upValues = new UpValue[p.upvalues.length]; this.upValues = new UpValue[p.upvalues.length];
this.upValues[0] = new UpValue(new LuaValue[] {env}, 0); this.upValues[0] = new UpValue(new LuaValue[] { env }, 0);
} }
} }
public boolean isclosure() { public boolean isclosure() {
return true; return true;
} }
public LuaClosure optclosure(LuaClosure defval) { public LuaClosure optclosure(LuaClosure defval) {
return this; return this;
} }
@@ -124,379 +139,435 @@ public class LuaClosure extends LuaFunction {
public LuaClosure checkclosure() { public LuaClosure checkclosure() {
return this; return this;
} }
public String tojstring() { public String tojstring() {
return "function: " + p.toString(); return "function: " + p.toString();
} }
private LuaValue[] getNewStack() { private LuaValue[] getNewStack() {
int max = p.maxstacksize; int max = p.maxstacksize;
LuaValue[] stack = new LuaValue[max]; LuaValue[] stack = new LuaValue[max];
System.arraycopy(NILS, 0, stack, 0, max); System.arraycopy(NILS, 0, stack, 0, max);
return stack; return stack;
} }
public final LuaValue call() { public final LuaValue call() {
LuaValue[] stack = getNewStack(); LuaValue[] stack = getNewStack();
return execute(stack,NONE).arg1(); return execute(stack, NONE).arg1();
} }
public final LuaValue call(LuaValue arg) { public final LuaValue call(LuaValue arg) {
LuaValue[] stack = getNewStack(); LuaValue[] stack = getNewStack();
switch ( p.numparams ) { switch (p.numparams) {
default: stack[0]=arg; return execute(stack,NONE).arg1(); default:
case 0: return execute(stack,arg).arg1(); stack[0] = arg;
return execute(stack, NONE).arg1();
case 0:
return execute(stack, arg).arg1();
} }
} }
public final LuaValue call(LuaValue arg1, LuaValue arg2) { public final LuaValue call(LuaValue arg1, LuaValue arg2) {
LuaValue[] stack = getNewStack(); LuaValue[] stack = getNewStack();
switch ( p.numparams ) { switch (p.numparams) {
default: stack[0]=arg1; stack[1]=arg2; return execute(stack,NONE).arg1(); default:
case 1: stack[0]=arg1; return execute(stack,arg2).arg1(); stack[0] = arg1;
case 0: return execute(stack,p.is_vararg!=0? varargsOf(arg1,arg2): NONE).arg1(); stack[1] = arg2;
return execute(stack, NONE).arg1();
case 1:
stack[0] = arg1;
return execute(stack, arg2).arg1();
case 0:
return execute(stack, p.is_vararg != 0? varargsOf(arg1, arg2): NONE).arg1();
} }
} }
public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
LuaValue[] stack = getNewStack(); LuaValue[] stack = getNewStack();
switch ( p.numparams ) { switch (p.numparams) {
default: stack[0]=arg1; stack[1]=arg2; stack[2]=arg3; return execute(stack,NONE).arg1(); default:
case 2: stack[0]=arg1; stack[1]=arg2; return execute(stack,arg3).arg1(); stack[0] = arg1;
case 1: stack[0]=arg1; return execute(stack,p.is_vararg!=0? varargsOf(arg2,arg3): NONE).arg1(); stack[1] = arg2;
case 0: return execute(stack,p.is_vararg!=0? varargsOf(arg1,arg2,arg3): NONE).arg1(); stack[2] = arg3;
return execute(stack, NONE).arg1();
case 2:
stack[0] = arg1;
stack[1] = arg2;
return execute(stack, arg3).arg1();
case 1:
stack[0] = arg1;
return execute(stack, p.is_vararg != 0? varargsOf(arg2, arg3): NONE).arg1();
case 0:
return execute(stack, p.is_vararg != 0? varargsOf(arg1, arg2, arg3): NONE).arg1();
} }
} }
public final Varargs invoke(Varargs varargs) { public final Varargs invoke(Varargs varargs) {
return onInvoke(varargs).eval(); return onInvoke(varargs).eval();
} }
public final Varargs onInvoke(Varargs varargs) { public final Varargs onInvoke(Varargs varargs) {
LuaValue[] stack = getNewStack(); LuaValue[] stack = getNewStack();
for ( int i=0; i<p.numparams; i++ ) for (int i = 0; i < p.numparams; i++)
stack[i] = varargs.arg(i+1); stack[i] = varargs.arg(i+1);
return execute(stack,p.is_vararg!=0? varargs.subargs(p.numparams+1): NONE); return execute(stack, p.is_vararg != 0? varargs.subargs(p.numparams+1): NONE);
} }
protected Varargs execute( LuaValue[] stack, Varargs varargs ) { protected Varargs execute(LuaValue[] stack, Varargs varargs) {
// loop through instructions // loop through instructions
int i,a,b,c,pc=0,top=0; int i, a, b, c, pc = 0, top = 0;
LuaValue o; LuaValue o;
Varargs v = NONE; Varargs v = NONE;
int[] code = p.code; int[] code = p.code;
LuaValue[] k = p.k; LuaValue[] k = p.k;
// upvalues are only possible when closures create closures // upvalues are only possible when closures create closures
// TODO: use linked list. // TODO: use linked list.
UpValue[] openups = p.p.length>0? new UpValue[stack.length]: null; UpValue[] openups = p.p.length > 0? new UpValue[stack.length]: null;
// allow for debug hooks // allow for debug hooks
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onCall( this, varargs, stack ); globals.debuglib.onCall(this, varargs, stack);
// process instructions // process instructions
try { try {
for (; true; ++pc) { for (; true; ++pc) {
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onInstruction( pc, v, top ); globals.debuglib.onInstruction(pc, v, top);
// pull out instruction // pull out instruction
i = code[pc]; i = code[pc];
a = ((i>>6) & 0xff); a = ((i>>6) & 0xff);
// process the op code // process the op code
switch ( i & 0x3f ) { switch (i & 0x3f) {
case Lua.OP_MOVE:/* A B R(A):= R(B) */ case Lua.OP_MOVE:/* A B R(A):= R(B) */
stack[a] = stack[i>>>23]; stack[a] = stack[i>>>23];
continue; continue;
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */ case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
stack[a] = k[i>>>14]; stack[a] = k[i>>>14];
continue; continue;
case Lua.OP_LOADKX:/* A R(A) := Kst(extra arg) */ case Lua.OP_LOADKX:/* A R(A) := Kst(extra arg) */
++pc; ++pc;
i = code[pc]; i = code[pc];
if ((i & 0x3f) != Lua.OP_EXTRAARG) { if ((i & 0x3f) != Lua.OP_EXTRAARG) {
int op = i & 0x3f; int op = i & 0x3f;
throw new LuaError("OP_EXTRAARG expected after OP_LOADKX, got " + throw new LuaError("OP_EXTRAARG expected after OP_LOADKX, got "
(op < Print.OPNAMES.length - 1 ? Print.OPNAMES[op] : "UNKNOWN_OP_" + op)); + (op < Print.OPNAMES.length-1? Print.OPNAMES[op]: "UNKNOWN_OP_" + op));
} }
stack[a] = k[i>>>6]; stack[a] = k[i>>>6];
continue; continue;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */ case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
stack[a] = (i>>>23!=0)? LuaValue.TRUE: LuaValue.FALSE; stack[a] = (i>>>23 != 0)? LuaValue.TRUE: LuaValue.FALSE;
if ((i&(0x1ff<<14)) != 0) if ((i & (0x1ff<<14)) != 0)
++pc; /* skip next instruction (if C) */ ++pc; /* skip next instruction (if C) */
continue; continue;
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */ case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */
for ( b=i>>>23; b-->=0; ) for (b = i>>>23; b-- >= 0;)
stack[a++] = LuaValue.NIL; stack[a++] = LuaValue.NIL;
continue; continue;
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */ case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
stack[a] = upValues[i>>>23].getValue(); stack[a] = upValues[i>>>23].getValue();
continue; continue;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */ case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
stack[a] = upValues[i>>>23].getValue().get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = upValues[i>>>23].getValue().get((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */ case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
stack[a] = stack[i>>>23].get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = stack[i>>>23].get((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */ case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
upValues[a].getValue().set(((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]), (c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); upValues[a].getValue().set(((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b]),
(c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */ case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
upValues[i>>>23].setValue(stack[a]); upValues[i>>>23].setValue(stack[a]);
continue; continue;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */ case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
stack[a].set(((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]), (c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a].set(((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b]),
(c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */ case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
stack[a] = new LuaTable(i>>>23,(i>>14)&0x1ff); stack[a] = new LuaTable(i>>>23, (i>>14) & 0x1ff);
continue; continue;
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */ case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
stack[a+1] = (o = stack[i>>>23]); stack[a+1] = (o = stack[i>>>23]);
stack[a] = o.get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = o.get((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */ case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).add((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.add((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */ case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).sub((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.sub((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */ case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mul((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.mul((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */ case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).div((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.div((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */ case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mod((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.mod((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */ case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).pow((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); stack[a] = ((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.pow((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]);
continue; continue;
case Lua.OP_UNM: /* A B R(A):= -R(B) */ case Lua.OP_UNM: /* A B R(A):= -R(B) */
stack[a] = stack[i>>>23].neg(); stack[a] = stack[i>>>23].neg();
continue; continue;
case Lua.OP_NOT: /* A B R(A):= not R(B) */ case Lua.OP_NOT: /* A B R(A):= not R(B) */
stack[a] = stack[i>>>23].not(); stack[a] = stack[i>>>23].not();
continue; continue;
case Lua.OP_LEN: /* A B R(A):= length of R(B) */ case Lua.OP_LEN: /* A B R(A):= length of R(B) */
stack[a] = stack[i>>>23].len(); stack[a] = stack[i>>>23].len();
continue; continue;
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */ case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
b = i>>>23; b = i>>>23;
c = (i>>14)&0x1ff; c = (i>>14) & 0x1ff; {
{ if (c > b+1) {
if ( c > b+1 ) { Buffer sb = stack[c].buffer();
Buffer sb = stack[c].buffer(); while ( --c >= b )
while ( --c>=b ) sb.concatTo(stack[c]);
sb.concatTo(stack[c]); stack[a] = sb.value();
stack[a] = sb.value(); } else {
} else { stack[a] = stack[c-1].concat(stack[c]);
stack[a] = stack[c-1].concat(stack[c]);
}
} }
}
continue; continue;
case Lua.OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ case Lua.OP_JMP: /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
pc += (i>>>14)-0x1ffff; pc += (i>>>14)-0x1ffff;
if (a > 0) { if (a > 0) {
for (--a, b = openups.length; --b>=0; ) for (--a, b = openups.length; --b >= 0;)
if (openups[b] != null && openups[b].index >= a) { if (openups[b] != null && openups[b].index >= a) {
openups[b].close(); openups[b].close();
openups[b] = null; openups[b] = null;
} }
} }
continue; continue;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).eq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) ) if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.eq_b((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc; ++pc;
continue; continue;
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lt_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) ) if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.lt_b((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc; ++pc;
continue; continue;
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lteq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) ) if (((b = i>>>23) > 0xff? k[b & 0x0ff]: stack[b])
.lteq_b((c = (i>>14) & 0x1ff) > 0xff? k[c & 0x0ff]: stack[c]) != (a != 0))
++pc; ++pc;
continue; continue;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */ case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
if ( stack[a].toboolean() != ((i&(0x1ff<<14))!=0) ) if (stack[a].toboolean() != ((i & (0x1ff<<14)) != 0))
++pc; ++pc;
continue; continue;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */ case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
/* note: doc appears to be reversed */ /* note: doc appears to be reversed */
if ( (o=stack[i>>>23]).toboolean() != ((i&(0x1ff<<14))!=0) ) if ((o = stack[i>>>23]).toboolean() != ((i & (0x1ff<<14)) != 0))
++pc; ++pc;
else else
stack[a] = o; // TODO: should be sBx? stack[a] = o; // TODO: should be sBx?
continue; continue;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */ case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
switch ( i & (Lua.MASK_B | Lua.MASK_C) ) { switch (i & (Lua.MASK_B | Lua.MASK_C)) {
case (1<<Lua.POS_B) | (0<<Lua.POS_C): v=stack[a].invoke(NONE); top=a+v.narg(); continue; case (1<<Lua.POS_B) | (0<<Lua.POS_C):
case (2<<Lua.POS_B) | (0<<Lua.POS_C): v=stack[a].invoke(stack[a+1]); top=a+v.narg(); continue; v = stack[a].invoke(NONE);
case (1<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(); continue; top = a+v.narg();
case (2<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(stack[a+1]); continue; continue;
case (3<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(stack[a+1],stack[a+2]); continue; case (2<<Lua.POS_B) | (0<<Lua.POS_C):
case (4<<Lua.POS_B) | (1<<Lua.POS_C): stack[a].call(stack[a+1],stack[a+2],stack[a+3]); continue; v = stack[a].invoke(stack[a+1]);
case (1<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(); continue; top = a+v.narg();
case (2<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(stack[a+1]); continue; continue;
case (3<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(stack[a+1],stack[a+2]); continue; case (1<<Lua.POS_B) | (1<<Lua.POS_C):
case (4<<Lua.POS_B) | (2<<Lua.POS_C): stack[a] = stack[a].call(stack[a+1],stack[a+2],stack[a+3]); continue; stack[a].call();
continue;
case (2<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call(stack[a+1]);
continue;
case (3<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call(stack[a+1], stack[a+2]);
continue;
case (4<<Lua.POS_B) | (1<<Lua.POS_C):
stack[a].call(stack[a+1], stack[a+2], stack[a+3]);
continue;
case (1<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call();
continue;
case (2<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call(stack[a+1]);
continue;
case (3<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call(stack[a+1], stack[a+2]);
continue;
case (4<<Lua.POS_B) | (2<<Lua.POS_C):
stack[a] = stack[a].call(stack[a+1], stack[a+2], stack[a+3]);
continue;
default: default:
b = i>>>23; b = i>>>23;
c = (i>>14)&0x1ff; c = (i>>14) & 0x1ff;
v = stack[a].invoke(b>0? v = stack[a].invoke(b > 0? varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack, a+1, b-1): // exact arg count varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top
varargsOf(stack, a+1, top-v.narg()-(a+1), v)); // from prev top if (c > 0) {
if ( c > 0 ) {
v.copyto(stack, a, c-1); v.copyto(stack, a, c-1);
v = NONE; v = NONE;
} else { } else {
top = a + v.narg(); top = a+v.narg();
v = v.dealias(); v = v.dealias();
} }
continue; continue;
} }
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
switch ( i & Lua.MASK_B ) { switch (i & Lua.MASK_B) {
case (1<<Lua.POS_B): return new TailcallVarargs(stack[a], NONE); case (1<<Lua.POS_B):
case (2<<Lua.POS_B): return new TailcallVarargs(stack[a], stack[a+1]); return new TailcallVarargs(stack[a], NONE);
case (3<<Lua.POS_B): return new TailcallVarargs(stack[a], varargsOf(stack[a+1],stack[a+2])); case (2<<Lua.POS_B):
case (4<<Lua.POS_B): return new TailcallVarargs(stack[a], varargsOf(stack[a+1],stack[a+2],stack[a+3])); return new TailcallVarargs(stack[a], stack[a+1]);
case (3<<Lua.POS_B):
return new TailcallVarargs(stack[a], varargsOf(stack[a+1], stack[a+2]));
case (4<<Lua.POS_B):
return new TailcallVarargs(stack[a], varargsOf(stack[a+1], stack[a+2], stack[a+3]));
default: default:
b = i>>>23; b = i>>>23;
v = b>0? v = b > 0? varargsOf(stack, a+1, b-1): // exact arg count
varargsOf(stack,a+1,b-1): // exact arg count
varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top
return new TailcallVarargs( stack[a], v ); return new TailcallVarargs(stack[a], v);
} }
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */ case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
b = i>>>23; b = i>>>23;
switch ( b ) { switch (b) {
case 0: return varargsOf(stack, a, top-v.narg()-a, v); case 0:
case 1: return NONE; return varargsOf(stack, a, top-v.narg()-a, v);
case 2: return stack[a]; case 1:
return NONE;
case 2:
return stack[a];
default: default:
return varargsOf(stack, a, b-1); return varargsOf(stack, a, b-1);
} }
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/ case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/
{ {
LuaValue limit = stack[a + 1]; LuaValue limit = stack[a+1];
LuaValue step = stack[a + 2]; LuaValue step = stack[a+2];
LuaValue idx = stack[a].add(step); LuaValue idx = stack[a].add(step);
if (step.gt_b(0)? idx.lteq_b(limit): idx.gteq_b(limit)) { if (step.gt_b(0)? idx.lteq_b(limit): idx.gteq_b(limit)) {
stack[a] = idx; stack[a] = idx;
stack[a + 3] = idx; stack[a+3] = idx;
pc += (i>>>14)-0x1ffff;
}
}
continue;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
{
LuaValue init = stack[a].checknumber("'for' initial value must be a number");
LuaValue limit = stack[a + 1].checknumber("'for' limit must be a number");
LuaValue step = stack[a + 2].checknumber("'for' step must be a number");
stack[a] = init.sub(step);
stack[a + 1] = limit;
stack[a + 2] = step;
pc += (i>>>14)-0x1ffff; pc += (i>>>14)-0x1ffff;
} }
}
continue;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
{
LuaValue init = stack[a].checknumber("'for' initial value must be a number");
LuaValue limit = stack[a+1].checknumber("'for' limit must be a number");
LuaValue step = stack[a+2].checknumber("'for' step must be a number");
stack[a] = init.sub(step);
stack[a+1] = limit;
stack[a+2] = step;
pc += (i>>>14)-0x1ffff;
}
continue; continue;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
v = stack[a].invoke(varargsOf(stack[a+1],stack[a+2])); v = stack[a].invoke(varargsOf(stack[a+1], stack[a+2]));
c = (i>>14) & 0x1ff; c = (i>>14) & 0x1ff;
while (--c >= 0) while ( --c >= 0 )
stack[a+3+c] = v.arg(c+1); stack[a+3+c] = v.arg(c+1);
v = NONE; v = NONE;
continue; continue;
case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */ case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */
if (!stack[a+1].isnil()) { /* continue loop? */ if (!stack[a+1].isnil()) { /* continue loop? */
stack[a] = stack[a+1]; /* save control varible. */ stack[a] = stack[a+1]; /* save control varible. */
pc += (i>>>14)-0x1ffff; pc += (i>>>14)-0x1ffff;
} }
continue; continue;
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */ case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
{ {
if ( (c=(i>>14)&0x1ff) == 0 ) if ((c = (i>>14) & 0x1ff) == 0)
c = code[++pc]; c = code[++pc];
int offset = (c-1) * Lua.LFIELDS_PER_FLUSH; int offset = (c-1)*Lua.LFIELDS_PER_FLUSH;
o = stack[a]; o = stack[a];
if ( (b=i>>>23) == 0 ) { if ((b = i>>>23) == 0) {
b = top - a - 1; b = top-a-1;
int m = b - v.narg(); int m = b-v.narg();
int j=1; int j = 1;
for ( ;j<=m; j++ ) for (; j <= m; j++)
o.set(offset+j, stack[a + j]); o.set(offset+j, stack[a+j]);
for ( ;j<=b; j++ ) for (; j <= b; j++)
o.set(offset+j, v.arg(j-m)); o.set(offset+j, v.arg(j-m));
} else { } else {
o.presize( offset + b ); o.presize(offset+b);
for (int j=1; j<=b; j++) for (int j = 1; j <= b; j++)
o.set(offset+j, stack[a + j]); o.set(offset+j, stack[a+j]);
}
} }
}
continue; continue;
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx]) */ case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx]) */
{ {
Prototype newp = p.p[i>>>14]; Prototype newp = p.p[i>>>14];
LuaClosure ncl = new LuaClosure(newp, globals); LuaClosure ncl = new LuaClosure(newp, globals);
Upvaldesc[] uv = newp.upvalues; Upvaldesc[] uv = newp.upvalues;
for ( int j=0, nup=uv.length; j<nup; ++j ) { for (int j = 0, nup = uv.length; j < nup; ++j) {
if (uv[j].instack) /* upvalue refes to local variable? */ if (uv[j].instack) /* upvalue refes to local variable? */
ncl.upValues[j] = findupval(stack, uv[j].idx, openups); ncl.upValues[j] = findupval(stack, uv[j].idx, openups);
else /* get upvalue from enclosing function */ else /* get upvalue from enclosing function */
ncl.upValues[j] = upValues[uv[j].idx]; ncl.upValues[j] = upValues[uv[j].idx];
}
stack[a] = ncl;
} }
stack[a] = ncl;
}
continue; continue;
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
b = i>>>23; b = i>>>23;
if ( b == 0 ) { if (b == 0) {
top = a + (b = varargs.narg()); top = a+(b = varargs.narg());
v = varargs; v = varargs;
} else { } else {
for ( int j=1; j<b; ++j ) for (int j = 1; j < b; ++j)
stack[a+j-1] = varargs.arg(j); stack[a+j-1] = varargs.arg(j);
} }
continue; continue;
@@ -508,18 +579,18 @@ public class LuaClosure extends LuaFunction {
throw new java.lang.IllegalArgumentException("Illegal opcode: " + (i & 0x3f)); throw new java.lang.IllegalArgumentException("Illegal opcode: " + (i & 0x3f));
} }
} }
} catch ( LuaError le ) { } catch (LuaError le) {
if (le.traceback == null) if (le.traceback == null)
processErrorHooks(le, p, pc); processErrorHooks(le, p, pc);
throw le; throw le;
} catch ( Exception e ) { } catch (Exception e) {
LuaError le = new LuaError(e); LuaError le = new LuaError(e);
processErrorHooks(le, p, pc); processErrorHooks(le, p, pc);
throw le; throw le;
} finally { } finally {
if ( openups != null ) if (openups != null)
for ( int u=openups.length; --u>=0; ) for (int u = openups.length; --u >= 0;)
if ( openups[u] != null ) if (openups[u] != null)
openups[u].close(); openups[u].close();
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn(); globals.debuglib.onReturn();
@@ -527,21 +598,21 @@ public class LuaClosure extends LuaFunction {
} }
/** /**
* Run the error hook if there is one * Run the error hook if there is one
* @param msg the message to use in error hook processing. *
* */ * @param msg the message to use in error hook processing.
*/
String errorHook(String msg, int level) { String errorHook(String msg, int level) {
if (globals == null ) return msg; if (globals == null)
return msg;
final LuaThread r = globals.running; final LuaThread r = globals.running;
if (r.errorfunc == null) if (r.errorfunc == null)
return globals.debuglib != null? return globals.debuglib != null? msg + "\n" + globals.debuglib.traceback(level): msg;
msg + "\n" + globals.debuglib.traceback(level):
msg;
final LuaValue e = r.errorfunc; final LuaValue e = r.errorfunc;
r.errorfunc = null; r.errorfunc = null;
try { try {
return e.call( LuaValue.valueOf(msg) ).tojstring(); return e.call(LuaValue.valueOf(msg)).tojstring();
} catch ( Throwable t ) { } catch (Throwable t) {
return "error in error handling"; return "error in error handling";
} finally { } finally {
r.errorfunc = e; r.errorfunc = e;
@@ -557,19 +628,19 @@ public class LuaClosure extends LuaFunction {
frame = globals.debuglib.getCallFrame(le.level); frame = globals.debuglib.getCallFrame(le.level);
if (frame != null) { if (frame != null) {
String src = frame.shortsource(); String src = frame.shortsource();
file = src != null ? src : "?"; file = src != null? src: "?";
line = frame.currentline(); line = frame.currentline();
} }
} }
if (frame == null) { if (frame == null) {
file = p.source != null? p.source.tojstring(): "?"; file = p.source != null? p.source.tojstring(): "?";
line = p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length ? p.lineinfo[pc] : -1; line = p.lineinfo != null && pc >= 0 && pc < p.lineinfo.length? p.lineinfo[pc]: -1;
} }
} }
le.fileline = file + ":" + line; le.fileline = file + ":" + line;
le.traceback = errorHook(le.getMessage(), le.level); le.traceback = errorHook(le.getMessage(), le.level);
} }
private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) { private UpValue findupval(LuaValue[] stack, short idx, UpValue[] openups) {
final int n = openups.length; final int n = openups.length;
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
@@ -585,14 +656,13 @@ public class LuaClosure extends LuaFunction {
protected LuaValue getUpvalue(int i) { protected LuaValue getUpvalue(int i) {
return upValues[i].getValue(); return upValues[i].getValue();
} }
protected void setUpvalue(int i, LuaValue v) { protected void setUpvalue(int i, LuaValue v) {
upValues[i].setValue(v); upValues[i].setValue(v);
} }
public String name() { public String name() {
return "<"+p.shortsource()+":"+p.linedefined+">"; return "<" + p.shortsource() + ":" + p.linedefined + ">";
} }
} }

View File

@@ -26,18 +26,21 @@ import org.luaj.vm2.lib.MathLib;
/** /**
* Extension of {@link LuaNumber} which can hold a Java double as its value. * Extension of {@link LuaNumber} which can hold a Java double as its value.
* <p> * <p>
* These instance are not instantiated directly by clients, but indirectly * These instance are not instantiated directly by clients, but indirectly via
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)} * the static functions {@link LuaValue#valueOf(int)} or
* functions. This ensures that values which can be represented as int * {@link LuaValue#valueOf(double)} functions. This ensures that values which
* are wrapped in {@link LuaInteger} instead of {@link LuaDouble}. * can be represented as int are wrapped in {@link LuaInteger} instead of
* {@link LuaDouble}.
* <p> * <p>
* Almost all API's implemented in LuaDouble are defined and documented in {@link LuaValue}. * Almost all API's implemented in LuaDouble are defined and documented in
* {@link LuaValue}.
* <p> * <p>
* However the constants {@link #NAN}, {@link #POSINF}, {@link #NEGINF}, * However the constants {@link #NAN}, {@link #POSINF}, {@link #NEGINF},
* {@link #JSTR_NAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be useful * {@link #JSTR_NAN}, {@link #JSTR_POSINF}, and {@link #JSTR_NEGINF} may be
* when dealing with Nan or Infinite values. * useful when dealing with Nan or Infinite values.
* <p> * <p>
* LuaDouble also defines functions for handling the unique math rules of lua devision and modulo in * LuaDouble also defines functions for handling the unique math rules of lua
* devision and modulo in
* <ul> * <ul>
* <li>{@link #ddiv(double, double)}</li> * <li>{@link #ddiv(double, double)}</li>
* <li>{@link #ddiv_d(double, double)}</li> * <li>{@link #ddiv_d(double, double)}</li>
@@ -45,6 +48,7 @@ import org.luaj.vm2.lib.MathLib;
* <li>{@link #dmod_d(double, double)}</li> * <li>{@link #dmod_d(double, double)}</li>
* </ul> * </ul>
* <p> * <p>
*
* @see LuaValue * @see LuaValue
* @see LuaNumber * @see LuaNumber
* @see LuaInteger * @see LuaInteger
@@ -54,186 +58,260 @@ import org.luaj.vm2.lib.MathLib;
public class LuaDouble extends LuaNumber { public class LuaDouble extends LuaNumber {
/** Constant LuaDouble representing NaN (not a number) */ /** Constant LuaDouble representing NaN (not a number) */
public static final LuaDouble NAN = new LuaDouble( Double.NaN ); public static final LuaDouble NAN = new LuaDouble(Double.NaN);
/** Constant LuaDouble representing positive infinity */ /** Constant LuaDouble representing positive infinity */
public static final LuaDouble POSINF = new LuaDouble( Double.POSITIVE_INFINITY ); public static final LuaDouble POSINF = new LuaDouble(Double.POSITIVE_INFINITY);
/** Constant LuaDouble representing negative infinity */ /** Constant LuaDouble representing negative infinity */
public static final LuaDouble NEGINF = new LuaDouble( Double.NEGATIVE_INFINITY ); public static final LuaDouble NEGINF = new LuaDouble(Double.NEGATIVE_INFINITY);
/** Constant String representation for NaN (not a number), "nan" */ /** Constant String representation for NaN (not a number), "nan" */
public static final String JSTR_NAN = "nan"; public static final String JSTR_NAN = "nan";
/** Constant String representation for positive infinity, "inf" */ /** Constant String representation for positive infinity, "inf" */
public static final String JSTR_POSINF = "inf"; public static final String JSTR_POSINF = "inf";
/** Constant String representation for negative infinity, "-inf" */ /** Constant String representation for negative infinity, "-inf" */
public static final String JSTR_NEGINF = "-inf"; public static final String JSTR_NEGINF = "-inf";
/** The value being held by this instance. */ /** The value being held by this instance. */
final double v; final double v;
public static LuaNumber valueOf(double d) { public static LuaNumber valueOf(double d) {
int id = (int) d; int id = (int) d;
return d==id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d); return d == id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d);
} }
/** Don't allow ints to be boxed by DoubleValues */ /** Don't allow ints to be boxed by DoubleValues */
private LuaDouble(double d) { private LuaDouble(double d) {
this.v = d; this.v = d;
} }
public int hashCode() { public int hashCode() {
long l = Double.doubleToLongBits(v + 1); long l = Double.doubleToLongBits(v+1);
return ((int)(l>>32)) + (int) l; return ((int) (l>>32))+(int) l;
} }
public boolean islong() { public boolean islong() {
return v == (long) v; return v == (long) v;
} }
public byte tobyte() { return (byte) (long) v; }
public char tochar() { return (char) (long) v; }
public double todouble() { return v; }
public float tofloat() { return (float) v; }
public int toint() { return (int) (long) v; }
public long tolong() { return (long) v; }
public short toshort() { return (short) (long) v; }
public double optdouble(double defval) { return v; } public byte tobyte() { return (byte) (long) v; }
public int optint(int defval) { return (int) (long) v; }
public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long)v); } public char tochar() { return (char) (long) v; }
public long optlong(long defval) { return (long) v; }
public double todouble() { return v; }
public LuaInteger checkinteger() { return LuaInteger.valueOf( (int) (long) v ); }
public float tofloat() { return (float) v; }
public int toint() { return (int) (long) v; }
public long tolong() { return (long) v; }
public short toshort() { return (short) (long) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return (int) (long) v; }
public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long) v); }
public long optlong(long defval) { return (long) v; }
public LuaInteger checkinteger() { return LuaInteger.valueOf((int) (long) v); }
// unary operators // unary operators
public LuaValue neg() { return valueOf(-v); } public LuaValue neg() { return valueOf(-v); }
// object equality, used for key comparison // object equality, used for key comparison
public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble)o).v == v: false; } public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble) o).v == v: false; }
// equality w/ metatable processing // equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return val.raweq(v)? TRUE: FALSE; } public LuaValue eq(LuaValue val) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) { return val.raweq(v); }
public boolean eq_b(LuaValue val) { return val.raweq(v); }
// equality w/o metatable processing // equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(v); } public boolean raweq(LuaValue val) { return val.raweq(v); }
public boolean raweq( double val ) { return v == val; }
public boolean raweq( int val ) { return v == val; } public boolean raweq(double val) { return v == val; }
public boolean raweq(int val) { return v == val; }
// basic binary arithmetic // basic binary arithmetic
public LuaValue add( LuaValue rhs ) { return rhs.add(v); } public LuaValue add(LuaValue rhs) { return rhs.add(v); }
public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); }
public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); } public LuaValue add(double lhs) { return LuaDouble.valueOf(lhs+v); }
public LuaValue sub( double rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue sub( int rhs ) { return LuaDouble.valueOf(v - rhs); } public LuaValue sub(LuaValue rhs) { return rhs.subFrom(v); }
public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); }
public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); } public LuaValue sub(double rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue mul( int lhs ) { return LuaDouble.valueOf(lhs * v); } public LuaValue sub(int rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); }
public LuaValue pow( double rhs ) { return MathLib.dpow(v,rhs); } public LuaValue subFrom(double lhs) { return LuaDouble.valueOf(lhs-v); }
public LuaValue pow( int rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); } public LuaValue mul(LuaValue rhs) { return rhs.mul(v); }
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); } public LuaValue mul(double lhs) { return LuaDouble.valueOf(lhs*v); }
public LuaValue div( double rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue div( int rhs ) { return LuaDouble.ddiv(v,rhs); } public LuaValue mul(int lhs) { return LuaDouble.valueOf(lhs*v); }
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); }
public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); } public LuaValue pow(LuaValue rhs) { return rhs.powWith(v); }
public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); } public LuaValue pow(double rhs) { return MathLib.dpow(v, rhs); }
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
public LuaValue pow(int rhs) { return MathLib.dpow(v, rhs); }
/** Divide two double numbers according to lua math, and return a {@link LuaValue} result. public LuaValue powWith(double lhs) { return MathLib.dpow(lhs, v); }
public LuaValue powWith(int lhs) { return MathLib.dpow(lhs, v); }
public LuaValue div(LuaValue rhs) { return rhs.divInto(v); }
public LuaValue div(double rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue div(int rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue divInto(double lhs) { return LuaDouble.ddiv(lhs, v); }
public LuaValue mod(LuaValue rhs) { return rhs.modFrom(v); }
public LuaValue mod(double rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue mod(int rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue modFrom(double lhs) { return LuaDouble.dmod(lhs, v); }
/**
* Divide two double numbers according to lua math, and return a
* {@link LuaValue} result.
*
* @param lhs Left-hand-side of the division. * @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division. * @param rhs Right-hand-side of the division.
* @return {@link LuaValue} for the result of the division, * @return {@link LuaValue} for the result of the division, taking into
* taking into account positive and negiative infinity, and Nan * account positive and negiative infinity, and Nan
* @see #ddiv_d(double, double) * @see #ddiv_d(double, double)
*/ */
public static LuaValue ddiv(double lhs, double rhs) { public static LuaValue ddiv(double lhs, double rhs) {
return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF; return rhs != 0? valueOf(lhs/rhs): lhs > 0? POSINF: lhs == 0? NAN: NEGINF;
} }
/** Divide two double numbers according to lua math, and return a double result. /**
* Divide two double numbers according to lua math, and return a double
* result.
*
* @param lhs Left-hand-side of the division. * @param lhs Left-hand-side of the division.
* @param rhs Right-hand-side of the division. * @param rhs Right-hand-side of the division.
* @return Value of the division, taking into account positive and negative infinity, and Nan * @return Value of the division, taking into account positive and negative
* infinity, and Nan
* @see #ddiv(double, double) * @see #ddiv(double, double)
*/ */
public static double ddiv_d(double lhs, double rhs) { public static double ddiv_d(double lhs, double rhs) {
return rhs!=0? lhs / rhs: lhs>0? Double.POSITIVE_INFINITY: lhs==0? Double.NaN: Double.NEGATIVE_INFINITY; return rhs != 0? lhs/rhs: lhs > 0? Double.POSITIVE_INFINITY: lhs == 0? Double.NaN: Double.NEGATIVE_INFINITY;
} }
/** Take modulo double numbers according to lua math, and return a {@link LuaValue} result. /**
* Take modulo double numbers according to lua math, and return a
* {@link LuaValue} result.
*
* @param lhs Left-hand-side of the modulo. * @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo. * @param rhs Right-hand-side of the modulo.
* @return {@link LuaValue} for the result of the modulo, * @return {@link LuaValue} for the result of the modulo, using lua's rules
* using lua's rules for modulo * for modulo
* @see #dmod_d(double, double) * @see #dmod_d(double, double)
*/ */
public static LuaValue dmod(double lhs, double rhs) { public static LuaValue dmod(double lhs, double rhs) {
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return NAN; if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY)
return NAN;
if (rhs == Double.POSITIVE_INFINITY) { if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? POSINF : valueOf(lhs); return lhs < 0? POSINF: valueOf(lhs);
} }
if (rhs == Double.NEGATIVE_INFINITY) { if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? NEGINF : valueOf(lhs); return lhs > 0? NEGINF: valueOf(lhs);
} }
return valueOf( lhs-rhs*Math.floor(lhs/rhs) ); return valueOf(lhs-rhs*Math.floor(lhs/rhs));
} }
/** Take modulo for double numbers according to lua math, and return a double result. /**
* Take modulo for double numbers according to lua math, and return a double
* result.
*
* @param lhs Left-hand-side of the modulo. * @param lhs Left-hand-side of the modulo.
* @param rhs Right-hand-side of the modulo. * @param rhs Right-hand-side of the modulo.
* @return double value for the result of the modulo, * @return double value for the result of the modulo, using lua's rules for
* using lua's rules for modulo * modulo
* @see #dmod(double, double) * @see #dmod(double, double)
*/ */
public static double dmod_d(double lhs, double rhs) { public static double dmod_d(double lhs, double rhs) {
if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY) return Double.NaN; if (rhs == 0 || lhs == Double.POSITIVE_INFINITY || lhs == Double.NEGATIVE_INFINITY)
return Double.NaN;
if (rhs == Double.POSITIVE_INFINITY) { if (rhs == Double.POSITIVE_INFINITY) {
return lhs < 0 ? Double.POSITIVE_INFINITY : lhs; return lhs < 0? Double.POSITIVE_INFINITY: lhs;
} }
if (rhs == Double.NEGATIVE_INFINITY) { if (rhs == Double.NEGATIVE_INFINITY) {
return lhs > 0 ? Double.NEGATIVE_INFINITY : lhs; return lhs > 0? Double.NEGATIVE_INFINITY: lhs;
} }
return lhs-rhs*Math.floor(lhs/rhs); return lhs-rhs*Math.floor(lhs/rhs);
} }
// relational operators // relational operators
public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); } public LuaValue lt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.gt_b(v)? TRUE: FALSE): super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; } public LuaValue lt(double rhs) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; } public LuaValue lt(int rhs) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( double rhs ) { return v < rhs; }
public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); } public boolean lt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v): super.lt_b(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; } public boolean lt_b(int rhs) { return v < rhs; }
public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; } public boolean lt_b(double rhs) { return v < rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); } public LuaValue lteq(LuaValue rhs) {
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; } return rhs instanceof LuaNumber? (rhs.gteq_b(v)? TRUE: FALSE): super.lteq(rhs);
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; } }
public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; } public LuaValue lteq(double rhs) { return v <= rhs? TRUE: FALSE; }
public boolean gt_b( double rhs ) { return v > rhs; }
public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); } public LuaValue lteq(int rhs) { return v <= rhs? TRUE: FALSE; }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; } public boolean lteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gteq_b(v): super.lteq_b(rhs); }
public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; } public boolean lteq_b(int rhs) { return v <= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
public boolean lteq_b(double rhs) { return v <= rhs; }
public LuaValue gt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.lt_b(v)? TRUE: FALSE): super.gt(rhs); }
public LuaValue gt(double rhs) { return v > rhs? TRUE: FALSE; }
public LuaValue gt(int rhs) { return v > rhs? TRUE: FALSE; }
public boolean gt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v): super.gt_b(rhs); }
public boolean gt_b(int rhs) { return v > rhs; }
public boolean gt_b(double rhs) { return v > rhs; }
public LuaValue gteq(LuaValue rhs) {
return rhs instanceof LuaNumber? (rhs.lteq_b(v)? TRUE: FALSE): super.gteq(rhs);
}
public LuaValue gteq(double rhs) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq(int rhs) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lteq_b(v): super.gteq_b(rhs); }
public boolean gteq_b(int rhs) { return v >= rhs; }
public boolean gteq_b(double rhs) { return v >= rhs; }
// string comparison // string comparison
public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; } public int strcmp(LuaString rhs) { typerror("attempt to compare number with string"); return 0; }
public String tojstring() { public String tojstring() {
/* /*
if ( v == 0.0 ) { // never occurs in J2me if ( v == 0.0 ) { // never occurs in J2me
@@ -242,58 +320,63 @@ public class LuaDouble extends LuaNumber {
} }
*/ */
long l = (long) v; long l = (long) v;
if ( l == v ) if (l == v)
return Long.toString(l); return Long.toString(l);
if ( Double.isNaN(v) ) if (Double.isNaN(v))
return JSTR_NAN; return JSTR_NAN;
if ( Double.isInfinite(v) ) if (Double.isInfinite(v))
return (v<0? JSTR_NEGINF: JSTR_POSINF); return (v < 0? JSTR_NEGINF: JSTR_POSINF);
return Float.toString((float)v); return Float.toString((float) v);
} }
public LuaString strvalue() { public LuaString strvalue() {
return LuaString.valueOf(tojstring()); return LuaString.valueOf(tojstring());
} }
public LuaString optstring(LuaString defval) { public LuaString optstring(LuaString defval) {
return LuaString.valueOf(tojstring()); return LuaString.valueOf(tojstring());
} }
public LuaValue tostring() { public LuaValue tostring() {
return LuaString.valueOf(tojstring()); return LuaString.valueOf(tojstring());
} }
public String optjstring(String defval) { public String optjstring(String defval) {
return tojstring(); return tojstring();
} }
public LuaNumber optnumber(LuaNumber defval) { public LuaNumber optnumber(LuaNumber defval) {
return this; return this;
} }
public boolean isnumber() { public boolean isnumber() {
return true; return true;
} }
public boolean isstring() { public boolean isstring() {
return true; return true;
} }
public LuaValue tonumber() { public LuaValue tonumber() {
return this; return this;
} }
public int checkint() { return (int) (long) v; }
public long checklong() { return (long) v; } public int checkint() { return (int) (long) v; }
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; } public long checklong() { return (long) v; }
public LuaNumber checknumber() { return this; }
public double checkdouble() { return v; }
public String checkjstring() { public String checkjstring() {
return tojstring(); return tojstring();
} }
public LuaString checkstring() { public LuaString checkstring() {
return LuaString.valueOf(tojstring()); return LuaString.valueOf(tojstring());
} }
public boolean isvalidkey() { public boolean isvalidkey() {
return !Double.isNaN(v); return !Double.isNaN(v);
} }

View File

@@ -21,38 +21,38 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* RuntimeException that is thrown and caught in response to a lua error. * RuntimeException that is thrown and caught in response to a lua error.
* <p> * <p>
* {@link LuaError} is used wherever a lua call to {@code error()} * {@link LuaError} is used wherever a lua call to {@code error()} would be used
* would be used within a script. * within a script.
* <p> * <p>
* Since it is an unchecked exception inheriting from {@link RuntimeException}, * Since it is an unchecked exception inheriting from {@link RuntimeException},
* Java method signatures do notdeclare this exception, althoug it can * Java method signatures do notdeclare this exception, althoug it can be thrown
* be thrown on almost any luaj Java operation. * on almost any luaj Java operation. This is analagous to the fact that any lua
* This is analagous to the fact that any lua script can throw a lua error at any time. * script can throw a lua error at any time.
* <p> * <p>
* The LuaError may be constructed with a message object, in which case the message * The LuaError may be constructed with a message object, in which case the
* is the string representation of that object. getMessageObject will get the object * message is the string representation of that object. getMessageObject will
* supplied at construct time, or a LuaString containing the message of an object * get the object supplied at construct time, or a LuaString containing the
* was not supplied. * message of an object was not supplied.
*/ */
public class LuaError extends RuntimeException { public class LuaError extends RuntimeException {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
protected int level; protected int level;
protected String fileline; protected String fileline;
protected String traceback; protected String traceback;
protected Throwable cause; protected Throwable cause;
private LuaValue object; private LuaValue object;
/** Get the string message if it was supplied, or a string /**
* representation of the message object if that was supplied. * Get the string message if it was supplied, or a string representation of
* the message object if that was supplied.
*/ */
public String getMessage() { public String getMessage() {
if (traceback != null) if (traceback != null)
@@ -65,66 +65,70 @@ public class LuaError extends RuntimeException {
return m; return m;
} }
/** Get the LuaValue that was provided in the constructor, or /**
* a LuaString containing the message if it was a string error argument. * Get the LuaValue that was provided in the constructor, or a LuaString
* containing the message if it was a string error argument.
*
* @return LuaValue which was used in the constructor, or a LuaString * @return LuaValue which was used in the constructor, or a LuaString
* containing the message. * containing the message.
*/ */
public LuaValue getMessageObject() { public LuaValue getMessageObject() {
if (object != null) return object; if (object != null)
return object;
String m = getMessage(); String m = getMessage();
return m != null ? LuaValue.valueOf(m): null; return m != null? LuaValue.valueOf(m): null;
} }
/** Construct LuaError when a program exception occurs. /**
* <p> * Construct LuaError when a program exception occurs.
* <p>
* All errors generated from lua code should throw LuaError(String) instead. * All errors generated from lua code should throw LuaError(String) instead.
* @param cause the Throwable that caused the error, if known. *
* @param cause the Throwable that caused the error, if known.
*/ */
public LuaError(Throwable cause) { public LuaError(Throwable cause) {
super( "vm error: "+cause ); super("vm error: " + cause);
this.cause = cause; this.cause = cause;
this.level = 1; this.level = 1;
} }
/** /**
* Construct a LuaError with a specific message. * Construct a LuaError with a specific message.
* *
* @param message message to supply * @param message message to supply
*/ */
public LuaError(String message) { public LuaError(String message) {
super( message ); super(message);
this.level = 1; this.level = 1;
} }
/** /**
* Construct a LuaError with a message, and level to draw line number information from. * Construct a LuaError with a message, and level to draw line number
* information from.
*
* @param message message to supply * @param message message to supply
* @param level where to supply line info from in call stack * @param level where to supply line info from in call stack
*/ */
public LuaError(String message, int level) { public LuaError(String message, int level) {
super( message ); super(message);
this.level = level; this.level = level;
} }
/** /**
* Construct a LuaError with a LuaValue as the message object, * Construct a LuaError with a LuaValue as the message object, and level to
* and level to draw line number information from. * draw line number information from.
*
* @param message_object message string or object to supply * @param message_object message string or object to supply
*/ */
public LuaError(LuaValue message_object) { public LuaError(LuaValue message_object) {
super( message_object.tojstring() ); super(message_object.tojstring());
this.object = message_object; this.object = message_object;
this.level = 1; this.level = 1;
}
/**
* Get the cause, if any.
*/
public Throwable getCause() {
return cause;
} }
/**
* Get the cause, if any.
*/
public Throwable getCause() { return cause; }
} }

View File

@@ -21,41 +21,39 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Base class for functions implemented in Java. * Base class for functions implemented in Java.
* <p> * <p>
* Direct subclass include {@link org.luaj.vm2.lib.LibFunction} * Direct subclass include {@link org.luaj.vm2.lib.LibFunction} which is the
* which is the base class for * base class for all built-in library functions coded in Java, and
* all built-in library functions coded in Java, * {@link LuaClosure}, which represents a lua closure whose bytecode is
* and {@link LuaClosure}, which represents a lua closure * interpreted when the function is invoked.
* whose bytecode is interpreted when the function is invoked. *
* @see LuaValue * @see LuaValue
* @see LuaClosure * @see LuaClosure
* @see org.luaj.vm2.lib.LibFunction * @see org.luaj.vm2.lib.LibFunction
*/ */
abstract abstract public class LuaFunction extends LuaValue {
public class LuaFunction extends LuaValue {
/** Shared static metatable for all functions and closures. */ /** Shared static metatable for all functions and closures. */
public static LuaValue s_metatable; public static LuaValue s_metatable;
public int type() { public int type() {
return TFUNCTION; return TFUNCTION;
} }
public String typename() { public String typename() {
return "function"; return "function";
} }
public boolean isfunction() { public boolean isfunction() {
return true; return true;
} }
public LuaFunction checkfunction() { public LuaFunction checkfunction() {
return this; return this;
} }
public LuaFunction optfunction(LuaFunction defval) { public LuaFunction optfunction(LuaFunction defval) {
return this; return this;
} }
@@ -72,20 +70,29 @@ public class LuaFunction extends LuaValue {
return valueOf(tojstring()); return valueOf(tojstring());
} }
/** Return the last part of the class name, to be used as a function name in tojstring and elsewhere. /**
* @return String naming the last part of the class name after the last dot (.) or dollar sign ($). * Return the last part of the class name, to be used as a function name in
* If the first character is '_', it is skipped. * tojstring and elsewhere.
*
* @return String naming the last part of the class name after the last dot
* (.) or dollar sign ($). If the first character is '_', it is
* skipped.
*/ */
public String classnamestub() { public String classnamestub() {
String s = getClass().getName(); String s = getClass().getName();
int offset = Math.max(s.lastIndexOf('.'), s.lastIndexOf('$')) + 1; int offset = Math.max(s.lastIndexOf('.'), s.lastIndexOf('$'))+1;
if (s.charAt(offset) == '_') offset++; if (s.charAt(offset) == '_')
offset++;
return s.substring(offset); return s.substring(offset);
} }
/** Return a human-readable name for this function. Returns the last part of the class name by default. /**
* Is overridden by LuaClosure to return the source file and line, and by LibFunctions to return the name. * Return a human-readable name for this function. Returns the last part of
* @return common name for this function. */ * the class name by default. Is overridden by LuaClosure to return the
* source file and line, and by LibFunctions to return the name.
*
* @return common name for this function.
*/
public String name() { public String name() {
return classnamestub(); return classnamestub();
} }

View File

@@ -26,13 +26,13 @@ import org.luaj.vm2.lib.MathLib;
/** /**
* Extension of {@link LuaNumber} which can hold a Java int as its value. * Extension of {@link LuaNumber} which can hold a Java int as its value.
* <p> * <p>
* These instance are not instantiated directly by clients, but indirectly * These instance are not instantiated directly by clients, but indirectly via
* via the static functions {@link LuaValue#valueOf(int)} or {@link LuaValue#valueOf(double)} * the static functions {@link LuaValue#valueOf(int)} or
* functions. This ensures that policies regarding pooling of instances are * {@link LuaValue#valueOf(double)} functions. This ensures that policies
* encapsulated. * regarding pooling of instances are encapsulated.
* <p> * <p>
* There are no API's specific to LuaInteger that are useful beyond what is already * There are no API's specific to LuaInteger that are useful beyond what is
* exposed in {@link LuaValue}. * already exposed in {@link LuaValue}.
* *
* @see LuaValue * @see LuaValue
* @see LuaNumber * @see LuaNumber
@@ -44,16 +44,18 @@ public class LuaInteger extends LuaNumber {
private static final LuaInteger[] intValues = new LuaInteger[512]; private static final LuaInteger[] intValues = new LuaInteger[512];
static { static {
for ( int i=0; i<512; i++ ) for (int i = 0; i < 512; i++)
intValues[i] = new LuaInteger(i-256); intValues[i] = new LuaInteger(i-256);
} }
public static LuaInteger valueOf(int i) { public static LuaInteger valueOf(int i) {
return i<=255 && i>=-256? intValues[i+256]: new LuaInteger(i); return i <= 255 && i >= -256? intValues[i+256]: new LuaInteger(i);
}; };
// TODO consider moving this to LuaValue // TODO consider moving this to LuaValue
/** Return a LuaNumber that represents the value provided /**
* Return a LuaNumber that represents the value provided
*
* @param l long value to represent. * @param l long value to represent.
* @return LuaNumber that is eithe LuaInteger or LuaDouble representing l * @return LuaNumber that is eithe LuaInteger or LuaDouble representing l
* @see LuaValue#valueOf(int) * @see LuaValue#valueOf(int)
@@ -61,38 +63,49 @@ public class LuaInteger extends LuaNumber {
*/ */
public static LuaNumber valueOf(long l) { public static LuaNumber valueOf(long l) {
int i = (int) l; int i = (int) l;
return l==i? (i<=255 && i>=-256? intValues[i+256]: return l == i? (i <= 255 && i >= -256? intValues[i+256]: (LuaNumber) new LuaInteger(i))
(LuaNumber) new LuaInteger(i)): : (LuaNumber) LuaDouble.valueOf(l);
(LuaNumber) LuaDouble.valueOf(l);
} }
/** The value being held by this instance. */ /** The value being held by this instance. */
public final int v; public final int v;
/** /**
* Package protected constructor. * Package protected constructor.
*
* @see LuaValue#valueOf(int) * @see LuaValue#valueOf(int)
**/ **/
LuaInteger(int i) { LuaInteger(int i) {
this.v = i; this.v = i;
} }
public boolean isint() { return true; }
public boolean isinttype() { return true; }
public boolean islong() { return true; }
public byte tobyte() { return (byte) v; }
public char tochar() { return (char) v; }
public double todouble() { return v; }
public float tofloat() { return v; }
public int toint() { return v; }
public long tolong() { return v; }
public short toshort() { return (short) v; }
public double optdouble(double defval) { return v; } public boolean isint() { return true; }
public int optint(int defval) { return v; }
public LuaInteger optinteger(LuaInteger defval) { return this; } public boolean isinttype() { return true; }
public long optlong(long defval) { return v; }
public boolean islong() { return true; }
public byte tobyte() { return (byte) v; }
public char tochar() { return (char) v; }
public double todouble() { return v; }
public float tofloat() { return v; }
public int toint() { return v; }
public long tolong() { return v; }
public short toshort() { return (short) v; }
public double optdouble(double defval) { return v; }
public int optint(int defval) { return v; }
public LuaInteger optinteger(LuaInteger defval) { return this; }
public long optlong(long defval) { return v; }
public String tojstring() { public String tojstring() {
return Integer.toString(v); return Integer.toString(v);
@@ -101,27 +114,27 @@ public class LuaInteger extends LuaNumber {
public LuaString strvalue() { public LuaString strvalue() {
return LuaString.valueOf(Integer.toString(v)); return LuaString.valueOf(Integer.toString(v));
} }
public LuaString optstring(LuaString defval) { public LuaString optstring(LuaString defval) {
return LuaString.valueOf(Integer.toString(v)); return LuaString.valueOf(Integer.toString(v));
} }
public LuaValue tostring() { public LuaValue tostring() {
return LuaString.valueOf(Integer.toString(v)); return LuaString.valueOf(Integer.toString(v));
} }
public String optjstring(String defval) { public String optjstring(String defval) {
return Integer.toString(v); return Integer.toString(v);
} }
public LuaInteger checkinteger() { public LuaInteger checkinteger() {
return this; return this;
} }
public boolean isstring() { public boolean isstring() {
return true; return true;
} }
public int hashCode() { public int hashCode() {
return v; return v;
} }
@@ -131,89 +144,146 @@ public class LuaInteger extends LuaNumber {
} }
// unary operators // unary operators
public LuaValue neg() { return valueOf(-(long)v); } public LuaValue neg() { return valueOf(-(long) v); }
// object equality, used for key comparison // object equality, used for key comparison
public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger)o).v == v: false; } public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger) o).v == v: false; }
// equality w/ metatable processing // equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return val.raweq(v)? TRUE: FALSE; } public LuaValue eq(LuaValue val) { return val.raweq(v)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) { return val.raweq(v); }
public boolean eq_b(LuaValue val) { return val.raweq(v); }
// equality w/o metatable processing // equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(v); } public boolean raweq(LuaValue val) { return val.raweq(v); }
public boolean raweq( double val ) { return v == val; }
public boolean raweq( int val ) { return v == val; } public boolean raweq(double val) { return v == val; }
public boolean raweq(int val) { return v == val; }
// arithmetic operators // arithmetic operators
public LuaValue add( LuaValue rhs ) { return rhs.add(v); } public LuaValue add(LuaValue rhs) { return rhs.add(v); }
public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); }
public LuaValue add( int lhs ) { return LuaInteger.valueOf(lhs + (long)v); } public LuaValue add(double lhs) { return LuaDouble.valueOf(lhs+v); }
public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); }
public LuaValue sub( double rhs ) { return LuaDouble.valueOf(v - rhs); } public LuaValue add(int lhs) { return LuaInteger.valueOf(lhs+(long) v); }
public LuaValue sub( int rhs ) { return LuaDouble.valueOf(v - rhs); }
public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); } public LuaValue sub(LuaValue rhs) { return rhs.subFrom(v); }
public LuaValue subFrom( int lhs ) { return LuaInteger.valueOf(lhs - (long)v); }
public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); } public LuaValue sub(double rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); }
public LuaValue mul( int lhs ) { return LuaInteger.valueOf(lhs * (long)v); } public LuaValue sub(int rhs) { return LuaDouble.valueOf(v-rhs); }
public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); }
public LuaValue pow( double rhs ) { return MathLib.dpow(v,rhs); } public LuaValue subFrom(double lhs) { return LuaDouble.valueOf(lhs-v); }
public LuaValue pow( int rhs ) { return MathLib.dpow(v,rhs); }
public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); } public LuaValue subFrom(int lhs) { return LuaInteger.valueOf(lhs-(long) v); }
public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); }
public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); } public LuaValue mul(LuaValue rhs) { return rhs.mul(v); }
public LuaValue div( double rhs ) { return LuaDouble.ddiv(v,rhs); }
public LuaValue div( int rhs ) { return LuaDouble.ddiv(v,rhs); } public LuaValue mul(double lhs) { return LuaDouble.valueOf(lhs*v); }
public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); }
public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); } public LuaValue mul(int lhs) { return LuaInteger.valueOf(lhs*(long) v); }
public LuaValue mod( double rhs ) { return LuaDouble.dmod(v,rhs); }
public LuaValue mod( int rhs ) { return LuaDouble.dmod(v,rhs); } public LuaValue pow(LuaValue rhs) { return rhs.powWith(v); }
public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); }
public LuaValue pow(double rhs) { return MathLib.dpow(v, rhs); }
public LuaValue pow(int rhs) { return MathLib.dpow(v, rhs); }
public LuaValue powWith(double lhs) { return MathLib.dpow(lhs, v); }
public LuaValue powWith(int lhs) { return MathLib.dpow(lhs, v); }
public LuaValue div(LuaValue rhs) { return rhs.divInto(v); }
public LuaValue div(double rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue div(int rhs) { return LuaDouble.ddiv(v, rhs); }
public LuaValue divInto(double lhs) { return LuaDouble.ddiv(lhs, v); }
public LuaValue mod(LuaValue rhs) { return rhs.modFrom(v); }
public LuaValue mod(double rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue mod(int rhs) { return LuaDouble.dmod(v, rhs); }
public LuaValue modFrom(double lhs) { return LuaDouble.dmod(lhs, v); }
// relational operators // relational operators
public LuaValue lt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gt_b(v)? TRUE: FALSE) : super.lt(rhs); } public LuaValue lt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.gt_b(v)? TRUE: FALSE): super.lt(rhs); }
public LuaValue lt( double rhs ) { return v < rhs? TRUE: FALSE; }
public LuaValue lt( int rhs ) { return v < rhs? TRUE: FALSE; } public LuaValue lt(double rhs) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gt_b(v) : super.lt_b(rhs); }
public boolean lt_b( int rhs ) { return v < rhs; } public LuaValue lt(int rhs) { return v < rhs? TRUE: FALSE; }
public boolean lt_b( double rhs ) { return v < rhs; }
public LuaValue lteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.gteq_b(v)? TRUE: FALSE) : super.lteq(rhs); } public boolean lt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v): super.lt_b(rhs); }
public LuaValue lteq( double rhs ) { return v <= rhs? TRUE: FALSE; }
public LuaValue lteq( int rhs ) { return v <= rhs? TRUE: FALSE; } public boolean lt_b(int rhs) { return v < rhs; }
public boolean lteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.gteq_b(v) : super.lteq_b(rhs); }
public boolean lteq_b( int rhs ) { return v <= rhs; } public boolean lt_b(double rhs) { return v < rhs; }
public boolean lteq_b( double rhs ) { return v <= rhs; }
public LuaValue gt( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lt_b(v)? TRUE: FALSE) : super.gt(rhs); } public LuaValue lteq(LuaValue rhs) {
public LuaValue gt( double rhs ) { return v > rhs? TRUE: FALSE; } return rhs instanceof LuaNumber? (rhs.gteq_b(v)? TRUE: FALSE): super.lteq(rhs);
public LuaValue gt( int rhs ) { return v > rhs? TRUE: FALSE; } }
public boolean gt_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lt_b(v) : super.gt_b(rhs); }
public boolean gt_b( int rhs ) { return v > rhs; } public LuaValue lteq(double rhs) { return v <= rhs? TRUE: FALSE; }
public boolean gt_b( double rhs ) { return v > rhs; }
public LuaValue gteq( LuaValue rhs ) { return rhs instanceof LuaNumber ? (rhs.lteq_b(v)? TRUE: FALSE) : super.gteq(rhs); } public LuaValue lteq(int rhs) { return v <= rhs? TRUE: FALSE; }
public LuaValue gteq( double rhs ) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq( int rhs ) { return v >= rhs? TRUE: FALSE; } public boolean lteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gteq_b(v): super.lteq_b(rhs); }
public boolean gteq_b( LuaValue rhs ) { return rhs instanceof LuaNumber ? rhs.lteq_b(v) : super.gteq_b(rhs); }
public boolean gteq_b( int rhs ) { return v >= rhs; } public boolean lteq_b(int rhs) { return v <= rhs; }
public boolean gteq_b( double rhs ) { return v >= rhs; }
public boolean lteq_b(double rhs) { return v <= rhs; }
public LuaValue gt(LuaValue rhs) { return rhs instanceof LuaNumber? (rhs.lt_b(v)? TRUE: FALSE): super.gt(rhs); }
public LuaValue gt(double rhs) { return v > rhs? TRUE: FALSE; }
public LuaValue gt(int rhs) { return v > rhs? TRUE: FALSE; }
public boolean gt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v): super.gt_b(rhs); }
public boolean gt_b(int rhs) { return v > rhs; }
public boolean gt_b(double rhs) { return v > rhs; }
public LuaValue gteq(LuaValue rhs) {
return rhs instanceof LuaNumber? (rhs.lteq_b(v)? TRUE: FALSE): super.gteq(rhs);
}
public LuaValue gteq(double rhs) { return v >= rhs? TRUE: FALSE; }
public LuaValue gteq(int rhs) { return v >= rhs? TRUE: FALSE; }
public boolean gteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lteq_b(v): super.gteq_b(rhs); }
public boolean gteq_b(int rhs) { return v >= rhs; }
public boolean gteq_b(double rhs) { return v >= rhs; }
// string comparison // string comparison
public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; } public int strcmp(LuaString rhs) { typerror("attempt to compare number with string"); return 0; }
public int checkint() { public int checkint() {
return v; return v;
} }
public long checklong() { public long checklong() {
return v; return v;
} }
public double checkdouble() { public double checkdouble() {
return v; return v;
} }
public String checkjstring() { public String checkjstring() {
return String.valueOf(v); return String.valueOf(v);
} }
public LuaString checkstring() { public LuaString checkstring() {
return valueOf( String.valueOf(v) ); return valueOf(String.valueOf(v));
} }
} }

View File

@@ -22,27 +22,27 @@
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Class to encapsulate behavior of the singleton instance {@code nil} * Class to encapsulate behavior of the singleton instance {@code nil}
* <p> * <p>
* There will be one instance of this class, {@link LuaValue#NIL}, * There will be one instance of this class, {@link LuaValue#NIL}, per Java
* per Java virtual machine. * virtual machine. However, the {@link Varargs} instance {@link LuaValue#NONE}
* However, the {@link Varargs} instance {@link LuaValue#NONE} * which is the empty list, is also considered treated as a nil value by
* which is the empty list, * default.
* is also considered treated as a nil value by default.
* <p> * <p>
* Although it is possible to test for nil using Java == operator, * Although it is possible to test for nil using Java == operator, the
* the recommended approach is to use the method {@link LuaValue#isnil()} * recommended approach is to use the method {@link LuaValue#isnil()} instead.
* instead. By using that any ambiguities between * By using that any ambiguities between {@link LuaValue#NIL} and
* {@link LuaValue#NIL} and {@link LuaValue#NONE} are avoided. * {@link LuaValue#NONE} are avoided.
*
* @see LuaValue * @see LuaValue
* @see LuaValue#NIL * @see LuaValue#NIL
*/ */
public class LuaNil extends LuaValue { public class LuaNil extends LuaValue {
static final LuaNil _NIL = new LuaNil(); static final LuaNil _NIL = new LuaNil();
public static LuaValue s_metatable; public static LuaValue s_metatable;
LuaNil() {} LuaNil() {}
public int type() { public int type() {
@@ -50,59 +50,73 @@ public class LuaNil extends LuaValue {
} }
public String toString() { public String toString() {
return "nil"; return "nil";
} }
public String typename() { public String typename() {
return "nil"; return "nil";
} }
public String tojstring() { public String tojstring() {
return "nil"; return "nil";
} }
public LuaValue not() { public LuaValue not() {
return LuaValue.TRUE; return LuaValue.TRUE;
} }
public boolean toboolean() { public boolean toboolean() {
return false; return false;
} }
public boolean isnil() { public boolean isnil() {
return true; return true;
} }
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof LuaNil; return o instanceof LuaNil;
} }
public LuaValue checknotnil() { public LuaValue checknotnil() {
return argerror("value"); return argerror("value");
} }
public boolean isvalidkey() { public boolean isvalidkey() {
return false; return false;
} }
// optional argument conversions - nil alwas falls badk to default value // optional argument conversions - nil alwas falls badk to default value
public boolean optboolean(boolean defval) { return defval; } public boolean optboolean(boolean defval) { return defval; }
public LuaClosure optclosure(LuaClosure defval) { return defval; }
public double optdouble(double defval) { return defval; } public LuaClosure optclosure(LuaClosure defval) { return defval; }
public LuaFunction optfunction(LuaFunction defval) { return defval; }
public int optint(int defval) { return defval; } public double optdouble(double defval) { return defval; }
public LuaInteger optinteger(LuaInteger defval) { return defval; }
public long optlong(long defval) { return defval; } public LuaFunction optfunction(LuaFunction defval) { return defval; }
public LuaNumber optnumber(LuaNumber defval) { return defval; }
public LuaTable opttable(LuaTable defval) { return defval; } public int optint(int defval) { return defval; }
public LuaThread optthread(LuaThread defval) { return defval; }
public String optjstring(String defval) { return defval; } public LuaInteger optinteger(LuaInteger defval) { return defval; }
public LuaString optstring(LuaString defval) { return defval; }
public Object optuserdata(Object defval) { return defval; } public long optlong(long defval) { return defval; }
public Object optuserdata(Class c, Object defval) { return defval; }
public LuaValue optvalue(LuaValue defval) { return defval; } public LuaNumber optnumber(LuaNumber defval) { return defval; }
public LuaTable opttable(LuaTable defval) { return defval; }
public LuaThread optthread(LuaThread defval) { return defval; }
public String optjstring(String defval) { return defval; }
public LuaString optstring(LuaString defval) { return defval; }
public Object optuserdata(Object defval) { return defval; }
public Object optuserdata(Class c, Object defval) { return defval; }
public LuaValue optvalue(LuaValue defval) { return defval; }
} }

View File

@@ -21,61 +21,64 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Base class for representing numbers as lua values directly. * Base class for representing numbers as lua values directly.
* <p> * <p>
* The main subclasses are {@link LuaInteger} which holds values that fit in a java int, * The main subclasses are {@link LuaInteger} which holds values that fit in a
* and {@link LuaDouble} which holds all other number values. * java int, and {@link LuaDouble} which holds all other number values.
*
* @see LuaInteger * @see LuaInteger
* @see LuaDouble * @see LuaDouble
* @see LuaValue * @see LuaValue
* *
*/ */
abstract abstract public class LuaNumber extends LuaValue {
public class LuaNumber extends LuaValue {
/** Shared static metatable for all number values represented in lua. */ /** Shared static metatable for all number values represented in lua. */
public static LuaValue s_metatable; public static LuaValue s_metatable;
public int type() { public int type() {
return TNUMBER; return TNUMBER;
} }
public String typename() { public String typename() {
return "number"; return "number";
} }
public LuaNumber checknumber() { public LuaNumber checknumber() {
return this; return this;
} }
public LuaNumber checknumber(String errmsg) { public LuaNumber checknumber(String errmsg) {
return this; return this;
} }
public LuaNumber optnumber(LuaNumber defval) { public LuaNumber optnumber(LuaNumber defval) {
return this; return this;
} }
public LuaValue tonumber() { public LuaValue tonumber() {
return this; return this;
} }
public boolean isnumber() { public boolean isnumber() {
return true; return true;
} }
public boolean isstring() { public boolean isstring() {
return true; return true;
} }
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); } public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); } public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); }
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -21,45 +21,43 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
/** /**
* Subclass of {@link LuaValue} that implements * Subclass of {@link LuaValue} that implements a lua coroutine thread using
* a lua coroutine thread using Java Threads. * Java Threads.
* <p> * <p>
* A LuaThread is typically created in response to a scripted call to * A LuaThread is typically created in response to a scripted call to
* {@code coroutine.create()} * {@code coroutine.create()}
* <p> * <p>
* The threads must be initialized with the globals, so that * The threads must be initialized with the globals, so that the global
* the global environment may be passed along according to rules of lua. * environment may be passed along according to rules of lua. This is done via
* This is done via the constructor arguments {@link #LuaThread(Globals)} or * the constructor arguments {@link #LuaThread(Globals)} or
* {@link #LuaThread(Globals, LuaValue)}. * {@link #LuaThread(Globals, LuaValue)}.
* <p>
* The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform}
* see to it that this {@link Globals} are initialized properly.
* <p> * <p>
* The behavior of coroutine threads matches closely the behavior * The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and
* of C coroutine library. However, because of the use of Java threads * {@link org.luaj.vm2.lib.jme.JmePlatform} see to it that this {@link Globals}
* to manage call state, it is possible to yield from anywhere in luaj. * are initialized properly.
* <p> * <p>
* Each Java thread wakes up at regular intervals and checks a weak reference * The behavior of coroutine threads matches closely the behavior of C coroutine
* to determine if it can ever be resumed. If not, it throws * library. However, because of the use of Java threads to manage call state, it
* {@link OrphanedThread} which is an {@link java.lang.Error}. * is possible to yield from anywhere in luaj.
* Applications should not catch {@link OrphanedThread}, because it can break * <p>
* the thread safety of luaj. The value controlling the polling interval * Each Java thread wakes up at regular intervals and checks a weak reference to
* is {@link #thread_orphan_check_interval} and may be set by the user. * determine if it can ever be resumed. If not, it throws {@link OrphanedThread}
* <p> * which is an {@link java.lang.Error}. Applications should not catch
* There are two main ways to abandon a coroutine. The first is to call * {@link OrphanedThread}, because it can break the thread safety of luaj. The
* {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)}, * value controlling the polling interval is
* and arrange to have it never resumed possibly by values passed to yield. * {@link #thread_orphan_check_interval} and may be set by the user.
* The second is to throw {@link OrphanedThread}, which should put the thread * <p>
* in a dead state. In either case all references to the thread must be * There are two main ways to abandon a coroutine. The first is to call
* dropped, and the garbage collector must run for the thread to be * {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)}, and
* garbage collected. * arrange to have it never resumed possibly by values passed to yield. The
* second is to throw {@link OrphanedThread}, which should put the thread in a
* dead state. In either case all references to the thread must be dropped, and
* the garbage collector must run for the thread to be garbage collected.
* *
* *
* @see LuaValue * @see LuaValue
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
@@ -70,107 +68,102 @@ public class LuaThread extends LuaValue {
/** Shared metatable for lua threads. */ /** Shared metatable for lua threads. */
public static LuaValue s_metatable; public static LuaValue s_metatable;
/** The current number of coroutines. Should not be set. */ /** The current number of coroutines. Should not be set. */
public static int coroutine_count = 0; public static int coroutine_count = 0;
/** Polling interval, in milliseconds, which each thread uses while waiting to /**
* return from a yielded state to check if the lua threads is no longer * Polling interval, in milliseconds, which each thread uses while waiting
* referenced and therefore should be garbage collected. * to return from a yielded state to check if the lua threads is no longer
* A short polling interval for many threads will consume server resources. * referenced and therefore should be garbage collected. A short polling
* Orphaned threads cannot be detected and collected unless garbage * interval for many threads will consume server resources. Orphaned threads
* collection is run. This can be changed by Java startup code if desired. * cannot be detected and collected unless garbage collection is run. This
* can be changed by Java startup code if desired.
*/ */
public static long thread_orphan_check_interval = 5000; public static long thread_orphan_check_interval = 5000;
public static final int STATUS_INITIAL = 0; public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1; public static final int STATUS_SUSPENDED = 1;
public static final int STATUS_RUNNING = 2; public static final int STATUS_RUNNING = 2;
public static final int STATUS_NORMAL = 3; public static final int STATUS_NORMAL = 3;
public static final int STATUS_DEAD = 4; public static final int STATUS_DEAD = 4;
public static final String[] STATUS_NAMES = { public static final String[] STATUS_NAMES = { "suspended", "suspended", "running", "normal", "dead", };
"suspended",
"suspended",
"running",
"normal",
"dead",};
public final State state; public final State state;
public static final int MAX_CALLSTACK = 256; public static final int MAX_CALLSTACK = 256;
/** Thread-local used by DebugLib to store debugging state. /**
* This is an opaque value that should not be modified by applications. */ * Thread-local used by DebugLib to store debugging state. This is an opaque
* value that should not be modified by applications.
*/
public Object callstack; public Object callstack;
public final Globals globals; public final Globals globals;
/** Error message handler for this thread, if any. */ /** Error message handler for this thread, if any. */
public LuaValue errorfunc; public LuaValue errorfunc;
/** Private constructor for main thread only */ /** Private constructor for main thread only */
public LuaThread(Globals globals) { public LuaThread(Globals globals) {
state = new State(globals, this, null); state = new State(globals, this, null);
state.status = STATUS_RUNNING; state.status = STATUS_RUNNING;
this.globals = globals; this.globals = globals;
} }
/** /**
* Create a LuaThread around a function and environment * Create a LuaThread around a function and environment
*
* @param func The function to execute * @param func The function to execute
*/ */
public LuaThread(Globals globals, LuaValue func) { public LuaThread(Globals globals, LuaValue func) {
LuaValue.assert_(func != null, "function cannot be null"); LuaValue.assert_(func != null, "function cannot be null");
state = new State(globals, this, func); state = new State(globals, this, func);
this.globals = globals; this.globals = globals;
} }
public int type() { public int type() {
return LuaValue.TTHREAD; return LuaValue.TTHREAD;
} }
public String typename() { public String typename() {
return "thread"; return "thread";
} }
public boolean isthread() { public boolean isthread() {
return true; return true;
} }
public LuaThread optthread(LuaThread defval) { public LuaThread optthread(LuaThread defval) {
return this; return this;
} }
public LuaThread checkthread() { public LuaThread checkthread() {
return this; return this;
} }
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
}
public String getStatus() {
return STATUS_NAMES[state.status];
} }
public boolean isMainThread() { public String getStatus() { return STATUS_NAMES[state.status]; }
return this.state.function == null;
} public boolean isMainThread() { return this.state.function == null; }
public Varargs resume(Varargs args) { public Varargs resume(Varargs args) {
final LuaThread.State s = this.state; final LuaThread.State s = this.state;
if (s.status > LuaThread.STATUS_SUSPENDED) if (s.status > LuaThread.STATUS_SUSPENDED)
return LuaValue.varargsOf(LuaValue.FALSE, return LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(
LuaValue.valueOf("cannot resume "+(s.status==LuaThread.STATUS_DEAD? "dead": "non-suspended")+" coroutine")); "cannot resume " + (s.status == LuaThread.STATUS_DEAD? "dead": "non-suspended") + " coroutine"));
return s.lua_resume(this, args); return s.lua_resume(this, args);
} }
public static class State implements Runnable { public static class State implements Runnable {
private final Globals globals; private final Globals globals;
final WeakReference lua_thread; final WeakReference lua_thread;
public final LuaValue function; public final LuaValue function;
Varargs args = LuaValue.NONE; Varargs args = LuaValue.NONE;
Varargs result = LuaValue.NONE; Varargs result = LuaValue.NONE;
String error = null; String error = null;
/** Hook function control state used by debug lib. */ /** Hook function control state used by debug lib. */
public LuaValue hookfunc; public LuaValue hookfunc;
@@ -178,11 +171,11 @@ public class LuaThread extends LuaValue {
public boolean hookline; public boolean hookline;
public boolean hookcall; public boolean hookcall;
public boolean hookrtrn; public boolean hookrtrn;
public int hookcount; public int hookcount;
public boolean inhook; public boolean inhook;
public int lastline; public int lastline;
public int bytecodes; public int bytecodes;
public int status = LuaThread.STATUS_INITIAL; public int status = LuaThread.STATUS_INITIAL;
State(Globals globals, LuaThread lua_thread, LuaValue function) { State(Globals globals, LuaThread lua_thread, LuaValue function) {
@@ -190,7 +183,7 @@ public class LuaThread extends LuaValue {
this.lua_thread = new WeakReference(lua_thread); this.lua_thread = new WeakReference(lua_thread);
this.function = function; this.function = function;
} }
public synchronized void run() { public synchronized void run() {
try { try {
Varargs a = this.args; Varargs a = this.args;
@@ -210,8 +203,8 @@ public class LuaThread extends LuaValue {
globals.running = new_thread; globals.running = new_thread;
this.args = args; this.args = args;
if (this.status == STATUS_INITIAL) { if (this.status == STATUS_INITIAL) {
this.status = STATUS_RUNNING; this.status = STATUS_RUNNING;
new Thread(this, "Coroutine-"+(++coroutine_count)).start(); new Thread(this, "Coroutine-" + (++coroutine_count)).start();
} else { } else {
this.notify(); this.notify();
} }
@@ -219,9 +212,8 @@ public class LuaThread extends LuaValue {
previous_thread.state.status = STATUS_NORMAL; previous_thread.state.status = STATUS_NORMAL;
this.status = STATUS_RUNNING; this.status = STATUS_RUNNING;
this.wait(); this.wait();
return (this.error != null? return (this.error != null? LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error))
LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)): : LuaValue.varargsOf(LuaValue.TRUE, this.result));
LuaValue.varargsOf(LuaValue.TRUE, this.result));
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
throw new OrphanedThread(); throw new OrphanedThread();
} finally { } finally {
@@ -230,7 +222,7 @@ public class LuaThread extends LuaValue {
this.error = null; this.error = null;
globals.running = previous_thread; globals.running = previous_thread;
if (previous_thread != null) if (previous_thread != null)
globals.running.state.status =STATUS_RUNNING; globals.running.state.status = STATUS_RUNNING;
} }
} }
@@ -245,7 +237,7 @@ public class LuaThread extends LuaValue {
this.status = STATUS_DEAD; this.status = STATUS_DEAD;
throw new OrphanedThread(); throw new OrphanedThread();
} }
} while (this.status == STATUS_SUSPENDED); } while ( this.status == STATUS_SUSPENDED );
return this.args; return this.args;
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
this.status = STATUS_DEAD; this.status = STATUS_DEAD;
@@ -256,5 +248,5 @@ public class LuaThread extends LuaValue {
} }
} }
} }
} }

View File

@@ -21,29 +21,28 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
public class LuaUserdata extends LuaValue { public class LuaUserdata extends LuaValue {
public Object m_instance; public Object m_instance;
public LuaValue m_metatable; public LuaValue m_metatable;
public LuaUserdata(Object obj) { public LuaUserdata(Object obj) {
m_instance = obj; m_instance = obj;
} }
public LuaUserdata(Object obj, LuaValue metatable) { public LuaUserdata(Object obj, LuaValue metatable) {
m_instance = obj; m_instance = obj;
m_metatable = metatable; m_metatable = metatable;
} }
public String tojstring() { public String tojstring() {
return String.valueOf(m_instance); return String.valueOf(m_instance);
} }
public int type() { public int type() {
return LuaValue.TUSERDATA; return LuaValue.TUSERDATA;
} }
public String typename() { public String typename() {
return "userdata"; return "userdata";
} }
@@ -51,22 +50,27 @@ public class LuaUserdata extends LuaValue {
public int hashCode() { public int hashCode() {
return m_instance.hashCode(); return m_instance.hashCode();
} }
public Object userdata() { public Object userdata() {
return m_instance; return m_instance;
} }
public boolean isuserdata() { return true; } public boolean isuserdata() { return true; }
public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
public Object touserdata() { return m_instance; } public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
public Object optuserdata(Object defval) { return m_instance; } public Object touserdata() { return m_instance; }
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
public Object optuserdata(Object defval) { return m_instance; }
public Object optuserdata(Class c, Object defval) { public Object optuserdata(Class c, Object defval) {
if (!c.isAssignableFrom(m_instance.getClass())) if (!c.isAssignableFrom(m_instance.getClass()))
typerror(c.getName()); typerror(c.getName());
return m_instance; return m_instance;
} }
public LuaValue getmetatable() { public LuaValue getmetatable() {
return m_metatable; return m_metatable;
} }
@@ -79,48 +83,53 @@ public class LuaUserdata extends LuaValue {
public Object checkuserdata() { public Object checkuserdata() {
return m_instance; return m_instance;
} }
public Object checkuserdata(Class c) { public Object checkuserdata(Class c) {
if ( c.isAssignableFrom(m_instance.getClass()) ) if (c.isAssignableFrom(m_instance.getClass()))
return m_instance; return m_instance;
return typerror(c.getName()); return typerror(c.getName());
} }
public LuaValue get( LuaValue key ) { public LuaValue get(LuaValue key) {
return m_metatable!=null? gettable(this,key): NIL; return m_metatable != null? gettable(this, key): NIL;
}
public void set( LuaValue key, LuaValue value ) {
if ( m_metatable==null || ! settable(this,key,value) )
error( "cannot set "+key+" for userdata" );
} }
public boolean equals( Object val ) { public void set(LuaValue key, LuaValue value) {
if ( this == val ) if (m_metatable == null || !settable(this, key, value))
error("cannot set " + key + " for userdata");
}
public boolean equals(Object val) {
if (this == val)
return true; return true;
if ( ! (val instanceof LuaUserdata) ) if (!(val instanceof LuaUserdata))
return false; return false;
LuaUserdata u = (LuaUserdata) val; LuaUserdata u = (LuaUserdata) val;
return m_instance.equals(u.m_instance); return m_instance.equals(u.m_instance);
} }
// equality w/ metatable processing // equality w/ metatable processing
public LuaValue eq( LuaValue val ) { return eq_b(val)? TRUE: FALSE; } public LuaValue eq(LuaValue val) { return eq_b(val)? TRUE: FALSE; }
public boolean eq_b( LuaValue val ) {
if ( val.raweq(this) ) return true; public boolean eq_b(LuaValue val) {
if ( m_metatable == null || !val.isuserdata() ) return false; if (val.raweq(this))
return true;
if (m_metatable == null || !val.isuserdata())
return false;
LuaValue valmt = val.getmetatable(); LuaValue valmt = val.getmetatable();
return valmt!=null && LuaValue.eqmtcall(this, m_metatable, val, valmt); return valmt != null && LuaValue.eqmtcall(this, m_metatable, val, valmt);
} }
// equality w/o metatable processing // equality w/o metatable processing
public boolean raweq( LuaValue val ) { return val.raweq(this); } public boolean raweq(LuaValue val) { return val.raweq(this); }
public boolean raweq( LuaUserdata val ) {
return this == val || (m_metatable == val.m_metatable && m_instance.equals(val.m_instance)); public boolean raweq(LuaUserdata val) {
return this == val || (m_metatable == val.m_metatable && m_instance.equals(val.m_instance));
} }
// __eq metatag processing // __eq metatag processing
public boolean eqmt( LuaValue val ) { public boolean eqmt(LuaValue val) {
return m_metatable!=null && val.isuserdata()? LuaValue.eqmtcall(this, m_metatable, val, val.getmetatable()): false; return m_metatable != null && val.isuserdata()? LuaValue.eqmtcall(this, m_metatable, val, val.getmetatable())
: false;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -38,14 +38,14 @@ interface Metatable {
public LuaValue toLuaValue(); public LuaValue toLuaValue();
/** Return an instance of Slot appropriate for the given key and value. */ /** Return an instance of Slot appropriate for the given key and value. */
public Slot entry( LuaValue key, LuaValue value ); public Slot entry(LuaValue key, LuaValue value);
/** Returns the given value wrapped in a weak reference if appropriate. */ /** Returns the given value wrapped in a weak reference if appropriate. */
public LuaValue wrap( LuaValue value ); public LuaValue wrap(LuaValue value);
/** /**
* Returns the value at the given index in the array, or null if it is a weak reference that * Returns the value at the given index in the array, or null if it is a
* has been dropped. * weak reference that has been dropped.
*/ */
public LuaValue arrayget(LuaValue[] array, int index); public LuaValue arrayget(LuaValue[] array, int index);
} }

View File

@@ -29,7 +29,8 @@ package org.luaj.vm2;
* {@link LuaThread} being used as a coroutine that could not possibly be * {@link LuaThread} being used as a coroutine that could not possibly be
* resumed again because there are no more references to the LuaThread with * resumed again because there are no more references to the LuaThread with
* which it is associated. Rather than locking up resources forever, this error * which it is associated. Rather than locking up resources forever, this error
* is thrown, and should fall through all the way to the thread's {@link Thread#run()} method. * is thrown, and should fall through all the way to the thread's
* {@link Thread#run()} method.
* <p> * <p>
* Java code mixed with the luaj vm should not catch this error because it may * Java code mixed with the luaj vm should not catch this error because it may
* occur when the coroutine is not running, so any processing done during error * occur when the coroutine is not running, so any processing done during error

View File

@@ -25,131 +25,96 @@ import java.io.ByteArrayOutputStream;
import java.io.PrintStream; import java.io.PrintStream;
/** /**
* Debug helper class to pretty-print lua bytecodes. * Debug helper class to pretty-print lua bytecodes.
*
* @see Prototype * @see Prototype
* @see LuaClosure * @see LuaClosure
*/ */
public class Print extends Lua { public class Print extends Lua {
/** opcode names */ /** opcode names */
private static final String STRING_FOR_NULL = "null"; private static final String STRING_FOR_NULL = "null";
public static PrintStream ps = System.out; public static PrintStream ps = System.out;
/** String names for each lua opcode value. */ /** String names for each lua opcode value. */
public static final String[] OPNAMES = { public static final String[] OPNAMES = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL", "GETUPVAL", "GETTABUP",
"MOVE", "GETTABLE", "SETTABUP", "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL", "DIV", "MOD",
"LOADK", "POW", "UNM", "NOT", "LEN", "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL",
"LOADKX", "RETURN", "FORLOOP", "FORPREP", "TFORCALL", "TFORLOOP", "SETLIST", "CLOSURE", "VARARG", "EXTRAARG", null, };
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETTABUP",
"GETTABLE",
"SETTABUP",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"SELF",
"ADD",
"SUB",
"MUL",
"DIV",
"MOD",
"POW",
"UNM",
"NOT",
"LEN",
"CONCAT",
"JMP",
"EQ",
"LT",
"LE",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"FORLOOP",
"FORPREP",
"TFORCALL",
"TFORLOOP",
"SETLIST",
"CLOSURE",
"VARARG",
"EXTRAARG",
null,
};
static void printString(PrintStream ps, final LuaString s) { static void printString(PrintStream ps, final LuaString s) {
ps.print('"'); ps.print('"');
for (int i = 0, n = s.m_length; i < n; i++) { for (int i = 0, n = s.m_length; i < n; i++) {
int c = s.m_bytes[s.m_offset+i]; int c = s.m_bytes[s.m_offset+i];
if ( c >= ' ' && c <= '~' && c != '\"' && c != '\\' ) if (c >= ' ' && c <= '~' && c != '\"' && c != '\\')
ps.print((char) c); ps.print((char) c);
else { else {
switch (c) { switch (c) {
case '"': case '"':
ps.print("\\\""); ps.print("\\\"");
break; break;
case '\\': case '\\':
ps.print("\\\\"); ps.print("\\\\");
break; break;
case 0x0007: /* bell */ case 0x0007: /* bell */
ps.print("\\a"); ps.print("\\a");
break; break;
case '\b': /* backspace */ case '\b': /* backspace */
ps.print("\\b"); ps.print("\\b");
break; break;
case '\f': /* form feed */ case '\f': /* form feed */
ps.print("\\f"); ps.print("\\f");
break; break;
case '\t': /* tab */ case '\t': /* tab */
ps.print("\\t"); ps.print("\\t");
break; break;
case '\r': /* carriage return */ case '\r': /* carriage return */
ps.print("\\r"); ps.print("\\r");
break; break;
case '\n': /* newline */ case '\n': /* newline */
ps.print("\\n"); ps.print("\\n");
break; break;
case 0x000B: /* vertical tab */ case 0x000B: /* vertical tab */
ps.print("\\v"); ps.print("\\v");
break; break;
default: default:
ps.print('\\'); ps.print('\\');
ps.print(Integer.toString(1000 + 0xff&c).substring(1)); ps.print(Integer.toString(1000+0xff & c).substring(1));
break; break;
} }
} }
} }
ps.print('"'); ps.print('"');
} }
static void printValue( PrintStream ps, LuaValue v ) { static void printValue(PrintStream ps, LuaValue v) {
if (v == null) { if (v == null) {
ps.print("null"); ps.print("null");
return; return;
} }
switch ( v.type() ) { switch (v.type()) {
case LuaValue.TSTRING: printString( ps, (LuaString) v ); break; case LuaValue.TSTRING:
default: ps.print( v.tojstring() ); printString(ps, (LuaString) v);
break;
default:
ps.print(v.tojstring());
} }
} }
static void printConstant(PrintStream ps, Prototype f, int i) { static void printConstant(PrintStream ps, Prototype f, int i) {
printValue( ps, i < f.k.length ? f.k[i] : LuaValue.valueOf("UNKNOWN_CONST_" + i) ); printValue(ps, i < f.k.length? f.k[i]: LuaValue.valueOf("UNKNOWN_CONST_" + i));
} }
static void printUpvalue(PrintStream ps, Upvaldesc u) { static void printUpvalue(PrintStream ps, Upvaldesc u) {
ps.print( u.idx + " " ); ps.print(u.idx + " ");
printValue( ps, u.name ); printValue(ps, u.name);
} }
/** /**
* Print the code in a prototype * Print the code in a prototype
*
* @param f the {@link Prototype} * @param f the {@link Prototype}
*/ */
public static void printCode(Prototype f) { public static void printCode(Prototype f) {
@@ -161,20 +126,22 @@ public class Print extends Lua {
} }
} }
/** /**
* Print an opcode in a prototype * Print an opcode in a prototype
* @param f the {@link Prototype} *
* @param f the {@link Prototype}
* @param pc the program counter to look up and print * @param pc the program counter to look up and print
* @return pc same as above or changed * @return pc same as above or changed
*/ */
public static int printOpCode(Prototype f, int pc) { public static int printOpCode(Prototype f, int pc) {
return printOpCode(ps,f,pc); return printOpCode(ps, f, pc);
} }
/** /**
* Print an opcode in a prototype * Print an opcode in a prototype
*
* @param ps the {@link PrintStream} to print to * @param ps the {@link PrintStream} to print to
* @param f the {@link Prototype} * @param f the {@link Prototype}
* @param pc the program counter to look up and print * @param pc the program counter to look up and print
* @return pc same as above or changed * @return pc same as above or changed
*/ */
@@ -188,33 +155,33 @@ public class Print extends Lua {
int bx = GETARG_Bx(i); int bx = GETARG_Bx(i);
int sbx = GETARG_sBx(i); int sbx = GETARG_sBx(i);
int line = getline(f, pc); int line = getline(f, pc);
ps.print(" " + (pc + 1) + " "); ps.print(" " + (pc+1) + " ");
if (line > 0) if (line > 0)
ps.print("[" + line + "] "); ps.print("[" + line + "] ");
else else
ps.print("[-] "); ps.print("[-] ");
if (o >= OPNAMES.length - 1) { if (o >= OPNAMES.length-1) {
ps.print("UNKNOWN_OP_" + o + " "); ps.print("UNKNOWN_OP_" + o + " ");
} else { } else {
ps.print(OPNAMES[o] + " "); ps.print(OPNAMES[o] + " ");
switch (getOpMode(o)) { switch (getOpMode(o)) {
case iABC: case iABC:
ps.print( a ); ps.print(a);
if (getBMode(o) != OpArgN) if (getBMode(o) != OpArgN)
ps.print(" "+(ISK(b) ? (-1 - INDEXK(b)) : b)); ps.print(" " + (ISK(b)? (-1-INDEXK(b)): b));
if (getCMode(o) != OpArgN) if (getCMode(o) != OpArgN)
ps.print(" "+(ISK(c) ? (-1 - INDEXK(c)) : c)); ps.print(" " + (ISK(c)? (-1-INDEXK(c)): c));
break; break;
case iABx: case iABx:
if (getBMode(o) == OpArgK) { if (getBMode(o) == OpArgK) {
ps.print(a + " " + (-1 - bx)); ps.print(a + " " + (-1-bx));
} else { } else {
ps.print(a + " " + (bx)); ps.print(a + " " + (bx));
} }
break; break;
case iAsBx: case iAsBx:
if (o == OP_JMP) if (o == OP_JMP)
ps.print( sbx ); ps.print(sbx);
else else
ps.print(a + " " + sbx); ps.print(a + " " + sbx);
break; break;
@@ -231,7 +198,7 @@ public class Print extends Lua {
printUpvalue(ps, f.upvalues[b]); printUpvalue(ps, f.upvalues[b]);
} else { } else {
ps.print("UNKNOWN_UPVALUE_" + b); ps.print("UNKNOWN_UPVALUE_" + b);
} }
break; break;
case OP_GETTABUP: case OP_GETTABUP:
ps.print(" ; "); ps.print(" ; ");
@@ -296,7 +263,7 @@ public class Print extends Lua {
case OP_JMP: case OP_JMP:
case OP_FORLOOP: case OP_FORLOOP:
case OP_FORPREP: case OP_FORPREP:
ps.print(" ; to " + (sbx + pc + 2)); ps.print(" ; to " + (sbx+pc+2));
break; break;
case OP_CLOSURE: case OP_CLOSURE:
if (bx < f.p.length) { if (bx < f.p.length) {
@@ -312,8 +279,8 @@ public class Print extends Lua {
ps.print(" ; " + ((int) c)); ps.print(" ; " + ((int) c));
break; break;
case OP_VARARG: case OP_VARARG:
ps.print( " ; is_vararg="+ f.is_vararg ); ps.print(" ; is_vararg=" + f.is_vararg);
break; break;
default: default:
break; break;
} }
@@ -322,7 +289,7 @@ public class Print extends Lua {
} }
private static int getline(Prototype f, int pc) { private static int getline(Prototype f, int pc) {
return pc>0 && f.lineinfo!=null && pc<f.lineinfo.length? f.lineinfo[pc]: -1; return pc > 0 && f.lineinfo != null && pc < f.lineinfo.length? f.lineinfo[pc]: -1;
} }
static void printHeader(Prototype f) { static void printHeader(Prototype f) {
@@ -333,23 +300,20 @@ public class Print extends Lua {
s = "(bstring)"; s = "(bstring)";
else else
s = "(string)"; s = "(string)";
String a = (f.linedefined == 0) ? "main" : "function"; String a = (f.linedefined == 0)? "main": "function";
ps.print("\n%" + a + " <" + s + ":" + f.linedefined + "," ps.print("\n%" + a + " <" + s + ":" + f.linedefined + "," + f.lastlinedefined + "> (" + f.code.length
+ f.lastlinedefined + "> (" + f.code.length + " instructions, " + " instructions, " + f.code.length*4 + " bytes at " + id(f) + ")\n");
+ f.code.length * 4 + " bytes at " + id(f) + ")\n"); ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " + f.upvalues.length + " upvalue, ");
ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " ps.print(f.locvars.length + " local, " + f.k.length + " constant, " + f.p.length + " function\n");
+ f.upvalues.length + " upvalue, ");
ps.print(f.locvars.length + " local, " + f.k.length
+ " constant, " + f.p.length + " function\n");
} }
static void printConstants(Prototype f) { static void printConstants(Prototype f) {
int i, n = f.k.length; int i, n = f.k.length;
ps.print("constants (" + n + ") for " + id(f) + ":\n"); ps.print("constants (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
ps.print(" " + (i + 1) + " "); ps.print(" " + (i+1) + " ");
printValue( ps, f.k[i] ); printValue(ps, f.k[i]);
ps.print( "\n"); ps.print("\n");
} }
} }
@@ -357,7 +321,8 @@ public class Print extends Lua {
int i, n = f.locvars.length; int i, n = f.locvars.length;
ps.print("locals (" + n + ") for " + id(f) + ":\n"); ps.print("locals (" + n + ") for " + id(f) + ":\n");
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
ps.println(" "+i+" "+f.locvars[i].varname+" "+(f.locvars[i].startpc+1)+" "+(f.locvars[i].endpc+1)); ps.println(
" " + i + " " + f.locvars[i].varname + " " + (f.locvars[i].startpc+1) + " " + (f.locvars[i].endpc+1));
} }
} }
@@ -369,18 +334,20 @@ public class Print extends Lua {
} }
} }
/** Pretty-prints contents of a Prototype. /**
* Pretty-prints contents of a Prototype.
* *
* @param prototype Prototype to print. * @param prototype Prototype to print.
*/ */
public static void print(Prototype prototype) { public static void print(Prototype prototype) {
printFunction(prototype, true); printFunction(prototype, true);
} }
/** Pretty-prints contents of a Prototype in short or long form. /**
* Pretty-prints contents of a Prototype in short or long form.
* *
* @param prototype Prototype to print. * @param prototype Prototype to print.
* @param full true to print all fields, false to print short form. * @param full true to print all fields, false to print short form.
*/ */
public static void printFunction(Prototype prototype, boolean full) { public static void printFunction(Prototype prototype, boolean full) {
int i, n = prototype.p.length; int i, n = prototype.p.length;
@@ -395,43 +362,45 @@ public class Print extends Lua {
printFunction(prototype.p[i], full); printFunction(prototype.p[i], full);
} }
private static void format( String s, int maxcols ) { private static void format(String s, int maxcols) {
int n = s.length(); int n = s.length();
if ( n > maxcols ) if (n > maxcols)
ps.print( s.substring(0,maxcols) ); ps.print(s.substring(0, maxcols));
else { else {
ps.print( s ); ps.print(s);
for ( int i=maxcols-n; --i>=0; ) for (int i = maxcols-n; --i >= 0;)
ps.print( ' ' ); ps.print(' ');
} }
} }
private static String id(Prototype f) { private static String id(Prototype f) {
return "Proto"; return "Proto";
} }
private void _assert(boolean b) { private void _assert(boolean b) {
if ( !b ) if (!b)
throw new NullPointerException("_assert failed"); throw new NullPointerException("_assert failed");
} }
/** /**
* Print the state of a {@link LuaClosure} that is being executed * Print the state of a {@link LuaClosure} that is being executed
* @param cl the {@link LuaClosure} *
* @param pc the program counter * @param cl the {@link LuaClosure}
* @param stack the stack of {@link LuaValue} * @param pc the program counter
* @param top the top of the stack * @param stack the stack of {@link LuaValue}
* @param top the top of the stack
* @param varargs any {@link Varargs} value that may apply * @param varargs any {@link Varargs} value that may apply
*/ */
public static void printState(LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) { public static void printState(LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) {
// print opcode into buffer // print opcode into buffer
PrintStream previous = ps; PrintStream previous = ps;
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ps = new PrintStream( baos ); ps = new PrintStream(baos);
printOpCode( cl.p, pc ); printOpCode(cl.p, pc);
ps.flush(); ps.flush();
ps.close(); ps.close();
ps = previous; ps = previous;
format( baos.toString(), 50 ); format(baos.toString(), 50);
printStack(stack, top, varargs); printStack(stack, top, varargs);
ps.println(); ps.println();
} }
@@ -439,38 +408,38 @@ public class Print extends Lua {
public static void printStack(LuaValue[] stack, int top, Varargs varargs) { public static void printStack(LuaValue[] stack, int top, Varargs varargs) {
// print stack // print stack
ps.print('['); ps.print('[');
for ( int i=0; i<stack.length; i++ ) { for (int i = 0; i < stack.length; i++) {
LuaValue v = stack[i]; LuaValue v = stack[i];
if ( v == null ) if (v == null)
ps.print(STRING_FOR_NULL); ps.print(STRING_FOR_NULL);
else switch ( v.type() ) { else
case LuaValue.TSTRING: switch (v.type()) {
LuaString s = v.checkstring(); case LuaValue.TSTRING:
ps.print( s.length() < 48? LuaString s = v.checkstring();
s.tojstring(): ps.print(s.length() < 48? s.tojstring()
s.substring(0, 32).tojstring()+"...+"+(s.length()-32)+"b"); : s.substring(0, 32).tojstring() + "...+" + (s.length()-32) + "b");
break; break;
case LuaValue.TFUNCTION: case LuaValue.TFUNCTION:
ps.print( v.tojstring() ); ps.print(v.tojstring());
break; break;
case LuaValue.TUSERDATA: case LuaValue.TUSERDATA:
Object o = v.touserdata(); Object o = v.touserdata();
if ( o != null ) { if (o != null) {
String n = o.getClass().getName(); String n = o.getClass().getName();
n = n.substring(n.lastIndexOf('.')+1); n = n.substring(n.lastIndexOf('.')+1);
ps.print( n+": "+Integer.toHexString(o.hashCode()) ); ps.print(n + ": " + Integer.toHexString(o.hashCode()));
} else { } else {
ps.print( v.toString() ); ps.print(v.toString());
}
break;
default:
ps.print(v.tojstring());
} }
break; if (i+1 == top)
default:
ps.print(v.tojstring());
}
if ( i+1 == top )
ps.print(']'); ps.print(']');
ps.print( " | " ); ps.print(" | ");
} }
ps.print(varargs); ps.print(varargs);
} }
} }

View File

@@ -22,59 +22,91 @@
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Prototype representing compiled lua code. * Prototype representing compiled lua code.
* *
* <p> * <p>
* This is both a straight translation of the corresponding C type, * This is both a straight translation of the corresponding C type, and the main
* and the main data structure for execution of compiled lua bytecode. * data structure for execution of compiled lua bytecode.
* *
* <p> * <p>
* Generally, the {@link Prototype} is not constructed directly is an intermediate result * Generally, the {@link Prototype} is not constructed directly is an
* as lua code is loaded using {@link Globals#load(java.io.Reader, String)}: * intermediate result as lua code is loaded using
* <pre> {@code * {@link Globals#load(java.io.Reader, String)}:
* Globals globals = JsePlatform.standardGlobals(); *
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call(); * <pre>
* } </pre> * {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
* *
* <p> * <p>
* To create a {@link Prototype} directly, a compiler such as * To create a {@link Prototype} directly, a compiler such as
* {@link org.luaj.vm2.compiler.LuaC} may be used: * {@link org.luaj.vm2.compiler.LuaC} may be used:
* <pre> {@code
* InputStream is = new ByteArrayInputStream("print('hello,world')".getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* }</pre>
* *
* To simplify loading, the {@link Globals#compilePrototype(java.io.InputStream, String)} method may be used: * <pre>
* <pre> {@code * {
* Prototype p = globals.compileProtoytpe(is, "script"); * &#64;code
* }</pre> * InputStream is = new ByteArrayInputStream("print('hello,world')".getBytes());
* Prototype p = LuaC.instance.compile(is, "script");
* }
* </pre>
* *
* It may also be loaded from a {@link java.io.Reader} via {@link Globals#compilePrototype(java.io.Reader, String)}: * To simplify loading, the
* <pre> {@code * {@link Globals#compilePrototype(java.io.InputStream, String)} method may be
* Prototype p = globals.compileProtoytpe(new StringReader(script), "script"); * used:
* }</pre>
* *
* To un-dump a binary file known to be a binary lua file that has been dumped to a string, * <pre>
* the {@link Globals.Undumper} interface may be used: * {
* <pre> {@code * &#64;code
* FileInputStream lua_binary_file = new FileInputStream("foo.lc"); // Known to be compiled lua. * Prototype p = globals.compileProtoytpe(is, "script");
* Prototype p = globals.undumper.undump(lua_binary_file, "foo.lua"); * }
* }</pre> * </pre>
* *
* To execute the code represented by the {@link Prototype} it must be supplied to * It may also be loaded from a {@link java.io.Reader} via
* the constructor of a {@link LuaClosure}: * {@link Globals#compilePrototype(java.io.Reader, String)}:
* <pre> {@code
* Globals globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }</pre>
* *
* To simplify the debugging of prototype values, the contents may be printed using {@link Print#print}: * <pre>
* <pre> {@code * {
* &#64;code
* Prototype p = globals.compileProtoytpe(new StringReader(script), "script");
* }
* </pre>
*
* To un-dump a binary file known to be a binary lua file that has been dumped
* to a string, the {@link Globals.Undumper} interface may be used:
*
* <pre>
* {
* &#64;code
* FileInputStream lua_binary_file = new FileInputStream("foo.lc"); // Known to be compiled lua.
* Prototype p = globals.undumper.undump(lua_binary_file, "foo.lua");
* }
* </pre>
*
* To execute the code represented by the {@link Prototype} it must be supplied
* to the constructor of a {@link LuaClosure}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals);
* f.call();
* }
* </pre>
*
* To simplify the debugging of prototype values, the contents may be printed
* using {@link Print#print}:
*
* <pre>
* {@code
* Print.print(p); * Print.print(p);
* }</pre> * }
* </pre>
* <p> * <p>
* *
* @see LuaClosure * @see LuaClosure
* @see Globals * @see Globals
* @see Globals#undumper * @see Globals#undumper
@@ -84,8 +116,8 @@ package org.luaj.vm2;
public class Prototype { public class Prototype {
/* constants used by the function */ /* constants used by the function */
public LuaValue[] k; public LuaValue[] k;
public int[] code; public int[] code;
/* functions defined inside the function */ /* functions defined inside the function */
public Prototype[] p; public Prototype[] p;
/* map from opcodes to source lines */ /* map from opcodes to source lines */
@@ -93,14 +125,14 @@ public class Prototype {
/* information about local variables */ /* information about local variables */
public LocVars[] locvars; public LocVars[] locvars;
/* upvalue information */ /* upvalue information */
public Upvaldesc[] upvalues; public Upvaldesc[] upvalues;
public LuaString source; public LuaString source;
public int linedefined; public int linedefined;
public int lastlinedefined; public int lastlinedefined;
public int numparams; public int numparams;
public int is_vararg; public int is_vararg;
public int maxstacksize; public int maxstacksize;
private static final Upvaldesc[] NOUPVALUES = {}; private static final Upvaldesc[] NOUPVALUES = {};
private static final Prototype[] NOSUBPROTOS = {}; private static final Prototype[] NOSUBPROTOS = {};
public Prototype() { public Prototype() {
@@ -112,35 +144,36 @@ public class Prototype {
p = NOSUBPROTOS; p = NOSUBPROTOS;
upvalues = new Upvaldesc[n_upvalues]; upvalues = new Upvaldesc[n_upvalues];
} }
public String toString() { public String toString() {
return source + ":" + linedefined+"-"+lastlinedefined; return source + ":" + linedefined + "-" + lastlinedefined;
} }
/** Get the name of a local variable. /**
* Get the name of a local variable.
* *
* @param number the local variable number to look up * @param number the local variable number to look up
* @param pc the program counter * @param pc the program counter
* @return the name, or null if not found * @return the name, or null if not found
*/ */
public LuaString getlocalname(int number, int pc) { public LuaString getlocalname(int number, int pc) {
int i; int i;
for (i = 0; i<locvars.length && locvars[i].startpc <= pc; i++) { for (i = 0; i < locvars.length && locvars[i].startpc <= pc; i++) {
if (pc < locvars[i].endpc) { /* is variable active? */ if (pc < locvars[i].endpc) { /* is variable active? */
number--; number--;
if (number == 0) if (number == 0)
return locvars[i].varname; return locvars[i].varname;
} }
} }
return null; /* not found */ return null; /* not found */
} }
public String shortsource() { public String shortsource() {
String name = source.tojstring(); String name = source.tojstring();
if ( name.startsWith("@") || name.startsWith("=") ) if (name.startsWith("@") || name.startsWith("="))
name = name.substring(1); name = name.substring(1);
else if ( name.startsWith("\033") ) else if (name.startsWith("\033"))
name = "binary string"; name = "binary string";
return name; return name;
} }
} }

View File

@@ -22,44 +22,41 @@
package org.luaj.vm2; package org.luaj.vm2;
/** /**
* Subclass of {@link Varargs} that represents a lua tail call * Subclass of {@link Varargs} that represents a lua tail call in a Java library
* in a Java library function execution environment. * function execution environment.
* <p> * <p>
* Since Java doesn't have direct support for tail calls, * Since Java doesn't have direct support for tail calls, any lua function whose
* any lua function whose {@link Prototype} contains the * {@link Prototype} contains the {@link Lua#OP_TAILCALL} bytecode needs a
* {@link Lua#OP_TAILCALL} bytecode needs a mechanism * mechanism for tail calls when converting lua-bytecode to java-bytecode.
* for tail calls when converting lua-bytecode to java-bytecode.
* <p> * <p>
* The tail call holds the next function and arguments, * The tail call holds the next function and arguments, and the client a call to
* and the client a call to {@link #eval()} executes the function * {@link #eval()} executes the function repeatedly until the tail calls are
* repeatedly until the tail calls are completed. * completed.
* <p> * <p>
* Normally, users of luaj need not concern themselves with the * Normally, users of luaj need not concern themselves with the details of this
* details of this mechanism, as it is built into the core * mechanism, as it is built into the core execution framework.
* execution framework. *
* @see Prototype * @see Prototype
* @see org.luaj.vm2.luajc.LuaJC * @see org.luaj.vm2.luajc.LuaJC
*/ */
public class TailcallVarargs extends Varargs { public class TailcallVarargs extends Varargs {
private LuaValue func; private LuaValue func;
private Varargs args; private Varargs args;
private Varargs result; private Varargs result;
public TailcallVarargs(LuaValue f, Varargs args) { public TailcallVarargs(LuaValue f, Varargs args) {
this.func = f; this.func = f;
this.args = args; this.args = args;
} }
public TailcallVarargs(LuaValue object, LuaValue methodname, Varargs args) { public TailcallVarargs(LuaValue object, LuaValue methodname, Varargs args) {
this.func = object.get(methodname); this.func = object.get(methodname);
this.args = LuaValue.varargsOf(object, args); this.args = LuaValue.varargsOf(object, args);
} }
public boolean isTailcall() { public boolean isTailcall() { return true; }
return true;
}
public Varargs eval() { public Varargs eval() {
while ( result == null ) { while ( result == null ) {
Varargs r = func.onInvoke(args); Varargs r = func.onInvoke(args);
@@ -67,28 +64,27 @@ public class TailcallVarargs extends Varargs {
TailcallVarargs t = (TailcallVarargs) r; TailcallVarargs t = (TailcallVarargs) r;
func = t.func; func = t.func;
args = t.args; args = t.args;
} } else {
else { result = r;
result = r;
func = null; func = null;
args = null; args = null;
} }
} }
return result; return result;
} }
public LuaValue arg( int i ) { public LuaValue arg(int i) {
if ( result == null ) if (result == null)
eval(); eval();
return result.arg(i); return result.arg(i);
} }
public LuaValue arg1() { public LuaValue arg1() {
if (result == null) if (result == null)
eval(); eval();
return result.arg1(); return result.arg1();
} }
public int narg() { public int narg() {
if (result == null) if (result == null)
eval(); eval();
@@ -100,4 +96,4 @@ public class TailcallVarargs extends Varargs {
eval(); eval();
return result.subargs(start); return result.subargs(start);
} }
} }

View File

@@ -21,23 +21,25 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
/**
/** Upvalue used with Closure formulation * Upvalue used with Closure formulation
* <p> * <p>
*
* @see LuaClosure * @see LuaClosure
* @see Prototype * @see Prototype
*/ */
public final class UpValue { public final class UpValue {
LuaValue[] array; // initially the stack, becomes a holder LuaValue[] array; // initially the stack, becomes a holder
int index; int index;
/** /**
* Create an upvalue relative to a stack * Create an upvalue relative to a stack
*
* @param stack the stack * @param stack the stack
* @param index the index on the stack for the upvalue * @param index the index on the stack for the upvalue
*/ */
public UpValue( LuaValue[] stack, int index) { public UpValue(LuaValue[] stack, int index) {
this.array = stack; this.array = stack;
this.index = index; this.index = index;
} }
@@ -45,32 +47,31 @@ public final class UpValue {
public String toString() { public String toString() {
return index + "/" + array.length + " " + array[index]; return index + "/" + array.length + " " + array[index];
} }
/** /**
* Convert this upvalue to a Java String * Convert this upvalue to a Java String
*
* @return the Java String for this upvalue. * @return the Java String for this upvalue.
* @see LuaValue#tojstring() * @see LuaValue#tojstring()
*/ */
public String tojstring() { public String tojstring() {
return array[index].tojstring(); return array[index].tojstring();
} }
/** /**
* Get the value of the upvalue * Get the value of the upvalue
*
* @return the {@link LuaValue} for this upvalue * @return the {@link LuaValue} for this upvalue
*/ */
public final LuaValue getValue() { public final LuaValue getValue() { return array[index]; }
return array[index];
}
/** /**
* Set the value of the upvalue * Set the value of the upvalue
*
* @param value the {@link LuaValue} to set it to * @param value the {@link LuaValue} to set it to
*/ */
public final void setValue( LuaValue value ) { public final void setValue(LuaValue value) { array[index] = value; }
array[index] = value;
}
/** /**
* Close this upvalue so it is no longer on the stack * Close this upvalue so it is no longer on the stack
*/ */

View File

@@ -25,20 +25,20 @@ public class Upvaldesc {
/* upvalue name (for debug information) */ /* upvalue name (for debug information) */
public LuaString name; public LuaString name;
/* whether it is in stack */ /* whether it is in stack */
public final boolean instack; public final boolean instack;
/* index of upvalue (in stack or in outer function's list) */ /* index of upvalue (in stack or in outer function's list) */
public final short idx; public final short idx;
public Upvaldesc(LuaString name, boolean instack, int idx) { public Upvaldesc(LuaString name, boolean instack, int idx) {
this.name = name; this.name = name;
this.instack = instack; this.instack = instack;
this.idx = (short) idx; this.idx = (short) idx;
} }
public String toString() { public String toString() {
return idx + (instack? " instack ": " closed ") + String.valueOf(name); return idx+(instack? " instack ": " closed ")+String.valueOf(name);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -27,26 +27,26 @@ import org.luaj.vm2.LuaTable.Slot;
import org.luaj.vm2.LuaTable.StrongSlot; import org.luaj.vm2.LuaTable.StrongSlot;
/** /**
* Subclass of {@link LuaTable} that provides weak key and weak value semantics. * Subclass of {@link LuaTable} that provides weak key and weak value semantics.
* <p>
* Normally these are not created directly, but indirectly when changing the mode
* of a {@link LuaTable} as lua script executes.
* <p> * <p>
* However, calling the constructors directly when weak tables are required from * Normally these are not created directly, but indirectly when changing the
* Java will reduce overhead. * mode of a {@link LuaTable} as lua script executes.
* <p>
* However, calling the constructors directly when weak tables are required from
* Java will reduce overhead.
*/ */
public class WeakTable implements Metatable { public class WeakTable implements Metatable {
private boolean weakkeys, weakvalues; private boolean weakkeys, weakvalues;
private LuaValue backing; private LuaValue backing;
public static LuaTable make(boolean weakkeys, boolean weakvalues) { public static LuaTable make(boolean weakkeys, boolean weakvalues) {
LuaString mode; LuaString mode;
if ( weakkeys && weakvalues ) { if (weakkeys && weakvalues) {
mode = LuaString.valueOf("kv"); mode = LuaString.valueOf("kv");
} else if ( weakkeys ) { } else if (weakkeys) {
mode = LuaString.valueOf("k"); mode = LuaString.valueOf("k");
} else if ( weakvalues ) { } else if (weakvalues) {
mode = LuaString.valueOf("v"); mode = LuaString.valueOf("v");
} else { } else {
return LuaTable.tableOf(); return LuaTable.tableOf();
@@ -59,7 +59,8 @@ public class WeakTable implements Metatable {
/** /**
* Construct a table with weak keys, weak values, or both * Construct a table with weak keys, weak values, or both
* @param weakkeys true to let the table have weak keys *
* @param weakkeys true to let the table have weak keys
* @param weakvalues true to let the table have weak values * @param weakvalues true to let the table have weak values
*/ */
public WeakTable(boolean weakkeys, boolean weakvalues, LuaValue backing) { public WeakTable(boolean weakkeys, boolean weakvalues, LuaValue backing) {
@@ -82,26 +83,26 @@ public class WeakTable implements Metatable {
public Slot entry(LuaValue key, LuaValue value) { public Slot entry(LuaValue key, LuaValue value) {
value = value.strongvalue(); value = value.strongvalue();
if ( value == null ) if (value == null)
return null; return null;
if ( weakkeys && !( key.isnumber() || key.isstring() || key.isboolean() )) { if (weakkeys && !(key.isnumber() || key.isstring() || key.isboolean())) {
if ( weakvalues && !( value.isnumber() || value.isstring() || value.isboolean() )) { if (weakvalues && !(value.isnumber() || value.isstring() || value.isboolean())) {
return new WeakKeyAndValueSlot( key, value, null ); return new WeakKeyAndValueSlot(key, value, null);
} else { } else {
return new WeakKeySlot( key, value, null ); return new WeakKeySlot(key, value, null);
} }
} }
if ( weakvalues && ! (value.isnumber() || value.isstring() || value.isboolean() )) { if (weakvalues && !(value.isnumber() || value.isstring() || value.isboolean())) {
return new WeakValueSlot( key, value, null ); return new WeakValueSlot(key, value, null);
} }
return LuaTable.defaultEntry( key, value ); return LuaTable.defaultEntry(key, value);
} }
public static abstract class WeakSlot implements Slot { public static abstract class WeakSlot implements Slot {
protected Object key; protected Object key;
protected Object value; protected Object value;
protected Slot next; protected Slot next;
protected WeakSlot(Object key, Object value, Slot next) { protected WeakSlot(Object key, Object value, Slot next) {
this.key = key; this.key = key;
@@ -109,14 +110,14 @@ public class WeakTable implements Metatable {
this.next = next; this.next = next;
} }
public abstract int keyindex( int hashMask ); public abstract int keyindex(int hashMask);
public abstract Slot set(LuaValue value); public abstract Slot set(LuaValue value);
public StrongSlot first() { public StrongSlot first() {
LuaValue key = strongkey(); LuaValue key = strongkey();
LuaValue value = strongvalue(); LuaValue value = strongvalue();
if ( key != null && value != null ) { if (key != null && value != null) {
return new LuaTable.NormalEntry(key, value); return new LuaTable.NormalEntry(key, value);
} else { } else {
this.key = null; this.key = null;
@@ -127,12 +128,12 @@ public class WeakTable implements Metatable {
public StrongSlot find(LuaValue key) { public StrongSlot find(LuaValue key) {
StrongSlot first = first(); StrongSlot first = first();
return ( first != null ) ? first.find( key ) : null; return (first != null)? first.find(key): null;
} }
public boolean keyeq(LuaValue key) { public boolean keyeq(LuaValue key) {
StrongSlot first = first(); StrongSlot first = first();
return ( first != null ) && first.keyeq( key ); return (first != null) && first.keyeq(key);
} }
public Slot rest() { public Slot rest() {
@@ -146,46 +147,46 @@ public class WeakTable implements Metatable {
public Slot set(StrongSlot target, LuaValue value) { public Slot set(StrongSlot target, LuaValue value) {
LuaValue key = strongkey(); LuaValue key = strongkey();
if ( key != null && target.find( key ) != null ) { if (key != null && target.find(key) != null) {
return set( value ); return set(value);
} else if ( key != null ) { } else if (key != null) {
// Our key is still good. // Our key is still good.
next = next.set( target, value ); next = next.set(target, value);
return this; return this;
} else { } else {
// our key was dropped, remove ourselves from the chain. // our key was dropped, remove ourselves from the chain.
return next.set( target, value ); return next.set(target, value);
} }
} }
public Slot add( Slot entry ) { public Slot add(Slot entry) {
next = ( next != null ) ? next.add( entry ) : entry; next = (next != null)? next.add(entry): entry;
if ( strongkey() != null && strongvalue() != null ) { if (strongkey() != null && strongvalue() != null) {
return this; return this;
} else { } else {
return next; return next;
} }
} }
public Slot remove( StrongSlot target ) { public Slot remove(StrongSlot target) {
LuaValue key = strongkey(); LuaValue key = strongkey();
if ( key == null ) { if (key == null) {
return next.remove( target ); return next.remove(target);
} else if ( target.keyeq( key ) ) { } else if (target.keyeq(key)) {
this.value = null; this.value = null;
return this; return this;
} else { } else {
next = next.remove( target ); next = next.remove(target);
return this; return this;
} }
} }
public Slot relink( Slot rest ) { public Slot relink(Slot rest) {
if ( strongkey() != null && strongvalue() != null ) { if (strongkey() != null && strongvalue() != null) {
if ( rest == null && this.next == null ) { if (rest == null && this.next == null) {
return this; return this;
} else { } else {
return copy( rest ); return copy(rest);
} }
} else { } else {
return rest; return rest;
@@ -200,25 +201,25 @@ public class WeakTable implements Metatable {
return (LuaValue) value; return (LuaValue) value;
} }
protected abstract WeakSlot copy( Slot next ); protected abstract WeakSlot copy(Slot next);
} }
static class WeakKeySlot extends WeakSlot { static class WeakKeySlot extends WeakSlot {
private final int keyhash; private final int keyhash;
protected WeakKeySlot( LuaValue key, LuaValue value, Slot next ) { protected WeakKeySlot(LuaValue key, LuaValue value, Slot next) {
super(weaken(key), value, next); super(weaken(key), value, next);
keyhash = key.hashCode(); keyhash = key.hashCode();
} }
protected WeakKeySlot( WeakKeySlot copyFrom, Slot next ) { protected WeakKeySlot(WeakKeySlot copyFrom, Slot next) {
super( copyFrom.key, copyFrom.value, next ); super(copyFrom.key, copyFrom.value, next);
this.keyhash = copyFrom.keyhash; this.keyhash = copyFrom.keyhash;
} }
public int keyindex( int mask ) { public int keyindex(int mask) {
return LuaTable.hashmod( keyhash, mask ); return LuaTable.hashmod(keyhash, mask);
} }
public Slot set(LuaValue value) { public Slot set(LuaValue value) {
@@ -227,26 +228,26 @@ public class WeakTable implements Metatable {
} }
public LuaValue strongkey() { public LuaValue strongkey() {
return strengthen( key ); return strengthen(key);
} }
protected WeakSlot copy( Slot rest ) { protected WeakSlot copy(Slot rest) {
return new WeakKeySlot( this, rest ); return new WeakKeySlot(this, rest);
} }
} }
static class WeakValueSlot extends WeakSlot { static class WeakValueSlot extends WeakSlot {
protected WeakValueSlot( LuaValue key, LuaValue value, Slot next ) { protected WeakValueSlot(LuaValue key, LuaValue value, Slot next) {
super( key, weaken(value), next); super(key, weaken(value), next);
} }
protected WeakValueSlot( WeakValueSlot copyFrom, Slot next ) { protected WeakValueSlot(WeakValueSlot copyFrom, Slot next) {
super( copyFrom.key, copyFrom.value, next ); super(copyFrom.key, copyFrom.value, next);
} }
public int keyindex( int mask ) { public int keyindex(int mask) {
return LuaTable.hashSlot( strongkey(), mask ); return LuaTable.hashSlot(strongkey(), mask);
} }
public Slot set(LuaValue value) { public Slot set(LuaValue value) {
@@ -255,11 +256,11 @@ public class WeakTable implements Metatable {
} }
public LuaValue strongvalue() { public LuaValue strongvalue() {
return strengthen( value ); return strengthen(value);
} }
protected WeakSlot copy(Slot next) { protected WeakSlot copy(Slot next) {
return new WeakValueSlot( this, next ); return new WeakValueSlot(this, next);
} }
} }
@@ -267,18 +268,18 @@ public class WeakTable implements Metatable {
private final int keyhash; private final int keyhash;
protected WeakKeyAndValueSlot( LuaValue key, LuaValue value, Slot next ) { protected WeakKeyAndValueSlot(LuaValue key, LuaValue value, Slot next) {
super( weaken(key), weaken(value), next ); super(weaken(key), weaken(value), next);
keyhash = key.hashCode(); keyhash = key.hashCode();
} }
protected WeakKeyAndValueSlot(WeakKeyAndValueSlot copyFrom, Slot next) { protected WeakKeyAndValueSlot(WeakKeyAndValueSlot copyFrom, Slot next) {
super( copyFrom.key, copyFrom.value, next ); super(copyFrom.key, copyFrom.value, next);
keyhash = copyFrom.keyhash; keyhash = copyFrom.keyhash;
} }
public int keyindex( int hashMask ) { public int keyindex(int hashMask) {
return LuaTable.hashmod( keyhash, hashMask ); return LuaTable.hashmod(keyhash, hashMask);
} }
public Slot set(LuaValue value) { public Slot set(LuaValue value) {
@@ -287,53 +288,58 @@ public class WeakTable implements Metatable {
} }
public LuaValue strongkey() { public LuaValue strongkey() {
return strengthen( key ); return strengthen(key);
} }
public LuaValue strongvalue() { public LuaValue strongvalue() {
return strengthen( value ); return strengthen(value);
} }
protected WeakSlot copy( Slot next ) { protected WeakSlot copy(Slot next) {
return new WeakKeyAndValueSlot( this, next ); return new WeakKeyAndValueSlot(this, next);
} }
} }
/** /**
* Self-sent message to convert a value to its weak counterpart * Self-sent message to convert a value to its weak counterpart
*
* @param value value to convert * @param value value to convert
* @return {@link LuaValue} that is a strong or weak reference, depending on type of {@code value} * @return {@link LuaValue} that is a strong or weak reference, depending on
* type of {@code value}
*/ */
protected static LuaValue weaken( LuaValue value ) { protected static LuaValue weaken(LuaValue value) {
switch ( value.type() ) { switch (value.type()) {
case LuaValue.TFUNCTION: case LuaValue.TFUNCTION:
case LuaValue.TTHREAD: case LuaValue.TTHREAD:
case LuaValue.TTABLE: case LuaValue.TTABLE:
return new WeakValue(value); return new WeakValue(value);
case LuaValue.TUSERDATA: case LuaValue.TUSERDATA:
return new WeakUserdata(value); return new WeakUserdata(value);
default: default:
return value; return value;
} }
} }
/** /**
* Unwrap a LuaValue from a WeakReference and/or WeakUserdata. * Unwrap a LuaValue from a WeakReference and/or WeakUserdata.
*
* @param ref reference to convert * @param ref reference to convert
* @return LuaValue or null * @return LuaValue or null
* @see #weaken(LuaValue) * @see #weaken(LuaValue)
*/ */
protected static LuaValue strengthen(Object ref) { protected static LuaValue strengthen(Object ref) {
if ( ref instanceof WeakReference ) { if (ref instanceof WeakReference) {
ref = ((WeakReference) ref).get(); ref = ((WeakReference) ref).get();
} }
if ( ref instanceof WeakValue ) { if (ref instanceof WeakValue) {
return ((WeakValue) ref).strongvalue(); return ((WeakValue) ref).strongvalue();
} }
return (LuaValue) ref; return (LuaValue) ref;
} }
/** Internal class to implement weak values. /**
* Internal class to implement weak values.
*
* @see WeakTable * @see WeakTable
*/ */
static class WeakValue extends LuaValue { static class WeakValue extends LuaValue {
@@ -344,36 +350,38 @@ public class WeakTable implements Metatable {
} }
public int type() { public int type() {
illegal("type","weak value"); illegal("type", "weak value");
return 0; return 0;
} }
public String typename() { public String typename() {
illegal("typename","weak value"); illegal("typename", "weak value");
return null; return null;
} }
public String toString() { public String toString() {
return "weak<"+ref.get()+">"; return "weak<" + ref.get() + ">";
} }
public LuaValue strongvalue() { public LuaValue strongvalue() {
Object o = ref.get(); Object o = ref.get();
return (LuaValue)o; return (LuaValue) o;
} }
public boolean raweq(LuaValue rhs) { public boolean raweq(LuaValue rhs) {
Object o = ref.get(); Object o = ref.get();
return o!=null && rhs.raweq((LuaValue)o); return o != null && rhs.raweq((LuaValue) o);
} }
} }
/** Internal class to implement weak userdata values. /**
* Internal class to implement weak userdata values.
*
* @see WeakTable * @see WeakTable
*/ */
static final class WeakUserdata extends WeakValue { static final class WeakUserdata extends WeakValue {
private final WeakReference ob; private final WeakReference ob;
private final LuaValue mt; private final LuaValue mt;
private WeakUserdata(LuaValue value) { private WeakUserdata(LuaValue value) {
super(value); super(value);
@@ -383,11 +391,11 @@ public class WeakTable implements Metatable {
public LuaValue strongvalue() { public LuaValue strongvalue() {
Object u = ref.get(); Object u = ref.get();
if ( u != null ) if (u != null)
return (LuaValue) u; return (LuaValue) u;
Object o = ob.get(); Object o = ob.get();
if ( o != null ) { if (o != null) {
LuaValue ud = LuaValue.userdataOf(o,mt); LuaValue ud = LuaValue.userdataOf(o, mt);
ref = new WeakReference(ud); ref = new WeakReference(ud);
return ud; return ud;
} else { } else {
@@ -397,7 +405,7 @@ public class WeakTable implements Metatable {
} }
public LuaValue wrap(LuaValue value) { public LuaValue wrap(LuaValue value) {
return weakvalues ? weaken( value ) : value; return weakvalues? weaken(value): value;
} }
public LuaValue arrayget(LuaValue[] array, int index) { public LuaValue arrayget(LuaValue[] array, int index) {

View File

@@ -36,153 +36,141 @@ import org.luaj.vm2.Upvaldesc;
* @see FuncState * @see FuncState
*/ */
public class Constants extends Lua { public class Constants extends Lua {
/** Maximum stack size of a luaj vm interpreter instance. */ /** Maximum stack size of a luaj vm interpreter instance. */
public static final int MAXSTACK = 250; public static final int MAXSTACK = 250;
static final int LUAI_MAXUPVAL = 0xff; static final int LUAI_MAXUPVAL = 0xff;
static final int LUAI_MAXVARS = 200; static final int LUAI_MAXVARS = 200;
static final int NO_REG = MAXARG_A; static final int NO_REG = MAXARG_A;
/* OpMode - basic instruction format */ /* OpMode - basic instruction format */
static final int static final int iABC = 0, iABx = 1, iAsBx = 2;
iABC = 0,
iABx = 1,
iAsBx = 2;
/* OpArgMask */ /* OpArgMask */
static final int static final int OpArgN = 0, /* argument is not used */
OpArgN = 0, /* argument is not used */ OpArgU = 1, /* argument is used */
OpArgU = 1, /* argument is used */ OpArgR = 2, /* argument is a register or a jump offset */
OpArgR = 2, /* argument is a register or a jump offset */ OpArgK = 3; /* argument is a constant or register/constant */
OpArgK = 3; /* argument is a constant or register/constant */
protected static void _assert(boolean b) { protected static void _assert(boolean b) {
if (!b) if (!b)
throw new LuaError("compiler assert failed"); throw new LuaError("compiler assert failed");
} }
static void SET_OPCODE(InstructionPtr i,int o) { static void SET_OPCODE(InstructionPtr i, int o) {
i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) ); i.set((i.get() & (MASK_NOT_OP)) | ((o<<POS_OP) & MASK_OP));
} }
static void SETARG_A(int[] code, int index, int u) { static void SETARG_A(int[] code, int index, int u) {
code[index] = (code[index] & (MASK_NOT_A)) | ((u << POS_A) & MASK_A); code[index] = (code[index] & (MASK_NOT_A)) | ((u<<POS_A) & MASK_A);
} }
static void SETARG_A(InstructionPtr i,int u) { static void SETARG_A(InstructionPtr i, int u) {
i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) ); i.set((i.get() & (MASK_NOT_A)) | ((u<<POS_A) & MASK_A));
} }
static void SETARG_B(InstructionPtr i,int u) { static void SETARG_B(InstructionPtr i, int u) {
i.set( ( i.get() & (MASK_NOT_B)) | ((u << POS_B) & MASK_B) ); i.set((i.get() & (MASK_NOT_B)) | ((u<<POS_B) & MASK_B));
} }
static void SETARG_C(InstructionPtr i,int u) { static void SETARG_C(InstructionPtr i, int u) {
i.set( ( i.get() & (MASK_NOT_C)) | ((u << POS_C) & MASK_C) ); i.set((i.get() & (MASK_NOT_C)) | ((u<<POS_C) & MASK_C));
} }
static void SETARG_Bx(InstructionPtr i,int u) { static void SETARG_Bx(InstructionPtr i, int u) {
i.set( ( i.get() & (MASK_NOT_Bx)) | ((u << POS_Bx) & MASK_Bx) ); i.set((i.get() & (MASK_NOT_Bx)) | ((u<<POS_Bx) & MASK_Bx));
} }
static void SETARG_sBx(InstructionPtr i,int u) { static void SETARG_sBx(InstructionPtr i, int u) {
SETARG_Bx( i, u + MAXARG_sBx ); SETARG_Bx(i, u+MAXARG_sBx);
} }
static int CREATE_ABC(int o, int a, int b, int c) { static int CREATE_ABC(int o, int a, int b, int c) {
return ((o << POS_OP) & MASK_OP) | return ((o<<POS_OP) & MASK_OP) | ((a<<POS_A) & MASK_A) | ((b<<POS_B) & MASK_B) | ((c<<POS_C) & MASK_C);
((a << POS_A) & MASK_A) |
((b << POS_B) & MASK_B) |
((c << POS_C) & MASK_C) ;
} }
static int CREATE_ABx(int o, int a, int bc) { static int CREATE_ABx(int o, int a, int bc) {
return ((o << POS_OP) & MASK_OP) | return ((o<<POS_OP) & MASK_OP) | ((a<<POS_A) & MASK_A) | ((bc<<POS_Bx) & MASK_Bx);
((a << POS_A) & MASK_A) | }
((bc << POS_Bx) & MASK_Bx) ;
}
static int CREATE_Ax(int o, int a) { static int CREATE_Ax(int o, int a) {
return ((o << POS_OP) & MASK_OP) | return ((o<<POS_OP) & MASK_OP) | ((a<<POS_Ax) & MASK_Ax);
((a << POS_Ax) & MASK_Ax) ; }
}
// vector reallocation // vector reallocation
static LuaValue[] realloc(LuaValue[] v, int n) { static LuaValue[] realloc(LuaValue[] v, int n) {
LuaValue[] a = new LuaValue[n]; LuaValue[] a = new LuaValue[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static Prototype[] realloc(Prototype[] v, int n) { static Prototype[] realloc(Prototype[] v, int n) {
Prototype[] a = new Prototype[n]; Prototype[] a = new Prototype[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static LuaString[] realloc(LuaString[] v, int n) { static LuaString[] realloc(LuaString[] v, int n) {
LuaString[] a = new LuaString[n]; LuaString[] a = new LuaString[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static LocVars[] realloc(LocVars[] v, int n) { static LocVars[] realloc(LocVars[] v, int n) {
LocVars[] a = new LocVars[n]; LocVars[] a = new LocVars[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static Upvaldesc[] realloc(Upvaldesc[] v, int n) { static Upvaldesc[] realloc(Upvaldesc[] v, int n) {
Upvaldesc[] a = new Upvaldesc[n]; Upvaldesc[] a = new Upvaldesc[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) { static LexState.Vardesc[] realloc(LexState.Vardesc[] v, int n) {
LexState.Vardesc[] a = new LexState.Vardesc[n]; LexState.Vardesc[] a = new LexState.Vardesc[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) { static LexState.Labeldesc[] grow(LexState.Labeldesc[] v, int min_n) {
return v == null ? new LexState.Labeldesc[2] : v.length < min_n ? realloc(v, v.length*2) : v; return v == null? new LexState.Labeldesc[2]: v.length < min_n? realloc(v, v.length*2): v;
} }
static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) { static LexState.Labeldesc[] realloc(LexState.Labeldesc[] v, int n) {
LexState.Labeldesc[] a = new LexState.Labeldesc[n]; LexState.Labeldesc[] a = new LexState.Labeldesc[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static int[] realloc(int[] v, int n) { static int[] realloc(int[] v, int n) {
int[] a = new int[n]; int[] a = new int[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static byte[] realloc(byte[] v, int n) { static byte[] realloc(byte[] v, int n) {
byte[] a = new byte[n]; byte[] a = new byte[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }
static char[] realloc(char[] v, int n) { static char[] realloc(char[] v, int n) {
char[] a = new char[n]; char[] a = new char[n];
if ( v != null ) if (v != null)
System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); System.arraycopy(v, 0, a, 0, Math.min(v.length, n));
return a; return a;
} }

View File

@@ -32,35 +32,47 @@ import org.luaj.vm2.LuaString;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype; import org.luaj.vm2.Prototype;
/**
/** Class to dump a {@link Prototype} into an output stream, as part of compiling. * Class to dump a {@link Prototype} into an output stream, as part of
* compiling.
* <p> * <p>
* Generally, this class is not used directly, but rather indirectly via a command * Generally, this class is not used directly, but rather indirectly via a
* line interface tool such as {@link luac}. * command line interface tool such as {@link luac}.
* <p> * <p>
* A lua binary file is created via {@link DumpState#dump}: * A lua binary file is created via {@link DumpState#dump}:
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua"); * {
* ByteArrayOutputStream o = new ByteArrayOutputStream(); * &#64;code
* DumpState.dump(p, o, false); * Globals globals = JsePlatform.standardGlobals();
* byte[] lua_binary_file_bytes = o.toByteArray(); * Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* } </pre> * ByteArrayOutputStream o = new ByteArrayOutputStream();
* DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray();
* }
* </pre>
* *
* The {@link LoadState} may be used directly to undump these bytes: * The {@link LoadState} may be used directly to undump these bytes:
* <pre> {@code *
* <pre>
* {@code
* Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua"); * Prototypep = LoadState.instance.undump(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua");
* LuaClosure c = new LuaClosure(p, globals); * LuaClosure c = new LuaClosure(p, globals);
* c.call(); * c.call();
* } </pre> * }
* </pre>
* *
* *
* More commonly, the {@link Globals#undumper} may be used to undump them: * More commonly, the {@link Globals#undumper} may be used to undump them:
* <pre> {@code *
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b"); * <pre>
* LuaClosure c = new LuaClosure(p, globals); * {
* c.call(); * &#64;code
* } </pre> * Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals);
* c.call();
* }
* </pre>
* *
* @see luac * @see luac
* @see LoadState * @see LoadState
@@ -71,33 +83,39 @@ public class DumpState {
/** set true to allow integer compilation */ /** set true to allow integer compilation */
public static boolean ALLOW_INTEGER_CASTING = false; public static boolean ALLOW_INTEGER_CASTING = false;
/** format corresponding to non-number-patched lua, all numbers are floats or doubles */ /**
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; * format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0;
/** format corresponding to non-number-patched lua, all numbers are ints */ /** format corresponding to non-number-patched lua, all numbers are ints */
public static final int NUMBER_FORMAT_INTS_ONLY = 1; public static final int NUMBER_FORMAT_INTS_ONLY = 1;
/** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */ /**
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; * format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
/** default number format */ /** default number format */
public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES; public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES;
// header fields // header fields
private boolean IS_LITTLE_ENDIAN = true; private boolean IS_LITTLE_ENDIAN = true;
private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT; private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT;
private int SIZEOF_LUA_NUMBER = 8; private int SIZEOF_LUA_NUMBER = 8;
private static final int SIZEOF_INT = 4; private static final int SIZEOF_INT = 4;
private static final int SIZEOF_SIZET = 4; private static final int SIZEOF_SIZET = 4;
private static final int SIZEOF_INSTRUCTION = 4; private static final int SIZEOF_INSTRUCTION = 4;
DataOutputStream writer; DataOutputStream writer;
boolean strip; boolean strip;
int status; int status;
public DumpState(OutputStream w, boolean strip) { public DumpState(OutputStream w, boolean strip) {
this.writer = new DataOutputStream( w ); this.writer = new DataOutputStream(w);
this.strip = strip; this.strip = strip;
this.status = 0; this.status = 0;
} }
@@ -107,58 +125,58 @@ public class DumpState {
} }
void dumpChar(int b) throws IOException { void dumpChar(int b) throws IOException {
writer.write( b ); writer.write(b);
} }
void dumpInt(int x) throws IOException { void dumpInt(int x) throws IOException {
if ( IS_LITTLE_ENDIAN ) { if (IS_LITTLE_ENDIAN) {
writer.writeByte(x&0xff); writer.writeByte(x & 0xff);
writer.writeByte((x>>8)&0xff); writer.writeByte((x>>8) & 0xff);
writer.writeByte((x>>16)&0xff); writer.writeByte((x>>16) & 0xff);
writer.writeByte((x>>24)&0xff); writer.writeByte((x>>24) & 0xff);
} else { } else {
writer.writeInt(x); writer.writeInt(x);
} }
} }
void dumpString(LuaString s) throws IOException { void dumpString(LuaString s) throws IOException {
final int len = s.len().toint(); final int len = s.len().toint();
dumpInt( len+1 ); dumpInt(len+1);
s.write( writer, 0, len ); s.write(writer, 0, len);
writer.write( 0 ); writer.write(0);
} }
void dumpDouble(double d) throws IOException { void dumpDouble(double d) throws IOException {
long l = Double.doubleToLongBits(d); long l = Double.doubleToLongBits(d);
if ( IS_LITTLE_ENDIAN ) { if (IS_LITTLE_ENDIAN) {
dumpInt( (int) l ); dumpInt((int) l);
dumpInt( (int) (l>>32) ); dumpInt((int) (l>>32));
} else { } else {
writer.writeLong(l); writer.writeLong(l);
} }
} }
void dumpCode( final Prototype f ) throws IOException { void dumpCode(final Prototype f) throws IOException {
final int[] code = f.code; final int[] code = f.code;
int n = code.length; int n = code.length;
dumpInt( n ); dumpInt(n);
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
dumpInt( code[i] ); dumpInt(code[i]);
} }
void dumpConstants(final Prototype f) throws IOException { void dumpConstants(final Prototype f) throws IOException {
final LuaValue[] k = f.k; final LuaValue[] k = f.k;
int i, n = k.length; int i, n = k.length;
dumpInt(n); dumpInt(n);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
final LuaValue o = k[i]; final LuaValue o = k[i];
switch ( o.type() ) { switch (o.type()) {
case LuaValue.TNIL: case LuaValue.TNIL:
writer.write(LuaValue.TNIL); writer.write(LuaValue.TNIL);
break; break;
case LuaValue.TBOOLEAN: case LuaValue.TBOOLEAN:
writer.write(LuaValue.TBOOLEAN); writer.write(LuaValue.TBOOLEAN);
dumpChar(o.toboolean() ? 1 : 0); dumpChar(o.toboolean()? 1: 0);
break; break;
case LuaValue.TNUMBER: case LuaValue.TNUMBER:
switch (NUMBER_FORMAT) { switch (NUMBER_FORMAT) {
@@ -167,13 +185,13 @@ public class DumpState {
dumpDouble(o.todouble()); dumpDouble(o.todouble());
break; break;
case NUMBER_FORMAT_INTS_ONLY: case NUMBER_FORMAT_INTS_ONLY:
if ( ! ALLOW_INTEGER_CASTING && ! o.isint() ) if (!ALLOW_INTEGER_CASTING && !o.isint())
throw new java.lang.IllegalArgumentException("not an integer: "+o); throw new java.lang.IllegalArgumentException("not an integer: " + o);
writer.write(LuaValue.TNUMBER); writer.write(LuaValue.TNUMBER);
dumpInt(o.toint()); dumpInt(o.toint());
break; break;
case NUMBER_FORMAT_NUM_PATCH_INT32: case NUMBER_FORMAT_NUM_PATCH_INT32:
if ( o.isint() ) { if (o.isint()) {
writer.write(LuaValue.TINT); writer.write(LuaValue.TINT);
dumpInt(o.toint()); dumpInt(o.toint());
} else { } else {
@@ -182,12 +200,12 @@ public class DumpState {
} }
break; break;
default: default:
throw new IllegalArgumentException("number format not supported: "+NUMBER_FORMAT); throw new IllegalArgumentException("number format not supported: " + NUMBER_FORMAT);
} }
break; break;
case LuaValue.TSTRING: case LuaValue.TSTRING:
writer.write(LuaValue.TSTRING); writer.write(LuaValue.TSTRING);
dumpString((LuaString)o); dumpString((LuaString) o);
break; break;
default: default:
throw new IllegalArgumentException("bad type for " + o); throw new IllegalArgumentException("bad type for " + o);
@@ -203,7 +221,7 @@ public class DumpState {
int n = f.upvalues.length; int n = f.upvalues.length;
dumpInt(n); dumpInt(n);
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
writer.writeByte(f.upvalues[i].instack ? 1 : 0); writer.writeByte(f.upvalues[i].instack? 1: 0);
writer.writeByte(f.upvalues[i].idx); writer.writeByte(f.upvalues[i].idx);
} }
} }
@@ -214,11 +232,11 @@ public class DumpState {
dumpInt(0); dumpInt(0);
else else
dumpString(f.source); dumpString(f.source);
n = strip ? 0 : f.lineinfo.length; n = strip? 0: f.lineinfo.length;
dumpInt(n); dumpInt(n);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
dumpInt(f.lineinfo[i]); dumpInt(f.lineinfo[i]);
n = strip ? 0 : f.locvars.length; n = strip? 0: f.locvars.length;
dumpInt(n); dumpInt(n);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
LocVars lvi = f.locvars[i]; LocVars lvi = f.locvars[i];
@@ -226,12 +244,12 @@ public class DumpState {
dumpInt(lvi.startpc); dumpInt(lvi.startpc);
dumpInt(lvi.endpc); dumpInt(lvi.endpc);
} }
n = strip ? 0 : f.upvalues.length; n = strip? 0: f.upvalues.length;
dumpInt(n); dumpInt(n);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
dumpString(f.upvalues[i].name); dumpString(f.upvalues[i].name);
} }
void dumpFunction(final Prototype f) throws IOException { void dumpFunction(final Prototype f) throws IOException {
dumpInt(f.linedefined); dumpInt(f.linedefined);
dumpInt(f.lastlinedefined); dumpInt(f.lastlinedefined);
@@ -245,23 +263,23 @@ public class DumpState {
} }
void dumpHeader() throws IOException { void dumpHeader() throws IOException {
writer.write( LoadState.LUA_SIGNATURE ); writer.write(LoadState.LUA_SIGNATURE);
writer.write( LoadState.LUAC_VERSION ); writer.write(LoadState.LUAC_VERSION);
writer.write( LoadState.LUAC_FORMAT ); writer.write(LoadState.LUAC_FORMAT);
writer.write( IS_LITTLE_ENDIAN? 1: 0 ); writer.write(IS_LITTLE_ENDIAN? 1: 0);
writer.write( SIZEOF_INT ); writer.write(SIZEOF_INT);
writer.write( SIZEOF_SIZET ); writer.write(SIZEOF_SIZET);
writer.write( SIZEOF_INSTRUCTION ); writer.write(SIZEOF_INSTRUCTION);
writer.write( SIZEOF_LUA_NUMBER ); writer.write(SIZEOF_LUA_NUMBER);
writer.write( NUMBER_FORMAT ); writer.write(NUMBER_FORMAT);
writer.write( LoadState.LUAC_TAIL ); writer.write(LoadState.LUAC_TAIL);
} }
/* /*
** dump Lua function as precompiled chunk ** dump Lua function as precompiled chunk
*/ */
public static int dump( Prototype f, OutputStream w, boolean strip ) throws IOException { public static int dump(Prototype f, OutputStream w, boolean strip) throws IOException {
DumpState D = new DumpState(w,strip); DumpState D = new DumpState(w, strip);
D.dumpHeader(); D.dumpHeader();
D.dumpFunction(f); D.dumpFunction(f);
return D.status; return D.status;
@@ -269,28 +287,32 @@ public class DumpState {
/** /**
* *
* @param f the function to dump * @param f the function to dump
* @param w the output stream to dump to * @param w the output stream to dump to
* @param stripDebug true to strip debugging info, false otherwise * @param stripDebug true to strip debugging info, false otherwise
* @param numberFormat one of NUMBER_FORMAT_FLOATS_OR_DOUBLES, NUMBER_FORMAT_INTS_ONLY, NUMBER_FORMAT_NUM_PATCH_INT32 * @param numberFormat one of NUMBER_FORMAT_FLOATS_OR_DOUBLES,
* @param littleendian true to use little endian for numbers, false for big endian * NUMBER_FORMAT_INTS_ONLY,
* NUMBER_FORMAT_NUM_PATCH_INT32
* @param littleendian true to use little endian for numbers, false for big
* endian
* @return 0 if dump succeeds * @return 0 if dump succeeds
* @throws IOException * @throws IOException
* @throws IllegalArgumentException if the number format it not supported * @throws IllegalArgumentException if the number format it not supported
*/ */
public static int dump(Prototype f, OutputStream w, boolean stripDebug, int numberFormat, boolean littleendian) throws IOException { public static int dump(Prototype f, OutputStream w, boolean stripDebug, int numberFormat, boolean littleendian)
switch ( numberFormat ) { throws IOException {
switch (numberFormat) {
case NUMBER_FORMAT_FLOATS_OR_DOUBLES: case NUMBER_FORMAT_FLOATS_OR_DOUBLES:
case NUMBER_FORMAT_INTS_ONLY: case NUMBER_FORMAT_INTS_ONLY:
case NUMBER_FORMAT_NUM_PATCH_INT32: case NUMBER_FORMAT_NUM_PATCH_INT32:
break; break;
default: default:
throw new IllegalArgumentException("number format not supported: "+numberFormat); throw new IllegalArgumentException("number format not supported: " + numberFormat);
} }
DumpState D = new DumpState(w,stripDebug); DumpState D = new DumpState(w, stripDebug);
D.IS_LITTLE_ENDIAN = littleendian; D.IS_LITTLE_ENDIAN = littleendian;
D.NUMBER_FORMAT = numberFormat; D.NUMBER_FORMAT = numberFormat;
D.SIZEOF_LUA_NUMBER = (numberFormat==NUMBER_FORMAT_INTS_ONLY? 4: 8); D.SIZEOF_LUA_NUMBER = (numberFormat == NUMBER_FORMAT_INTS_ONLY? 4: 8);
D.dumpHeader(); D.dumpHeader();
D.dumpFunction(f); D.dumpFunction(f);
return D.status; return D.status;

View File

@@ -34,44 +34,42 @@ import org.luaj.vm2.Upvaldesc;
import org.luaj.vm2.compiler.LexState.ConsControl; import org.luaj.vm2.compiler.LexState.ConsControl;
import org.luaj.vm2.compiler.LexState.expdesc; import org.luaj.vm2.compiler.LexState.expdesc;
public class FuncState extends Constants { public class FuncState extends Constants {
static class BlockCnt { static class BlockCnt {
BlockCnt previous; /* chain */ BlockCnt previous; /* chain */
short firstlabel; /* index of first label in this block */ short firstlabel; /* index of first label in this block */
short firstgoto; /* index of first pending goto in this block */ short firstgoto; /* index of first pending goto in this block */
short nactvar; /* # active locals outside the breakable structure */ short nactvar; /* # active locals outside the breakable structure */
boolean upval; /* true if some variable in the block is an upvalue */ boolean upval; /* true if some variable in the block is an upvalue */
boolean isloop; /* true if `block' is a loop */ boolean isloop; /* true if `block' is a loop */
}; };
Prototype f; /* current function header */ Prototype f; /* current function header */
Hashtable h; /* table to find (and reuse) elements in `k' */ Hashtable h; /* table to find (and reuse) elements in `k' */
FuncState prev; /* enclosing function */ FuncState prev; /* enclosing function */
LexState ls; /* lexical state */ LexState ls; /* lexical state */
BlockCnt bl; /* chain of current blocks */ BlockCnt bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */ int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */ int lasttarget; /* `pc' of last `jump target' */
IntPtr jpc; /* list of pending jumps to `pc' */ IntPtr jpc; /* list of pending jumps to `pc' */
int nk; /* number of elements in `k' */ int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */ int np; /* number of elements in `p' */
int firstlocal; /* index of first local var (in Dyndata array) */ int firstlocal; /* index of first local var (in Dyndata array) */
short nlocvars; /* number of elements in `locvars' */ short nlocvars; /* number of elements in `locvars' */
short nactvar; /* number of active local variables */ short nactvar; /* number of active local variables */
short nups; /* number of upvalues */ short nups; /* number of upvalues */
short freereg; /* first free register */ short freereg; /* first free register */
FuncState() { FuncState() {
} }
// ============================================================= // =============================================================
// from lcode.h // from lcode.h
// ============================================================= // =============================================================
InstructionPtr getcodePtr(expdesc e) { InstructionPtr getcodePtr(expdesc e) {
return new InstructionPtr( f.code, e.u.info ); return new InstructionPtr(f.code, e.u.info);
} }
int getcode(expdesc e) { int getcode(expdesc e) {
@@ -79,93 +77,88 @@ public class FuncState extends Constants {
} }
int codeAsBx(int o, int A, int sBx) { int codeAsBx(int o, int A, int sBx) {
return codeABx(o,A,sBx+MAXARG_sBx); return codeABx(o, A, sBx+MAXARG_sBx);
} }
void setmultret(expdesc e) { void setmultret(expdesc e) {
setreturns(e, LUA_MULTRET); setreturns(e, LUA_MULTRET);
} }
// ============================================================= // =============================================================
// from lparser.c // from lparser.c
// ============================================================= // =============================================================
/* check for repeated labels on the same block */ /* check for repeated labels on the same block */
void checkrepeated (LexState.Labeldesc[] ll, int ll_n, LuaString label) { void checkrepeated(LexState.Labeldesc[] ll, int ll_n, LuaString label) {
int i; int i;
for (i = bl.firstlabel; i < ll_n; i++) { for (i = bl.firstlabel; i < ll_n; i++) {
if (label.eq_b(ll[i].name)) { if (label.eq_b(ll[i].name)) {
String msg = ls.L.pushfstring( String msg = ls.L.pushfstring("label '" + label + " already defined on line " + ll[i].line);
"label '" + label + " already defined on line " + ll[i].line);
ls.semerror(msg); ls.semerror(msg);
} }
} }
} }
void checklimit(int v, int l, String msg) { void checklimit(int v, int l, String msg) {
if ( v > l ) if (v > l)
errorlimit( l, msg ); errorlimit(l, msg);
} }
void errorlimit (int limit, String what) { void errorlimit(int limit, String what) {
// TODO: report message logic. // TODO: report message logic.
String msg = (f.linedefined == 0) ? String msg = (f.linedefined == 0)? ls.L.pushfstring("main function has more than " + limit + " " + what)
ls.L.pushfstring("main function has more than "+limit+" "+what) : : ls.L.pushfstring("function at line " + f.linedefined + " has more than " + limit + " " + what);
ls.L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what); ls.lexerror(msg, 0);
ls.lexerror(msg, 0);
} }
LocVars getlocvar(int i) { LocVars getlocvar(int i) {
int idx = ls.dyd.actvar[firstlocal + i].idx; int idx = ls.dyd.actvar[firstlocal+i].idx;
_assert(idx < nlocvars); _assert(idx < nlocvars);
return f.locvars[idx]; return f.locvars[idx];
} }
void removevars (int tolevel) { void removevars(int tolevel) {
ls.dyd.n_actvar -= (nactvar - tolevel); ls.dyd.n_actvar -= (nactvar-tolevel);
while (nactvar > tolevel) while ( nactvar > tolevel )
getlocvar(--nactvar).endpc = pc; getlocvar(--nactvar).endpc = pc;
} }
int searchupvalue(LuaString name) {
int searchupvalue (LuaString name) { int i;
int i; Upvaldesc[] up = f.upvalues;
Upvaldesc[] up = f.upvalues; for (i = 0; i < nups; i++)
for (i = 0; i < nups; i++) if (up[i].name.eq_b(name))
if (up[i].name.eq_b(name)) return i;
return i; return -1; /* not found */
return -1; /* not found */
} }
int newupvalue (LuaString name, expdesc v) { int newupvalue(LuaString name, expdesc v) {
checklimit(nups + 1, LUAI_MAXUPVAL, "upvalues"); checklimit(nups+1, LUAI_MAXUPVAL, "upvalues");
if (f.upvalues == null || nups + 1 > f.upvalues.length) if (f.upvalues == null || nups+1 > f.upvalues.length)
f.upvalues = realloc( f.upvalues, nups > 0 ? nups*2 : 1 ); f.upvalues = realloc(f.upvalues, nups > 0? nups*2: 1);
f.upvalues[nups] = new Upvaldesc(name, v.k == LexState.VLOCAL, v.u.info); f.upvalues[nups] = new Upvaldesc(name, v.k == LexState.VLOCAL, v.u.info);
return nups++; return nups++;
} }
int searchvar(LuaString n) { int searchvar(LuaString n) {
int i; int i;
for (i = nactvar - 1; i >= 0; i--) { for (i = nactvar-1; i >= 0; i--) {
if (n.eq_b(getlocvar(i).varname)) if (n.eq_b(getlocvar(i).varname))
return i; return i;
} }
return -1; /* not found */ return -1; /* not found */
} }
void markupval(int level) { void markupval(int level) {
BlockCnt bl = this.bl; BlockCnt bl = this.bl;
while (bl.nactvar > level) while ( bl.nactvar > level )
bl = bl.previous; bl = bl.previous;
bl.upval = true; bl.upval = true;
} }
static int singlevaraux(FuncState fs, LuaString n, expdesc var, int base) { static int singlevaraux(FuncState fs, LuaString n, expdesc var, int base) {
if (fs == null) /* no more levels? */ if (fs == null) /* no more levels? */
return LexState.VVOID; /* default is global */ return LexState.VVOID; /* default is global */
int v = fs.searchvar(n); /* look up at current level */ int v = fs.searchvar(n); /* look up at current level */
if (v >= 0) { if (v >= 0) {
var.init(LexState.VLOCAL, v); var.init(LexState.VLOCAL, v);
@@ -173,15 +166,15 @@ public class FuncState extends Constants {
fs.markupval(v); /* local will be used as an upval */ fs.markupval(v); /* local will be used as an upval */
return LexState.VLOCAL; return LexState.VLOCAL;
} else { /* not found at current level; try upvalues */ } else { /* not found at current level; try upvalues */
int idx = fs.searchupvalue(n); /* try existing upvalues */ int idx = fs.searchupvalue(n); /* try existing upvalues */
if (idx < 0) { /* not found? */ if (idx < 0) { /* not found? */
if (singlevaraux(fs.prev, n, var, 0) == LexState.VVOID) /* try upper levels */ if (singlevaraux(fs.prev, n, var, 0) == LexState.VVOID) /* try upper levels */
return LexState.VVOID; /* not found; is a global */ return LexState.VVOID; /* not found; is a global */
/* else was LOCAL or UPVAL */ /* else was LOCAL or UPVAL */
idx = fs.newupvalue(n, var); /* will be a new upvalue */ idx = fs.newupvalue(n, var); /* will be a new upvalue */
} }
var.init(LexState.VUPVAL, idx); var.init(LexState.VUPVAL, idx);
return LexState.VUPVAL; return LexState.VUPVAL;
} }
} }
@@ -196,7 +189,7 @@ public class FuncState extends Constants {
final LexState.Labeldesc[] gl = ls.dyd.gt; final LexState.Labeldesc[] gl = ls.dyd.gt;
/* correct pending gotos to current block and try to close it /* correct pending gotos to current block and try to close it
with visible labels */ with visible labels */
while (i < ls.dyd.n_gt) { while ( i < ls.dyd.n_gt ) {
LexState.Labeldesc gt = gl[i]; LexState.Labeldesc gt = gl[i];
if (gt.nactvar > bl.nactvar) { if (gt.nactvar > bl.nactvar) {
if (bl.upval) if (bl.upval)
@@ -207,37 +200,37 @@ public class FuncState extends Constants {
i++; /* move to next one */ i++; /* move to next one */
} }
} }
void enterblock (BlockCnt bl, boolean isloop) { void enterblock(BlockCnt bl, boolean isloop) {
bl.isloop = isloop; bl.isloop = isloop;
bl.nactvar = nactvar; bl.nactvar = nactvar;
bl.firstlabel = (short) ls.dyd.n_label; bl.firstlabel = (short) ls.dyd.n_label;
bl.firstgoto = (short) ls.dyd.n_gt; bl.firstgoto = (short) ls.dyd.n_gt;
bl.upval = false; bl.upval = false;
bl.previous = this.bl; bl.previous = this.bl;
this.bl = bl; this.bl = bl;
_assert(this.freereg == this.nactvar); _assert(this.freereg == this.nactvar);
} }
void leaveblock() { void leaveblock() {
BlockCnt bl = this.bl; BlockCnt bl = this.bl;
if (bl.previous != null && bl.upval) { if (bl.previous != null && bl.upval) {
/* create a 'jump to here' to close upvalues */ /* create a 'jump to here' to close upvalues */
int j = this.jump(); int j = this.jump();
this.patchclose(j, bl.nactvar); this.patchclose(j, bl.nactvar);
this.patchtohere(j); this.patchtohere(j);
} }
if (bl.isloop) if (bl.isloop)
ls.breaklabel(); /* close pending breaks */ ls.breaklabel(); /* close pending breaks */
this.bl = bl.previous; this.bl = bl.previous;
this.removevars(bl.nactvar); this.removevars(bl.nactvar);
_assert(bl.nactvar == this.nactvar); _assert(bl.nactvar == this.nactvar);
this.freereg = this.nactvar; /* free registers */ this.freereg = this.nactvar; /* free registers */
ls.dyd.n_label = bl.firstlabel; /* remove local labels */ ls.dyd.n_label = bl.firstlabel; /* remove local labels */
if (bl.previous != null) /* inner block? */ if (bl.previous != null) /* inner block? */
this.movegotosout(bl); /* update pending gotos to outer block */ this.movegotosout(bl); /* update pending gotos to outer block */
else if (bl.firstgoto < ls.dyd.n_gt) /* pending gotos in outer block? */ else if (bl.firstgoto < ls.dyd.n_gt) /* pending gotos in outer block? */
ls.undefgoto(ls.dyd.gt[bl.firstgoto]); /* error */ ls.undefgoto(ls.dyd.gt[bl.firstgoto]); /* error */
} }
void closelistfield(ConsControl cc) { void closelistfield(ConsControl cc) {
@@ -255,50 +248,49 @@ public class FuncState extends Constants {
return ((k) == LexState.VCALL || (k) == LexState.VVARARG); return ((k) == LexState.VCALL || (k) == LexState.VVARARG);
} }
void lastlistfield (ConsControl cc) { void lastlistfield(ConsControl cc) {
if (cc.tostore == 0) return; if (cc.tostore == 0)
return;
if (hasmultret(cc.v.k)) { if (hasmultret(cc.v.k)) {
this.setmultret(cc.v); this.setmultret(cc.v);
this.setlist(cc.t.u.info, cc.na, LUA_MULTRET); this.setlist(cc.t.u.info, cc.na, LUA_MULTRET);
cc.na--; /** do not count last expression (unknown number of elements) */ cc.na--; /**
} * do not count last expression (unknown number of
else { * elements)
if (cc.v.k != LexState.VVOID) */
this.exp2nextreg(cc.v); } else {
this.setlist(cc.t.u.info, cc.na, cc.tostore); if (cc.v.k != LexState.VVOID)
this.exp2nextreg(cc.v);
this.setlist(cc.t.u.info, cc.na, cc.tostore);
} }
} }
// ============================================================= // =============================================================
// from lcode.c // from lcode.c
// ============================================================= // =============================================================
void nil(int from, int n) { void nil(int from, int n) {
int l = from + n - 1; /* last register to set nil */ int l = from+n-1; /* last register to set nil */
if (this.pc > this.lasttarget && pc > 0) { /* no jumps to current position? */ if (this.pc > this.lasttarget && pc > 0) { /* no jumps to current position? */
final int previous_code = f.code[pc - 1]; final int previous_code = f.code[pc-1];
if (GET_OPCODE(previous_code) == OP_LOADNIL) { if (GET_OPCODE(previous_code) == OP_LOADNIL) {
int pfrom = GETARG_A(previous_code); int pfrom = GETARG_A(previous_code);
int pl = pfrom + GETARG_B(previous_code); int pl = pfrom+GETARG_B(previous_code);
if ((pfrom <= from && from <= pl + 1) if ((pfrom <= from && from <= pl+1) || (from <= pfrom && pfrom <= l+1)) { /* can connect both? */
|| (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */
if (pfrom < from) if (pfrom < from)
from = pfrom; /* from = min(from, pfrom) */ from = pfrom; /* from = min(from, pfrom) */
if (pl > l) if (pl > l)
l = pl; /* l = max(l, pl) */ l = pl; /* l = max(l, pl) */
InstructionPtr previous = new InstructionPtr(this.f.code, this.pc - 1); InstructionPtr previous = new InstructionPtr(this.f.code, this.pc-1);
SETARG_A(previous, from); SETARG_A(previous, from);
SETARG_B(previous, l - from); SETARG_B(previous, l-from);
return; return;
} }
} /* else go through */ } /* else go through */
} }
this.codeABC(OP_LOADNIL, from, n - 1, 0); this.codeABC(OP_LOADNIL, from, n-1, 0);
} }
int jump() { int jump() {
int jpc = this.jpc.i; /* save list of jumps to here */ int jpc = this.jpc.i; /* save list of jumps to here */
this.jpc.i = LexState.NO_JUMP; this.jpc.i = LexState.NO_JUMP;
@@ -308,24 +300,23 @@ public class FuncState extends Constants {
} }
void ret(int first, int nret) { void ret(int first, int nret) {
this.codeABC(OP_RETURN, first, nret + 1, 0); this.codeABC(OP_RETURN, first, nret+1, 0);
} }
int condjump(int /* OpCode */op, int A, int B, int C) { int condjump(int /* OpCode */ op, int A, int B, int C) {
this.codeABC(op, A, B, C); this.codeABC(op, A, B, C);
return this.jump(); return this.jump();
} }
void fixjump(int pc, int dest) { void fixjump(int pc, int dest) {
InstructionPtr jmp = new InstructionPtr(this.f.code, pc); InstructionPtr jmp = new InstructionPtr(this.f.code, pc);
int offset = dest - (pc + 1); int offset = dest-(pc+1);
_assert (dest != LexState.NO_JUMP); _assert(dest != LexState.NO_JUMP);
if (Math.abs(offset) > MAXARG_sBx) if (Math.abs(offset) > MAXARG_sBx)
ls.syntaxerror("control structure too long"); ls.syntaxerror("control structure too long");
SETARG_sBx(jmp, offset); SETARG_sBx(jmp, offset);
} }
/* /*
* * returns current `pc' and marks it as a jump target (to avoid wrong * * * returns current `pc' and marks it as a jump target (to avoid wrong *
* optimizations with consecutive instructions not in the same basic block). * optimizations with consecutive instructions not in the same basic block).
@@ -335,7 +326,6 @@ public class FuncState extends Constants {
return this.pc; return this.pc;
} }
int getjump(int pc) { int getjump(int pc) {
int offset = GETARG_sBx(this.f.code[pc]); int offset = GETARG_sBx(this.f.code[pc]);
/* point to itself represents end of list */ /* point to itself represents end of list */
@@ -344,19 +334,17 @@ public class FuncState extends Constants {
return LexState.NO_JUMP; return LexState.NO_JUMP;
else else
/* turn offset into absolute position */ /* turn offset into absolute position */
return (pc + 1) + offset; return (pc+1)+offset;
} }
InstructionPtr getjumpcontrol(int pc) { InstructionPtr getjumpcontrol(int pc) {
InstructionPtr pi = new InstructionPtr(this.f.code, pc); InstructionPtr pi = new InstructionPtr(this.f.code, pc);
if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1]))) if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx-1])))
return new InstructionPtr(pi.code, pi.idx - 1); return new InstructionPtr(pi.code, pi.idx-1);
else else
return pi; return pi;
} }
/* /*
* * check whether list has any jump that do not produce a value * (or * * check whether list has any jump that do not produce a value * (or
* produce an inverted value) * produce an inverted value)
@@ -370,7 +358,6 @@ public class FuncState extends Constants {
return false; /* not found */ return false; /* not found */
} }
boolean patchtestreg(int node, int reg) { boolean patchtestreg(int node, int reg) {
InstructionPtr i = this.getjumpcontrol(node); InstructionPtr i = this.getjumpcontrol(node);
if (GET_OPCODE(i.get()) != OP_TESTSET) if (GET_OPCODE(i.get()) != OP_TESTSET)
@@ -385,14 +372,13 @@ public class FuncState extends Constants {
return true; return true;
} }
void removevalues(int list) { void removevalues(int list) {
for (; list != LexState.NO_JUMP; list = this.getjump(list)) for (; list != LexState.NO_JUMP; list = this.getjump(list))
this.patchtestreg(list, NO_REG); this.patchtestreg(list, NO_REG);
} }
void patchlistaux(int list, int vtarget, int reg, int dtarget) { void patchlistaux(int list, int vtarget, int reg, int dtarget) {
while (list != LexState.NO_JUMP) { while ( list != LexState.NO_JUMP ) {
int next = this.getjump(list); int next = this.getjump(list);
if (this.patchtestreg(list, reg)) if (this.patchtestreg(list, reg))
this.fixjump(list, vtarget); this.fixjump(list, vtarget);
@@ -411,17 +397,17 @@ public class FuncState extends Constants {
if (target == this.pc) if (target == this.pc)
this.patchtohere(list); this.patchtohere(list);
else { else {
_assert (target < this.pc); _assert(target < this.pc);
this.patchlistaux(list, target, NO_REG, target); this.patchlistaux(list, target, NO_REG, target);
} }
} }
void patchclose(int list, int level) { void patchclose(int list, int level) {
level++; /* argument is +1 to reserve 0 as non-op */ level++; /* argument is +1 to reserve 0 as non-op */
while (list != LexState.NO_JUMP) { while ( list != LexState.NO_JUMP ) {
int next = getjump(list); int next = getjump(list);
_assert(GET_OPCODE(f.code[list]) == OP_JMP _assert(
&& (GETARG_A(f.code[list]) == 0 || GETARG_A(f.code[list]) >= level)); GET_OPCODE(f.code[list]) == OP_JMP && (GETARG_A(f.code[list]) == 0 || GETARG_A(f.code[list]) >= level));
SETARG_A(f.code, list, level); SETARG_A(f.code, list, level);
list = next; list = next;
} }
@@ -440,7 +426,7 @@ public class FuncState extends Constants {
else { else {
int list = l1.i; int list = l1.i;
int next; int next;
while ((next = this.getjump(list)) != LexState.NO_JUMP) while ( (next = this.getjump(list)) != LexState.NO_JUMP )
/* find last element */ /* find last element */
list = next; list = next;
this.fixjump(list, l2); this.fixjump(list, l2);
@@ -448,7 +434,7 @@ public class FuncState extends Constants {
} }
void checkstack(int n) { void checkstack(int n) {
int newstack = this.freereg + n; int newstack = this.freereg+n;
if (newstack > this.f.maxstacksize) { if (newstack > this.f.maxstacksize) {
if (newstack >= MAXSTACK) if (newstack >= MAXSTACK)
ls.syntaxerror("function or expression too complex"); ls.syntaxerror("function or expression too complex");
@@ -464,7 +450,7 @@ public class FuncState extends Constants {
void freereg(int reg) { void freereg(int reg) {
if (!ISK(reg) && reg >= this.nactvar) { if (!ISK(reg) && reg >= this.nactvar) {
this.freereg--; this.freereg--;
_assert (reg == this.freereg); _assert(reg == this.freereg);
} }
} }
@@ -472,6 +458,7 @@ public class FuncState extends Constants {
if (e.k == LexState.VNONRELOC) if (e.k == LexState.VNONRELOC)
this.freereg(e.u.info); this.freereg(e.u.info);
} }
int addk(LuaValue v) { int addk(LuaValue v) {
if (this.h == null) { if (this.h == null) {
this.h = new Hashtable(); this.h = new Hashtable();
@@ -481,8 +468,8 @@ public class FuncState extends Constants {
final int idx = this.nk; final int idx = this.nk;
this.h.put(v, new Integer(idx)); this.h.put(v, new Integer(idx));
final Prototype f = this.f; final Prototype f = this.f;
if (f.k == null || nk + 1 >= f.k.length) if (f.k == null || nk+1 >= f.k.length)
f.k = realloc( f.k, nk*2 + 1 ); f.k = realloc(f.k, nk*2+1);
f.k[this.nk++] = v; f.k[this.nk++] = v;
return idx; return idx;
} }
@@ -492,17 +479,17 @@ public class FuncState extends Constants {
} }
int numberK(LuaValue r) { int numberK(LuaValue r) {
if ( r instanceof LuaDouble ) { if (r instanceof LuaDouble) {
double d = r.todouble(); double d = r.todouble();
int i = (int) d; int i = (int) d;
if ( d == (double) i ) if (d == (double) i)
r = LuaInteger.valueOf(i); r = LuaInteger.valueOf(i);
} }
return this.addk(r); return this.addk(r);
} }
int boolK(boolean b) { int boolK(boolean b) {
return this.addk((b ? LuaValue.TRUE : LuaValue.FALSE)); return this.addk((b? LuaValue.TRUE: LuaValue.FALSE));
} }
int nilK() { int nilK() {
@@ -511,9 +498,9 @@ public class FuncState extends Constants {
void setreturns(expdesc e, int nresults) { void setreturns(expdesc e, int nresults) {
if (e.k == LexState.VCALL) { /* expression is an open function call? */ if (e.k == LexState.VCALL) { /* expression is an open function call? */
SETARG_C(this.getcodePtr(e), nresults + 1); SETARG_C(this.getcodePtr(e), nresults+1);
} else if (e.k == LexState.VVARARG) { } else if (e.k == LexState.VVARARG) {
SETARG_B(this.getcodePtr(e), nresults + 1); SETARG_B(this.getcodePtr(e), nresults+1);
SETARG_A(this.getcodePtr(e), this.freereg); SETARG_A(this.getcodePtr(e), this.freereg);
this.reserveregs(1); this.reserveregs(1);
} }
@@ -541,9 +528,9 @@ public class FuncState extends Constants {
break; break;
} }
case LexState.VINDEXED: { case LexState.VINDEXED: {
int op = OP_GETTABUP; /* assume 't' is in an upvalue */ int op = OP_GETTABUP; /* assume 't' is in an upvalue */
this.freereg(e.u.ind_idx); this.freereg(e.u.ind_idx);
if (e.u.ind_vt == LexState.VLOCAL) { /* 't' is in a register? */ if (e.u.ind_vt == LexState.VLOCAL) { /* 't' is in a register? */
this.freereg(e.u.ind_t); this.freereg(e.u.ind_t);
op = OP_GETTABLE; op = OP_GETTABLE;
} }
@@ -575,8 +562,7 @@ public class FuncState extends Constants {
} }
case LexState.VFALSE: case LexState.VFALSE:
case LexState.VTRUE: { case LexState.VTRUE: {
this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE ? 1 : 0), this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE? 1: 0), 0);
0);
break; break;
} }
case LexState.VK: { case LexState.VK: {
@@ -598,7 +584,7 @@ public class FuncState extends Constants {
break; break;
} }
default: { default: {
_assert (e.k == LexState.VVOID || e.k == LexState.VJMP); _assert(e.k == LexState.VVOID || e.k == LexState.VJMP);
return; /* nothing to do... */ return; /* nothing to do... */
} }
} }
@@ -609,7 +595,7 @@ public class FuncState extends Constants {
void discharge2anyreg(expdesc e) { void discharge2anyreg(expdesc e) {
if (e.k != LexState.VNONRELOC) { if (e.k != LexState.VNONRELOC) {
this.reserveregs(1); this.reserveregs(1);
this.discharge2reg(e, this.freereg - 1); this.discharge2reg(e, this.freereg-1);
} }
} }
@@ -622,8 +608,7 @@ public class FuncState extends Constants {
int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */ int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */
int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */ int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */
if (this.need_value(e.t.i) || this.need_value(e.f.i)) { if (this.need_value(e.t.i) || this.need_value(e.f.i)) {
int fj = (e.k == LexState.VJMP) ? LexState.NO_JUMP : this int fj = (e.k == LexState.VJMP)? LexState.NO_JUMP: this.jump();
.jump();
p_f = this.code_label(reg, 0, 1); p_f = this.code_label(reg, 0, 1);
p_t = this.code_label(reg, 1, 0); p_t = this.code_label(reg, 1, 0);
this.patchtohere(fj); this.patchtohere(fj);
@@ -641,7 +626,7 @@ public class FuncState extends Constants {
this.dischargevars(e); this.dischargevars(e);
this.freeexp(e); this.freeexp(e);
this.reserveregs(1); this.reserveregs(1);
this.exp2reg(e, this.freereg - 1); this.exp2reg(e, this.freereg-1);
} }
int exp2anyreg(expdesc e) { int exp2anyreg(expdesc e) {
@@ -658,7 +643,7 @@ public class FuncState extends Constants {
return e.u.info; return e.u.info;
} }
void exp2anyregup (expdesc e) { void exp2anyregup(expdesc e) {
if (e.k != LexState.VUPVAL || e.hasjumps()) if (e.k != LexState.VUPVAL || e.hasjumps())
exp2anyreg(e); exp2anyreg(e);
} }
@@ -677,17 +662,16 @@ public class FuncState extends Constants {
case LexState.VFALSE: case LexState.VFALSE:
case LexState.VNIL: { case LexState.VNIL: {
if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e.u.info = (e.k == LexState.VNIL) ? this.nilK() e.u.info = (e.k == LexState.VNIL)? this.nilK(): this.boolK((e.k == LexState.VTRUE));
: this.boolK((e.k == LexState.VTRUE));
e.k = LexState.VK; e.k = LexState.VK;
return RKASK(e.u.info); return RKASK(e.u.info);
} else } else
break; break;
} }
case LexState.VKNUM: { case LexState.VKNUM: {
e.u.info = this.numberK(e.u.nval()); e.u.info = this.numberK(e.u.nval());
e.k = LexState.VK; e.k = LexState.VK;
/* go through */ /* go through */
} }
case LexState.VK: { case LexState.VK: {
if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */ if (e.u.info <= MAXINDEXRK) /* constant fit in argC? */
@@ -715,13 +699,13 @@ public class FuncState extends Constants {
break; break;
} }
case LexState.VINDEXED: { case LexState.VINDEXED: {
int op = (var.u.ind_vt == LexState.VLOCAL) ? OP_SETTABLE : OP_SETTABUP; int op = (var.u.ind_vt == LexState.VLOCAL)? OP_SETTABLE: OP_SETTABUP;
int e = this.exp2RK(ex); int e = this.exp2RK(ex);
this.codeABC(op, var.u.ind_t, var.u.ind_idx, e); this.codeABC(op, var.u.ind_t, var.u.ind_idx, e);
break; break;
} }
default: { default: {
_assert (false); /* invalid var kind to store */ _assert(false); /* invalid var kind to store */
break; break;
} }
} }
@@ -742,12 +726,11 @@ public class FuncState extends Constants {
void invertjump(expdesc e) { void invertjump(expdesc e) {
InstructionPtr pc = this.getjumpcontrol(e.u.info); InstructionPtr pc = this.getjumpcontrol(e.u.info);
_assert (testTMode(GET_OPCODE(pc.get())) _assert(testTMode(GET_OPCODE(pc.get())) && GET_OPCODE(pc.get()) != OP_TESTSET
&& GET_OPCODE(pc.get()) != OP_TESTSET && Lua && Lua.GET_OPCODE(pc.get()) != OP_TEST);
.GET_OPCODE(pc.get()) != OP_TEST);
// SETARG_A(pc, !(GETARG_A(pc.get()))); // SETARG_A(pc, !(GETARG_A(pc.get())));
int a = GETARG_A(pc.get()); int a = GETARG_A(pc.get());
int nota = (a!=0? 0: 1); int nota = (a != 0? 0: 1);
SETARG_A(pc, nota); SETARG_A(pc, nota);
} }
@@ -756,7 +739,7 @@ public class FuncState extends Constants {
int ie = this.getcode(e); int ie = this.getcode(e);
if (GET_OPCODE(ie) == OP_NOT) { if (GET_OPCODE(ie) == OP_NOT) {
this.pc--; /* remove previous OP_NOT */ this.pc--; /* remove previous OP_NOT */
return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond!=0? 0: 1)); return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond != 0? 0: 1));
} }
/* else go through */ /* else go through */
} }
@@ -840,7 +823,7 @@ public class FuncState extends Constants {
break; break;
} }
default: { default: {
_assert (false); /* cannot happen */ _assert(false); /* cannot happen */
break; break;
} }
} }
@@ -862,7 +845,7 @@ public class FuncState extends Constants {
t.u.ind_t = (short) t.u.info; t.u.ind_t = (short) t.u.info;
t.u.ind_idx = (short) this.exp2RK(k); t.u.ind_idx = (short) this.exp2RK(k);
LuaC._assert(t.k == LexState.VUPVAL || vkisinreg(t.k)); LuaC._assert(t.k == LexState.VUPVAL || vkisinreg(t.k));
t.u.ind_vt = (short) ((t.k == LexState.VUPVAL) ? LexState.VUPVAL : LexState.VLOCAL); t.u.ind_vt = (short) ((t.k == LexState.VUPVAL)? LexState.VUPVAL: LexState.VLOCAL);
t.k = LexState.VINDEXED; t.k = LexState.VINDEXED;
} }
@@ -871,7 +854,7 @@ public class FuncState extends Constants {
if (!e1.isnumeral() || !e2.isnumeral()) if (!e1.isnumeral() || !e2.isnumeral())
return false; return false;
if ((op == OP_DIV || op == OP_MOD) && e2.u.nval().eq_b(LuaValue.ZERO)) if ((op == OP_DIV || op == OP_MOD) && e2.u.nval().eq_b(LuaValue.ZERO))
return false; /* do not attempt to divide by 0 */ return false; /* do not attempt to divide by 0 */
v1 = e1.u.nval(); v1 = e1.u.nval();
v2 = e2.u.nval(); v2 = e2.u.nval();
switch (op) { switch (op) {
@@ -901,13 +884,13 @@ public class FuncState extends Constants {
// break; // break;
return false; /* no constant folding for 'len' */ return false; /* no constant folding for 'len' */
default: default:
_assert (false); _assert(false);
r = null; r = null;
break; break;
} }
if ( Double.isNaN(r.todouble()) ) if (Double.isNaN(r.todouble()))
return false; /* do not attempt to produce NaN */ return false; /* do not attempt to produce NaN */
e1.u.setNval( r ); e1.u.setNval(r);
return true; return true;
} }
@@ -915,8 +898,7 @@ public class FuncState extends Constants {
if (constfolding(op, e1, e2)) if (constfolding(op, e1, e2))
return; return;
else { else {
int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) int o2 = (op != OP_UNM && op != OP_LEN)? this.exp2RK(e2): 0;
: 0;
int o1 = this.exp2RK(e1); int o1 = this.exp2RK(e1);
if (o1 > o2) { if (o1 > o2) {
this.freeexp(e1); this.freeexp(e1);
@@ -931,7 +913,7 @@ public class FuncState extends Constants {
} }
} }
void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) { void codecomp(int /* OpCode */ op, int cond, expdesc e1, expdesc e2) {
int o1 = this.exp2RK(e1); int o1 = this.exp2RK(e1);
int o2 = this.exp2RK(e2); int o2 = this.exp2RK(e2);
this.freeexp(e2); this.freeexp(e2);
@@ -947,17 +929,17 @@ public class FuncState extends Constants {
e1.k = LexState.VJMP; e1.k = LexState.VJMP;
} }
void prefix(int /* UnOpr */op, expdesc e, int line) { void prefix(int /* UnOpr */ op, expdesc e, int line) {
expdesc e2 = new expdesc(); expdesc e2 = new expdesc();
e2.init(LexState.VKNUM, 0); e2.init(LexState.VKNUM, 0);
switch (op) { switch (op) {
case LexState.OPR_MINUS: { case LexState.OPR_MINUS: {
if (e.isnumeral()) /* minus constant? */ if (e.isnumeral()) /* minus constant? */
e.u.setNval(e.u.nval().neg()); /* fold it */ e.u.setNval(e.u.nval().neg()); /* fold it */
else { else {
this.exp2anyreg(e); this.exp2anyreg(e);
this.codearith(OP_UNM, e, e2, line); this.codearith(OP_UNM, e, e2, line);
} }
break; break;
} }
case LexState.OPR_NOT: case LexState.OPR_NOT:
@@ -969,11 +951,11 @@ public class FuncState extends Constants {
break; break;
} }
default: default:
_assert (false); _assert(false);
} }
} }
void infix(int /* BinOpr */op, expdesc v) { void infix(int /* BinOpr */ op, expdesc v) {
switch (op) { switch (op) {
case LexState.OPR_AND: { case LexState.OPR_AND: {
this.goiftrue(v); this.goiftrue(v);
@@ -1004,11 +986,10 @@ public class FuncState extends Constants {
} }
} }
void posfix(int op, expdesc e1, expdesc e2, int line) { void posfix(int op, expdesc e1, expdesc e2, int line) {
switch (op) { switch (op) {
case LexState.OPR_AND: { case LexState.OPR_AND: {
_assert (e1.t.i == LexState.NO_JUMP); /* list must be closed */ _assert(e1.t.i == LexState.NO_JUMP); /* list must be closed */
this.dischargevars(e2); this.dischargevars(e2);
this.concat(e2.f, e1.f.i); this.concat(e2.f, e1.f.i);
// *e1 = *e2; // *e1 = *e2;
@@ -1016,7 +997,7 @@ public class FuncState extends Constants {
break; break;
} }
case LexState.OPR_OR: { case LexState.OPR_OR: {
_assert (e1.f.i == LexState.NO_JUMP); /* list must be closed */ _assert(e1.f.i == LexState.NO_JUMP); /* list must be closed */
this.dischargevars(e2); this.dischargevars(e2);
this.concat(e2.t, e1.t.i); this.concat(e2.t, e1.t.i);
// *e1 = *e2; // *e1 = *e2;
@@ -1025,9 +1006,8 @@ public class FuncState extends Constants {
} }
case LexState.OPR_CONCAT: { case LexState.OPR_CONCAT: {
this.exp2val(e2); this.exp2val(e2);
if (e2.k == LexState.VRELOCABLE if (e2.k == LexState.VRELOCABLE && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) {
&& GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { _assert(e1.u.info == GETARG_B(this.getcode(e2))-1);
_assert (e1.u.info == GETARG_B(this.getcode(e2)) - 1);
this.freeexp(e1); this.freeexp(e1);
SETARG_B(this.getcodePtr(e2), e1.u.info); SETARG_B(this.getcodePtr(e2), e1.u.info);
e1.k = LexState.VRELOCABLE; e1.k = LexState.VRELOCABLE;
@@ -1075,44 +1055,39 @@ public class FuncState extends Constants {
this.codecomp(OP_LE, 0, e1, e2); this.codecomp(OP_LE, 0, e1, e2);
break; break;
default: default:
_assert (false); _assert(false);
} }
} }
void fixline(int line) { void fixline(int line) {
this.f.lineinfo[this.pc - 1] = line; this.f.lineinfo[this.pc-1] = line;
} }
int code(int instruction, int line) { int code(int instruction, int line) {
Prototype f = this.f; Prototype f = this.f;
this.dischargejpc(); /* `pc' will change */ this.dischargejpc(); /* `pc' will change */
/* put new instruction in code array */ /* put new instruction in code array */
if (f.code == null || this.pc + 1 > f.code.length) if (f.code == null || this.pc+1 > f.code.length)
f.code = LuaC.realloc(f.code, this.pc * 2 + 1); f.code = LuaC.realloc(f.code, this.pc*2+1);
f.code[this.pc] = instruction; f.code[this.pc] = instruction;
/* save corresponding line information */ /* save corresponding line information */
if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length) if (f.lineinfo == null || this.pc+1 > f.lineinfo.length)
f.lineinfo = LuaC.realloc(f.lineinfo, f.lineinfo = LuaC.realloc(f.lineinfo, this.pc*2+1);
this.pc * 2 + 1);
f.lineinfo[this.pc] = line; f.lineinfo[this.pc] = line;
return this.pc++; return this.pc++;
} }
int codeABC(int o, int a, int b, int c) { int codeABC(int o, int a, int b, int c) {
_assert (getOpMode(o) == iABC); _assert(getOpMode(o) == iABC);
_assert (getBMode(o) != OpArgN || b == 0); _assert(getBMode(o) != OpArgN || b == 0);
_assert (getCMode(o) != OpArgN || c == 0); _assert(getCMode(o) != OpArgN || c == 0);
return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline); return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline);
} }
int codeABx(int o, int a, int bc) { int codeABx(int o, int a, int bc) {
_assert (getOpMode(o) == iABx || getOpMode(o) == iAsBx); _assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
_assert (getCMode(o) == OpArgN); _assert(getCMode(o) == OpArgN);
_assert (bc >= 0 && bc <= Lua.MAXARG_Bx); _assert(bc >= 0 && bc <= Lua.MAXARG_Bx);
return this.code(CREATE_ABx(o, a, bc), this.ls.lastline); return this.code(CREATE_ABx(o, a, bc), this.ls.lastline);
} }
@@ -1132,16 +1107,16 @@ public class FuncState extends Constants {
} }
void setlist(int base, int nelems, int tostore) { void setlist(int base, int nelems, int tostore) {
int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; int c = (nelems-1)/LFIELDS_PER_FLUSH+1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore; int b = (tostore == LUA_MULTRET)? 0: tostore;
_assert (tostore != 0); _assert(tostore != 0);
if (c <= MAXARG_C) if (c <= MAXARG_C)
this.codeABC(OP_SETLIST, base, b, c); this.codeABC(OP_SETLIST, base, b, c);
else { else {
this.codeABC(OP_SETLIST, base, b, 0); this.codeABC(OP_SETLIST, base, b, 0);
this.code(c, this.ls.lastline); this.code(c, this.ls.lastline);
} }
this.freereg = (short) (base + 1); /* free registers with list values */ this.freereg = (short) (base+1); /* free registers with list values */
} }
} }

View File

@@ -23,15 +23,18 @@ package org.luaj.vm2.compiler;
class InstructionPtr { class InstructionPtr {
final int[] code; final int[] code;
final int idx; final int idx;
InstructionPtr(int[] code, int idx ) {
InstructionPtr(int[] code, int idx) {
this.code = code; this.code = code;
this.idx = idx; this.idx = idx;
} }
int get() { int get() {
return code[idx]; return code[idx];
} }
void set(int value) { void set(int value) {
code[idx] = value; code[idx] = value;
} }
} }

View File

@@ -23,8 +23,10 @@ package org.luaj.vm2.compiler;
public class IntPtr { public class IntPtr {
int i; int i;
IntPtr() { IntPtr() {
} }
IntPtr(int value) { IntPtr(int value) {
this.i = value; this.i = value;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -37,30 +37,37 @@ import org.luaj.vm2.lib.BaseLib;
* Compiler for Lua. * Compiler for Lua.
* *
* <p> * <p>
* Compiles lua source files into lua bytecode within a {@link Prototype}, * Compiles lua source files into lua bytecode within a {@link Prototype}, loads
* loads lua binary files directly into a {@link Prototype}, * lua binary files directly into a {@link Prototype}, and optionaly
* and optionaly instantiates a {@link LuaClosure} around the result * instantiates a {@link LuaClosure} around the result using a user-supplied
* using a user-supplied environment. * environment.
* *
* <p> * <p>
* Implements the {@link org.luaj.vm2.Globals.Compiler} interface for loading * Implements the {@link org.luaj.vm2.Globals.Compiler} interface for loading
* initialized chunks, which is an interface common to * initialized chunks, which is an interface common to lua bytecode compiling
* lua bytecode compiling and java bytecode compiling. * and java bytecode compiling.
* *
* <p> * <p>
* The {@link LuaC} compiler is installed by default by both the * The {@link LuaC} compiler is installed by default by both the
* {@link org.luaj.vm2.lib.jse.JsePlatform} and {@link org.luaj.vm2.lib.jme.JmePlatform} classes, * {@link org.luaj.vm2.lib.jse.JsePlatform} and
* so in the following example, the default {@link LuaC} compiler * {@link org.luaj.vm2.lib.jme.JmePlatform} classes, so in the following
* will be used: * example, the default {@link LuaC} compiler will be used:
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* globals.load(new StringReader("print 'hello'"), "main.lua" ).call(); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
* *
* To load the LuaC compiler manually, use the install method: * To load the LuaC compiler manually, use the install method:
* <pre> {@code *
* <pre>
* {@code
* LuaC.install(globals); * LuaC.install(globals);
* } </pre> * }
* </pre>
* *
* @see #install(Globals) * @see #install(Globals)
* @see Globals#compiler * @see Globals#compiler
@@ -76,10 +83,11 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
/** A sharable instance of the LuaC compiler. */ /** A sharable instance of the LuaC compiler. */
public static final LuaC instance = new LuaC(); public static final LuaC instance = new LuaC();
/** Install the compiler so that LoadState will first /**
* try to use it when handed bytes that are * Install the compiler so that LoadState will first try to use it when
* not already a compiled lua chunk. * handed bytes that are not already a compiled lua chunk.
*
* @param globals the Globals into which this is to be installed. * @param globals the Globals into which this is to be installed.
*/ */
public static void install(Globals globals) { public static void install(Globals globals) {
@@ -89,8 +97,11 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
protected LuaC() {} protected LuaC() {}
/** Compile lua source into a Prototype. /**
* @param stream InputStream representing the text source conforming to lua source syntax. * Compile lua source into a Prototype.
*
* @param stream InputStream representing the text source conforming to
* lua source syntax.
* @param chunkname String name of the chunk to use. * @param chunkname String name of the chunk to use.
* @return Prototype representing the lua chunk for this source. * @return Prototype representing the lua chunk for this source.
* @throws IOException * @throws IOException
@@ -103,55 +114,57 @@ public class LuaC extends Constants implements Globals.Compiler, Globals.Loader
return new LuaClosure(prototype, env); return new LuaClosure(prototype, env);
} }
/** @deprecated /**
* Use Globals.load(InputString, String, String) instead, * @deprecated Use Globals.load(InputString, String, String) instead, or
* or LuaC.compile(InputStream, String) and construct LuaClosure directly. * LuaC.compile(InputStream, String) and construct LuaClosure
* directly.
*/ */
public LuaValue load(InputStream stream, String chunkname, Globals globals) throws IOException { public LuaValue load(InputStream stream, String chunkname, Globals globals) throws IOException {
return new LuaClosure(compile(stream, chunkname), globals); return new LuaClosure(compile(stream, chunkname), globals);
} }
static class CompileState { static class CompileState {
int nCcalls = 0; int nCcalls = 0;
private Hashtable strings = new Hashtable(); private Hashtable strings = new Hashtable();
protected CompileState() {} protected CompileState() {}
/** Parse the input */ /** Parse the input */
Prototype luaY_parser(InputStream z, String name) throws IOException{ Prototype luaY_parser(InputStream z, String name) throws IOException {
LexState lexstate = new LexState(this, z); LexState lexstate = new LexState(this, z);
FuncState funcstate = new FuncState(); FuncState funcstate = new FuncState();
// lexstate.buff = buff; // lexstate.buff = buff;
lexstate.fs = funcstate; lexstate.fs = funcstate;
lexstate.setinput(this, z.read(), z, (LuaString) LuaValue.valueOf(name) ); lexstate.setinput(this, z.read(), z, (LuaString) LuaValue.valueOf(name));
/* main func. is always vararg */ /* main func. is always vararg */
funcstate.f = new Prototype(); funcstate.f = new Prototype();
funcstate.f.source = (LuaString) LuaValue.valueOf(name); funcstate.f.source = (LuaString) LuaValue.valueOf(name);
lexstate.mainfunc(funcstate); lexstate.mainfunc(funcstate);
LuaC._assert (funcstate.prev == null); LuaC._assert(funcstate.prev == null);
/* all scopes should be correctly finished */ /* all scopes should be correctly finished */
LuaC._assert (lexstate.dyd == null LuaC._assert(lexstate.dyd == null
|| (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0)); || (lexstate.dyd.n_actvar == 0 && lexstate.dyd.n_gt == 0 && lexstate.dyd.n_label == 0));
return funcstate.f; return funcstate.f;
} }
// look up and keep at most one copy of each string // look up and keep at most one copy of each string
public LuaString newTString(String s) { public LuaString newTString(String s) {
return cachedLuaString(LuaString.valueOf(s)); return cachedLuaString(LuaString.valueOf(s));
} }
// look up and keep at most one copy of each string // look up and keep at most one copy of each string
public LuaString newTString(LuaString s) { public LuaString newTString(LuaString s) {
return cachedLuaString(s); return cachedLuaString(s);
} }
public LuaString cachedLuaString(LuaString s) { public LuaString cachedLuaString(LuaString s) {
LuaString c = (LuaString) strings.get(s); LuaString c = (LuaString) strings.get(s);
if (c != null) if (c != null)
return c; return c;
strings.put(s, s); strings.put(s, s);
return s; return s;
} }
public String pushfstring(String string) { public String pushfstring(String string) {
return string; return string;
} }

View File

@@ -34,14 +34,14 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua basic library functions. * Subclass of {@link LibFunction} which implements the lua basic library
* functions.
* <p> * <p>
* This contains all library functions listed as "basic functions" in the lua documentation for JME. * This contains all library functions listed as "basic functions" in the lua
* The functions dofile and loadfile use the * documentation for JME. The functions dofile and loadfile use the
* {@link Globals#finder} instance to find resource files. * {@link Globals#finder} instance to find resource files. Since JME has no file
* Since JME has no file system by default, {@link BaseLib} implements * system by default, {@link BaseLib} implements {@link ResourceFinder} using
* {@link ResourceFinder} using {@link Class#getResource(String)}, * {@link Class#getResource(String)}, which is the closest equivalent on JME.
* which is the closest equivalent on JME.
* The default loader chain in {@link PackageLib} will use these as well. * The default loader chain in {@link PackageLib} will use these as well.
* <p> * <p>
* To use basic library functions that include a {@link ResourceFinder} based on * To use basic library functions that include a {@link ResourceFinder} based on
@@ -50,47 +50,60 @@ import org.luaj.vm2.Varargs;
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* globals.get("print").call(LuaValue.valueOf("hello, world")); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired, a minimal
* a minimal set of libraries could be loaded * set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* directly via {@link Globals#load(LuaValue)} using code such as: * using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.get("print").call(LuaValue.valueOf("hello, world")); * &#64;code
* } </pre> * Globals globals = new Globals();
* Doing so will ensure the library is properly initialized * globals.load(new JseBaseLib());
* and loaded into the globals table. * globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
*
* Doing so will ensure the library is properly initialized and loaded into the
* globals table.
* <p> * <p>
* This is a direct port of the corresponding library in C. * This is a direct port of the corresponding library in C.
*
* @see org.luaj.vm2.lib.jse.JseBaseLib * @see org.luaj.vm2.lib.jse.JseBaseLib
* @see ResourceFinder * @see ResourceFinder
* @see Globals#finder * @see Globals#finder
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib
* Reference</a>
*/ */
public class BaseLib extends TwoArgFunction implements ResourceFinder { public class BaseLib extends TwoArgFunction implements ResourceFinder {
Globals globals;
/** Perform one-time initialization on the library by adding base functions Globals globals;
/**
* Perform one-time initialization on the library by adding base functions
* to the supplied environment, and returning it as the return value. * to the supplied environment, and returning it as the return value.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance. * @param env the environment to load into, which must be a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
globals.finder = this; globals.finder = this;
globals.baselib = this; globals.baselib = this;
env.set( "_G", env ); env.set("_G", env);
env.set( "_VERSION", Lua._VERSION ); env.set("_VERSION", Lua._VERSION);
env.set("assert", new _assert()); env.set("assert", new _assert());
env.set("collectgarbage", new collectgarbage()); env.set("collectgarbage", new collectgarbage());
env.set("dofile", new dofile()); env.set("dofile", new dofile());
@@ -115,24 +128,24 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
env.set("next", next = new next()); env.set("next", next = new next());
env.set("pairs", new pairs(next)); env.set("pairs", new pairs(next));
env.set("ipairs", new ipairs()); env.set("ipairs", new ipairs());
return env; return env;
} }
/** ResourceFinder implementation /**
* ResourceFinder implementation
* *
* Tries to open the file as a resource, which can work for JSE and JME. * Tries to open the file as a resource, which can work for JSE and JME.
*/ */
public InputStream findResource(String filename) { public InputStream findResource(String filename) {
return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/"+filename); return getClass().getResourceAsStream(filename.startsWith("/")? filename: "/" + filename);
} }
// "assert", // ( v [,message] ) -> v, message | ERR // "assert", // ( v [,message] ) -> v, message | ERR
static final class _assert extends VarArgFunction { static final class _assert extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
if ( !args.arg1().toboolean() ) if (!args.arg1().toboolean())
error( args.narg()>1? args.optjstring(2,"assertion failed!"): "assertion failed!" ); error(args.narg() > 1? args.optjstring(2, "assertion failed!"): "assertion failed!");
return args; return args;
} }
} }
@@ -141,14 +154,14 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
static final class collectgarbage extends VarArgFunction { static final class collectgarbage extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
String s = args.optjstring(1, "collect"); String s = args.optjstring(1, "collect");
if ( "collect".equals(s) ) { if ("collect".equals(s)) {
System.gc(); System.gc();
return ZERO; return ZERO;
} else if ( "count".equals(s) ) { } else if ("count".equals(s)) {
Runtime rt = Runtime.getRuntime(); Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory(); long used = rt.totalMemory()-rt.freeMemory();
return varargsOf(valueOf(used/1024.), valueOf(used%1024)); return varargsOf(valueOf(used/1024.), valueOf(used%1024));
} else if ( "step".equals(s) ) { } else if ("step".equals(s)) {
System.gc(); System.gc();
return LuaValue.TRUE; return LuaValue.TRUE;
} else { } else {
@@ -163,9 +176,8 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil"); args.argcheck(args.isstring(1) || args.isnil(1), 1, "filename must be string or nil");
String filename = args.isstring(1)? args.tojstring(1): null; String filename = args.isstring(1)? args.tojstring(1): null;
Varargs v = filename == null? Varargs v = filename == null? loadStream(globals.STDIN, "=stdin", "bt", globals)
loadStream( globals.STDIN, "=stdin", "bt", globals ): : loadFile(args.checkjstring(1), "bt", globals);
loadFile( args.checkjstring(1), "bt", globals );
return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke(); return v.isnil(1)? error(v.tojstring(2)): v.arg1().invoke();
} }
} }
@@ -173,8 +185,10 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "error", // ( message [,level] ) -> ERR // "error", // ( message [,level] ) -> ERR
static final class error extends TwoArgFunction { static final class error extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
if (arg1.isnil()) throw new LuaError(NIL); if (arg1.isnil())
if (!arg1.isstring() || arg2.optint(1) == 0) throw new LuaError(arg1); throw new LuaError(NIL);
if (!arg1.isstring() || arg2.optint(1) == 0)
throw new LuaError(arg1);
throw new LuaError(arg1.tojstring(), arg2.optint(1)); throw new LuaError(arg1.tojstring(), arg2.optint(1));
} }
} }
@@ -184,23 +198,26 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public LuaValue call() { public LuaValue call() {
return argerror(1, "value expected"); return argerror(1, "value expected");
} }
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
LuaValue mt = arg.getmetatable(); LuaValue mt = arg.getmetatable();
return mt!=null? mt.rawget(METATABLE).optvalue(mt): NIL; return mt != null? mt.rawget(METATABLE).optvalue(mt): NIL;
} }
} }
// "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg // "load", // ( ld [, source [, mode [, env]]] ) -> chunk | nil, msg
final class load extends VarArgFunction { final class load extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue ld = args.arg1(); LuaValue ld = args.arg1();
if (!ld.isstring() && !ld.isfunction()) { if (!ld.isstring() && !ld.isfunction()) {
throw new LuaError("bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")"); throw new LuaError(
"bad argument #1 to 'load' (string or function expected, got " + ld.typename() + ")");
} }
String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)"); String source = args.optjstring(2, ld.isstring()? ld.tojstring(): "=(load)");
String mode = args.optjstring(3, "bt"); String mode = args.optjstring(3, "bt");
LuaValue env = args.optvalue(4, globals); LuaValue env = args.optvalue(4, globals);
return loadStream(ld.isstring()? ld.strvalue().toInputStream(): return loadStream(ld.isstring()? ld.strvalue().toInputStream(): new StringInputStream(ld.checkfunction()),
new StringInputStream(ld.checkfunction()), source, mode, env); source, mode, env);
} }
} }
@@ -211,12 +228,10 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
String filename = args.isstring(1)? args.tojstring(1): null; String filename = args.isstring(1)? args.tojstring(1): null;
String mode = args.optjstring(2, "bt"); String mode = args.optjstring(2, "bt");
LuaValue env = args.optvalue(3, globals); LuaValue env = args.optvalue(3, globals);
return filename == null? return filename == null? loadStream(globals.STDIN, "=stdin", mode, env): loadFile(filename, mode, env);
loadStream( globals.STDIN, "=stdin", mode, env ):
loadFile( filename, mode, env );
} }
} }
// "pcall", // (f, arg1, ...) -> status, result1, ... // "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction { final class pcall extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
@@ -225,12 +240,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
globals.debuglib.onCall(this); globals.debuglib.onCall(this);
try { try {
return varargsOf(TRUE, func.invoke(args.subargs(2))); return varargsOf(TRUE, func.invoke(args.subargs(2)));
} catch ( LuaError le ) { } catch (LuaError le) {
final LuaValue m = le.getMessageObject(); final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL); return varargsOf(FALSE, m != null? m: NIL);
} catch ( Exception e ) { } catch (Exception e) {
final String m = e.getMessage(); final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); return varargsOf(FALSE, valueOf(m != null? m: e.toString()));
} finally { } finally {
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn(); globals.debuglib.onReturn();
@@ -241,30 +256,34 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "print", // (...) -> void // "print", // (...) -> void
final class print extends VarArgFunction { final class print extends VarArgFunction {
final BaseLib baselib; final BaseLib baselib;
print(BaseLib baselib) { print(BaseLib baselib) {
this.baselib = baselib; this.baselib = baselib;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue tostring = globals.get("tostring"); LuaValue tostring = globals.get("tostring");
for ( int i=1, n=args.narg(); i<=n; i++ ) { for (int i = 1, n = args.narg(); i <= n; i++) {
if ( i>1 ) globals.STDOUT.print( '\t' ); if (i > 1)
LuaString s = tostring.call( args.arg(i) ).strvalue(); globals.STDOUT.print('\t');
LuaString s = tostring.call(args.arg(i)).strvalue();
globals.STDOUT.print(s.tojstring()); globals.STDOUT.print(s.tojstring());
} }
globals.STDOUT.print('\n'); globals.STDOUT.print('\n');
return NONE; return NONE;
} }
} }
// "rawequal", // (v1, v2) -> boolean // "rawequal", // (v1, v2) -> boolean
static final class rawequal extends LibFunction { static final class rawequal extends LibFunction {
public LuaValue call() { public LuaValue call() {
return argerror(1, "value expected"); return argerror(1, "value expected");
} }
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return argerror(2, "value expected"); return argerror(2, "value expected");
} }
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return valueOf(arg1.raweq(arg2)); return valueOf(arg1.raweq(arg2));
} }
@@ -275,12 +294,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return argerror(2, "value expected"); return argerror(2, "value expected");
} }
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return arg1.checktable().rawget(arg2); return arg1.checktable().rawget(arg2);
} }
} }
// "rawlen", // (v) -> value // "rawlen", // (v) -> value
static final class rawlen extends LibFunction { static final class rawlen extends LibFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
@@ -291,68 +310,73 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "rawset", // (table, index, value) -> table // "rawset", // (table, index, value) -> table
static final class rawset extends TableLibFunction { static final class rawset extends TableLibFunction {
public LuaValue call(LuaValue table) { public LuaValue call(LuaValue table) {
return argerror(2,"value expected"); return argerror(2, "value expected");
} }
public LuaValue call(LuaValue table, LuaValue index) { public LuaValue call(LuaValue table, LuaValue index) {
return argerror(3,"value expected"); return argerror(3, "value expected");
} }
public LuaValue call(LuaValue table, LuaValue index, LuaValue value) { public LuaValue call(LuaValue table, LuaValue index, LuaValue value) {
LuaTable t = table.checktable(); LuaTable t = table.checktable();
if (!index.isvalidkey()) argerror(2, "table index is nil"); if (!index.isvalidkey())
argerror(2, "table index is nil");
t.rawset(index, value); t.rawset(index, value);
return t; return t;
} }
} }
// "select", // (f, ...) -> value1, ... // "select", // (f, ...) -> value1, ...
static final class select extends VarArgFunction { static final class select extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int n = args.narg()-1; int n = args.narg()-1;
if ( args.arg1().equals(valueOf("#")) ) if (args.arg1().equals(valueOf("#")))
return valueOf(n); return valueOf(n);
int i = args.checkint(1); int i = args.checkint(1);
if ( i == 0 || i < -n ) if (i == 0 || i < -n)
argerror(1,"index out of range"); argerror(1, "index out of range");
return args.subargs(i<0? n+i+2: i+1); return args.subargs(i < 0? n+i+2: i+1);
} }
} }
// "setmetatable", // (table, metatable) -> table // "setmetatable", // (table, metatable) -> table
static final class setmetatable extends TableLibFunction { static final class setmetatable extends TableLibFunction {
public LuaValue call(LuaValue table) { public LuaValue call(LuaValue table) {
return argerror(2,"nil or table expected"); return argerror(2, "nil or table expected");
} }
public LuaValue call(LuaValue table, LuaValue metatable) { public LuaValue call(LuaValue table, LuaValue metatable) {
final LuaValue mt0 = table.checktable().getmetatable(); final LuaValue mt0 = table.checktable().getmetatable();
if ( mt0!=null && !mt0.rawget(METATABLE).isnil() ) if (mt0 != null && !mt0.rawget(METATABLE).isnil())
error("cannot change a protected metatable"); error("cannot change a protected metatable");
return table.setmetatable(metatable.isnil()? null: metatable.checktable()); return table.setmetatable(metatable.isnil()? null: metatable.checktable());
} }
} }
// "tonumber", // (e [,base]) -> value // "tonumber", // (e [,base]) -> value
static final class tonumber extends LibFunction { static final class tonumber extends LibFunction {
public LuaValue call(LuaValue e) { public LuaValue call(LuaValue e) {
return e.tonumber(); return e.tonumber();
} }
public LuaValue call(LuaValue e, LuaValue base) { public LuaValue call(LuaValue e, LuaValue base) {
if (base.isnil()) if (base.isnil())
return e.tonumber(); return e.tonumber();
final int b = base.checkint(); final int b = base.checkint();
if ( b < 2 || b > 36 ) if (b < 2 || b > 36)
argerror(2, "base out of range"); argerror(2, "base out of range");
return e.checkstring().tonumber(b); return e.checkstring().tonumber(b);
} }
} }
// "tostring", // (e) -> value // "tostring", // (e) -> value
static final class tostring extends LibFunction { static final class tostring extends LibFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
LuaValue h = arg.metatag(TOSTRING); LuaValue h = arg.metatag(TOSTRING);
if ( ! h.isnil() ) if (!h.isnil())
return h.call(arg); return h.call(arg);
LuaValue v = arg.tostring(); LuaValue v = arg.tostring();
if ( ! v.isnil() ) if (!v.isnil())
return v; return v;
return valueOf(arg.tojstring()); return valueOf(arg.tojstring());
} }
@@ -376,12 +400,12 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
globals.debuglib.onCall(this); globals.debuglib.onCall(this);
try { try {
return varargsOf(TRUE, args.arg1().invoke(args.subargs(3))); return varargsOf(TRUE, args.arg1().invoke(args.subargs(3)));
} catch ( LuaError le ) { } catch (LuaError le) {
final LuaValue m = le.getMessageObject(); final LuaValue m = le.getMessageObject();
return varargsOf(FALSE, m!=null? m: NIL); return varargsOf(FALSE, m != null? m: NIL);
} catch ( Exception e ) { } catch (Exception e) {
final String m = e.getMessage(); final String m = e.getMessage();
return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); return varargsOf(FALSE, valueOf(m != null? m: e.toString()));
} finally { } finally {
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
globals.debuglib.onReturn(); globals.debuglib.onReturn();
@@ -391,56 +415,60 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
} }
} }
} }
// "pairs" (t) -> iter-func, t, nil // "pairs" (t) -> iter-func, t, nil
static final class pairs extends VarArgFunction { static final class pairs extends VarArgFunction {
final next next; final next next;
pairs(next next) { pairs(next next) {
this.next = next; this.next = next;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return varargsOf( next, args.checktable(1), NIL ); return varargsOf(next, args.checktable(1), NIL);
} }
} }
// // "ipairs", // (t) -> iter-func, t, 0 // // "ipairs", // (t) -> iter-func, t, 0
static final class ipairs extends VarArgFunction { static final class ipairs extends VarArgFunction {
inext inext = new inext(); inext inext = new inext();
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return varargsOf( inext, args.checktable(1), ZERO ); return varargsOf(inext, args.checktable(1), ZERO);
} }
} }
// "next" ( table, [index] ) -> next-index, next-value // "next" ( table, [index] ) -> next-index, next-value
static final class next extends VarArgFunction { static final class next extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return args.checktable(1).next(args.arg(2)); return args.checktable(1).next(args.arg(2));
} }
} }
// "inext" ( table, [int-index] ) -> next-index, next-value // "inext" ( table, [int-index] ) -> next-index, next-value
static final class inext extends VarArgFunction { static final class inext extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return args.checktable(1).inext(args.arg(2)); return args.checktable(1).inext(args.arg(2));
} }
} }
/** /**
* Load from a named file, returning the chunk or nil,error of can't load * Load from a named file, returning the chunk or nil,error of can't load
*
* @param env * @param env
* @param mode * @param mode
* @return Varargs containing chunk, or NIL,error-text on error * @return Varargs containing chunk, or NIL,error-text on error
*/ */
public Varargs loadFile(String filename, String mode, LuaValue env) { public Varargs loadFile(String filename, String mode, LuaValue env) {
InputStream is = globals.finder.findResource(filename); InputStream is = globals.finder.findResource(filename);
if ( is == null ) if (is == null)
return varargsOf(NIL, valueOf("cannot open "+filename+": No such file or directory")); return varargsOf(NIL, valueOf("cannot open " + filename + ": No such file or directory"));
try { try {
return loadStream(is, "@"+filename, mode, env); return loadStream(is, "@" + filename, mode, env);
} finally { } finally {
try { try {
is.close(); is.close();
} catch ( Exception e ) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@@ -448,28 +476,29 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) { public Varargs loadStream(InputStream is, String chunkname, String mode, LuaValue env) {
try { try {
if ( is == null ) if (is == null)
return varargsOf(NIL, valueOf("not found: "+chunkname)); return varargsOf(NIL, valueOf("not found: " + chunkname));
return globals.load(is, chunkname, mode, env); return globals.load(is, chunkname, mode, env);
} catch (Exception e) { } catch (Exception e) {
return varargsOf(NIL, valueOf(e.getMessage())); return varargsOf(NIL, valueOf(e.getMessage()));
} }
} }
private static class StringInputStream extends InputStream { private static class StringInputStream extends InputStream {
final LuaValue func; final LuaValue func;
byte[] bytes; byte[] bytes;
int offset, remaining = 0; int offset, remaining = 0;
StringInputStream(LuaValue func) { StringInputStream(LuaValue func) {
this.func = func; this.func = func;
} }
public int read() throws IOException { public int read() throws IOException {
if ( remaining < 0 ) if (remaining < 0)
return -1; return -1;
if ( remaining == 0 ) { if (remaining == 0) {
LuaValue s = func.call(); LuaValue s = func.call();
if ( s.isnil() ) if (s.isnil())
return remaining = -1; return remaining = -1;
LuaString ls = s.strvalue(); LuaString ls = s.strvalue();
bytes = ls.m_bytes; bytes = ls.m_bytes;
@@ -479,7 +508,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
return -1; return -1;
} }
--remaining; --remaining;
return 0xFF&bytes[offset++]; return 0xFF & bytes[offset++];
} }
} }
} }

View File

@@ -26,68 +26,86 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of LibFunction that implements the Lua standard {@code bit32} library. * Subclass of LibFunction that implements the Lua standard {@code bit32}
* library.
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* <pre> {@code * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* Globals globals = JsePlatform.standardGlobals(); *
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) ); * <pre>
* } </pre> * {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new Bit32Lib()); * Globals globals = new Globals();
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* globals.load(new Bit32Lib());
* System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.7">Lua 5.2 Bitwise Operation Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.7">Lua 5.2 Bitwise
* Operation Lib Reference</a>
*/ */
public class Bit32Lib extends TwoArgFunction { public class Bit32Lib extends TwoArgFunction {
public Bit32Lib() { public Bit32Lib() {
} }
/** Perform one-time initialization on the library by creating a table /**
* containing the library functions, adding that table to the supplied environment, * Perform one-time initialization on the library by creating a table
* adding the table to package.loaded, and returning table as the return value. * containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance. * @param env the environment to load into, which must be a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable t = new LuaTable(); LuaTable t = new LuaTable();
bind(t, Bit32LibV.class, new String[] { bind(t, Bit32LibV.class, new String[] { "band", "bnot", "bor", "btest", "bxor", "extract", "replace" });
"band", "bnot", "bor", "btest", "bxor", "extract", "replace" bind(t, Bit32Lib2.class, new String[] { "arshift", "lrotate", "lshift", "rrotate", "rshift" });
});
bind(t, Bit32Lib2.class, new String[] {
"arshift", "lrotate", "lshift", "rrotate", "rshift"
});
env.set("bit32", t); env.set("bit32", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("bit32", t); if (!env.get("package").isnil())
env.get("package").get("loaded").set("bit32", t);
return t; return t;
} }
static final class Bit32LibV extends VarArgFunction { static final class Bit32LibV extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
switch ( opcode ) { switch (opcode) {
case 0: return Bit32Lib.band( args ); case 0:
case 1: return Bit32Lib.bnot( args ); return Bit32Lib.band(args);
case 2: return Bit32Lib.bor( args ); case 1:
case 3: return Bit32Lib.btest( args ); return Bit32Lib.bnot(args);
case 4: return Bit32Lib.bxor( args ); case 2:
return Bit32Lib.bor(args);
case 3:
return Bit32Lib.btest(args);
case 4:
return Bit32Lib.bxor(args);
case 5: case 5:
return Bit32Lib.extract( args.checkint(1), args.checkint(2), args.optint(3, 1) ); return Bit32Lib.extract(args.checkint(1), args.checkint(2), args.optint(3, 1));
case 6: case 6:
return Bit32Lib.replace( args.checkint(1), args.checkint(2), return Bit32Lib.replace(args.checkint(1), args.checkint(2), args.checkint(3), args.optint(4, 1));
args.checkint(3), args.optint(4, 1) );
} }
return NIL; return NIL;
} }
@@ -96,23 +114,28 @@ public class Bit32Lib extends TwoArgFunction {
static final class Bit32Lib2 extends TwoArgFunction { static final class Bit32Lib2 extends TwoArgFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
switch ( opcode ) { switch (opcode) {
case 0: return Bit32Lib.arshift(arg1.checkint(), arg2.checkint()); case 0:
case 1: return Bit32Lib.lrotate(arg1.checkint(), arg2.checkint()); return Bit32Lib.arshift(arg1.checkint(), arg2.checkint());
case 2: return Bit32Lib.lshift(arg1.checkint(), arg2.checkint()); case 1:
case 3: return Bit32Lib.rrotate(arg1.checkint(), arg2.checkint()); return Bit32Lib.lrotate(arg1.checkint(), arg2.checkint());
case 4: return Bit32Lib.rshift(arg1.checkint(), arg2.checkint()); case 2:
return Bit32Lib.lshift(arg1.checkint(), arg2.checkint());
case 3:
return Bit32Lib.rrotate(arg1.checkint(), arg2.checkint());
case 4:
return Bit32Lib.rshift(arg1.checkint(), arg2.checkint());
} }
return NIL; return NIL;
} }
} }
static LuaValue arshift(int x, int disp) { static LuaValue arshift(int x, int disp) {
if (disp >= 0) { if (disp >= 0) {
return bitsToValue(x >> disp); return bitsToValue(x>>disp);
} else { } else {
return bitsToValue(x << -disp); return bitsToValue(x<<-disp);
} }
} }
@@ -120,9 +143,9 @@ public class Bit32Lib extends TwoArgFunction {
if (disp >= 32 || disp <= -32) { if (disp >= 32 || disp <= -32) {
return ZERO; return ZERO;
} else if (disp >= 0) { } else if (disp >= 0) {
return bitsToValue(x >>> disp); return bitsToValue(x>>>disp);
} else { } else {
return bitsToValue(x << -disp); return bitsToValue(x<<-disp);
} }
} }
@@ -130,46 +153,46 @@ public class Bit32Lib extends TwoArgFunction {
if (disp >= 32 || disp <= -32) { if (disp >= 32 || disp <= -32) {
return ZERO; return ZERO;
} else if (disp >= 0) { } else if (disp >= 0) {
return bitsToValue(x << disp); return bitsToValue(x<<disp);
} else { } else {
return bitsToValue(x >>> -disp); return bitsToValue(x>>>-disp);
} }
} }
static Varargs band( Varargs args ) { static Varargs band(Varargs args) {
int result = -1; int result = -1;
for ( int i = 1; i <= args.narg(); i++ ) { for (int i = 1; i <= args.narg(); i++) {
result &= args.checkint(i); result &= args.checkint(i);
} }
return bitsToValue( result ); return bitsToValue(result);
} }
static Varargs bnot( Varargs args ) { static Varargs bnot(Varargs args) {
return bitsToValue( ~args.checkint(1) ); return bitsToValue(~args.checkint(1));
} }
static Varargs bor( Varargs args ) { static Varargs bor(Varargs args) {
int result = 0; int result = 0;
for ( int i = 1; i <= args.narg(); i++ ) { for (int i = 1; i <= args.narg(); i++) {
result |= args.checkint(i); result |= args.checkint(i);
} }
return bitsToValue( result ); return bitsToValue(result);
} }
static Varargs btest( Varargs args ) { static Varargs btest(Varargs args) {
int bits = -1; int bits = -1;
for ( int i = 1; i <= args.narg(); i++ ) { for (int i = 1; i <= args.narg(); i++) {
bits &= args.checkint(i); bits &= args.checkint(i);
} }
return valueOf( bits != 0 ); return valueOf(bits != 0);
} }
static Varargs bxor( Varargs args ) { static Varargs bxor(Varargs args) {
int result = 0; int result = 0;
for ( int i = 1; i <= args.narg(); i++ ) { for (int i = 1; i <= args.narg(); i++) {
result ^= args.checkint(i); result ^= args.checkint(i);
} }
return bitsToValue( result ); return bitsToValue(result);
} }
static LuaValue lrotate(int x, int disp) { static LuaValue lrotate(int x, int disp) {
@@ -177,7 +200,7 @@ public class Bit32Lib extends TwoArgFunction {
return rrotate(x, -disp); return rrotate(x, -disp);
} else { } else {
disp = disp & 31; disp = disp & 31;
return bitsToValue((x << disp) | (x >>> (32 - disp))); return bitsToValue((x<<disp) | (x>>>(32-disp)));
} }
} }
@@ -186,7 +209,7 @@ public class Bit32Lib extends TwoArgFunction {
return lrotate(x, -disp); return lrotate(x, -disp);
} else { } else {
disp = disp & 31; disp = disp & 31;
return bitsToValue((x >>> disp) | (x << (32 - disp))); return bitsToValue((x>>>disp) | (x<<(32-disp)));
} }
} }
@@ -197,10 +220,10 @@ public class Bit32Lib extends TwoArgFunction {
if (width < 0) { if (width < 0) {
argerror(3, "width must be postive"); argerror(3, "width must be postive");
} }
if (field + width > 32) { if (field+width > 32) {
error("trying to access non-existent bits"); error("trying to access non-existent bits");
} }
return bitsToValue((n >>> field) & (-1 >>> (32 - width))); return bitsToValue((n>>>field) & (-1>>>(32-width)));
} }
static LuaValue replace(int n, int v, int field, int width) { static LuaValue replace(int n, int v, int field, int width) {
@@ -210,15 +233,15 @@ public class Bit32Lib extends TwoArgFunction {
if (width < 0) { if (width < 0) {
argerror(4, "width must be postive"); argerror(4, "width must be postive");
} }
if (field + width > 32) { if (field+width > 32) {
error("trying to access non-existent bits"); error("trying to access non-existent bits");
} }
int mask = (-1 >>> (32 - width)) << field; int mask = (-1>>>(32-width))<<field;
n = (n & ~mask) | ((v << field) & mask); n = (n & ~mask) | ((v<<field) & mask);
return bitsToValue(n); return bitsToValue(n);
} }
private static LuaValue bitsToValue( int x ) { private static LuaValue bitsToValue(int x) {
return ( x < 0 ) ? valueOf((double) ((long) x & 0xFFFFFFFFL)) : valueOf(x); return (x < 0)? valueOf((double) ((long) x & 0xFFFFFFFFL)): valueOf(x);
} }
} }

View File

@@ -28,49 +28,64 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua standard {@code coroutine} * Subclass of {@link LibFunction} which implements the lua standard
* library. * {@code coroutine} library.
* <p> * <p>
* The coroutine library in luaj has the same behavior as the * The coroutine library in luaj has the same behavior as the coroutine library
* coroutine library in C, but is implemented using Java Threads to maintain * in C, but is implemented using Java Threads to maintain the call state
* the call state between invocations. Therefore it can be yielded from anywhere, * between invocations. Therefore it can be yielded from anywhere, similar to
* similar to the "Coco" yield-from-anywhere patch available for C-based lua. * the "Coco" yield-from-anywhere patch available for C-based lua. However,
* However, coroutines that are yielded but never resumed to complete their execution * coroutines that are yielded but never resumed to complete their execution may
* may not be collected by the garbage collector. * not be collected by the garbage collector.
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* <pre> {@code * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* Globals globals = JsePlatform.standardGlobals(); *
* System.out.println( globals.get("coroutine").get("running").call() ); * <pre>
* } </pre> * {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("coroutine").get("running").call());
* }
* </pre>
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new CoroutineLib()); * Globals globals = new Globals();
* System.out.println( globals.get("coroutine").get("running").call() ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* globals.load(new CoroutineLib());
* System.out.println(globals.get("coroutine").get("running").call());
* }
* </pre>
* <p> * <p>
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.2">Lua 5.2 Coroutine Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.2">Lua 5.2
* Coroutine Lib Reference</a>
*/ */
public class CoroutineLib extends TwoArgFunction { public class CoroutineLib extends TwoArgFunction {
static int coroutine_count = 0; static int coroutine_count = 0;
Globals globals; Globals globals;
/** Perform one-time initialization on the library by creating a table /**
* containing the library functions, adding that table to the supplied environment, * Perform one-time initialization on the library by creating a table
* adding the table to package.loaded, and returning table as the return value. * containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance. * @param env the environment to load into, which must be a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
@@ -82,7 +97,8 @@ public class CoroutineLib extends TwoArgFunction {
coroutine.set("yield", new yield()); coroutine.set("yield", new yield());
coroutine.set("wrap", new wrap()); coroutine.set("wrap", new wrap());
env.set("coroutine", coroutine); env.set("coroutine", coroutine);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("coroutine", coroutine); if (!env.get("package").isnil())
env.get("package").get("loaded").set("coroutine", coroutine);
return coroutine; return coroutine;
} }
@@ -95,7 +111,7 @@ public class CoroutineLib extends TwoArgFunction {
static final class resume extends VarArgFunction { static final class resume extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
final LuaThread t = args.checkthread(1); final LuaThread t = args.checkthread(1);
return t.resume( args.subargs(2) ); return t.resume(args.subargs(2));
} }
} }
@@ -109,13 +125,13 @@ public class CoroutineLib extends TwoArgFunction {
static final class status extends LibFunction { static final class status extends LibFunction {
public LuaValue call(LuaValue t) { public LuaValue call(LuaValue t) {
LuaThread lt = t.checkthread(); LuaThread lt = t.checkthread();
return valueOf( lt.getStatus() ); return valueOf(lt.getStatus());
} }
} }
final class yield extends VarArgFunction { final class yield extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return globals.yield( args ); return globals.yield(args);
} }
} }
@@ -129,15 +145,17 @@ public class CoroutineLib extends TwoArgFunction {
static final class wrapper extends VarArgFunction { static final class wrapper extends VarArgFunction {
final LuaThread luathread; final LuaThread luathread;
wrapper(LuaThread luathread) { wrapper(LuaThread luathread) {
this.luathread = luathread; this.luathread = luathread;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
final Varargs result = luathread.resume(args); final Varargs result = luathread.resume(args);
if ( result.arg1().toboolean() ) { if (result.arg1().toboolean()) {
return result.subargs(2); return result.subargs(2);
} else { } else {
return error( result.arg(2).tojstring() ); return error(result.arg(2).tojstring());
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -32,77 +32,100 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Abstract base class extending {@link LibFunction} which implements the * Abstract base class extending {@link LibFunction} which implements the core
* core of the lua standard {@code io} library. * of the lua standard {@code io} library.
* <p> * <p>
* It contains the implementation of the io library support that is common to * It contains the implementation of the io library support that is common to
* the JSE and JME platforms. * the JSE and JME platforms. In practice on of the concrete IOLib subclasses is
* In practice on of the concrete IOLib subclasses is chosen: * chosen: {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* {@link org.luaj.vm2.lib.jse.JseIoLib} for the JSE platform, and
* {@link org.luaj.vm2.lib.jme.JmeIoLib} for the JME platform. * {@link org.luaj.vm2.lib.jme.JmeIoLib} for the JME platform.
* <p> * <p>
* The JSE implementation conforms almost completely to the C-based lua library, * The JSE implementation conforms almost completely to the C-based lua library,
* while the JME implementation follows closely except in the area of random-access files, * while the JME implementation follows closely except in the area of
* which are difficult to support properly on JME. * random-access files, which are difficult to support properly on JME.
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* <pre> {@code * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* Globals globals = JsePlatform.standardGlobals(); *
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * <pre>
* } </pre> * {
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseIoLib} library will be loaded, which will include * &#64;code
* the base functionality provided by this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load the * Globals globals = JsePlatform.standardGlobals();
* {@link org.luaj.vm2.lib.jse.JseIoLib}. * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
*
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseIoLib}
* library will be loaded, which will include the base functionality provided by
* this class, whereas the {@link org.luaj.vm2.lib.jse.JsePlatform} would load
* the {@link org.luaj.vm2.lib.jse.JseIoLib}.
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new OsLib()); * Globals globals = new Globals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* globals.load(new OsLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseIoLib * @see org.luaj.vm2.lib.jse.JseIoLib
* @see org.luaj.vm2.lib.jme.JmeIoLib * @see org.luaj.vm2.lib.jme.JmeIoLib
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a> * @see <a href=
* "http://www.lua.org/manual/5.1/manual.html#5.7">http://www.lua.org/manual/5.1/manual.html#5.7</a>
*/ */
abstract abstract public class IoLib extends TwoArgFunction {
public class IoLib extends TwoArgFunction {
abstract protected class File extends LuaValue {
abstract public void write(LuaString string) throws IOException;
abstract
protected class File extends LuaValue{
abstract public void write( LuaString string ) throws IOException;
abstract public void flush() throws IOException; abstract public void flush() throws IOException;
abstract public boolean isstdfile(); abstract public boolean isstdfile();
abstract public void close() throws IOException; abstract public void close() throws IOException;
abstract public boolean isclosed(); abstract public boolean isclosed();
// returns new position // returns new position
abstract public int seek(String option, int bytecount) throws IOException; abstract public int seek(String option, int bytecount) throws IOException;
abstract public void setvbuf(String mode, int size); abstract public void setvbuf(String mode, int size);
// get length remaining to read // get length remaining to read
abstract public int remaining() throws IOException; abstract public int remaining() throws IOException;
// peek ahead one character // peek ahead one character
abstract public int peek() throws IOException, EOFException; abstract public int peek() throws IOException, EOFException;
// return char if read, -1 if eof, throw IOException on other exception // return char if read, -1 if eof, throw IOException on other exception
abstract public int read() throws IOException, EOFException; abstract public int read() throws IOException, EOFException;
// return number of bytes read if positive, false if eof, throw IOException on other exception // return number of bytes read if positive, false if eof, throw IOException on other exception
abstract public int read(byte[] bytes, int offset, int length) throws IOException; abstract public int read(byte[] bytes, int offset, int length) throws IOException;
public boolean eof() throws IOException { public boolean eof() throws IOException {
try { try {
return peek() < 0; return peek() < 0;
} catch (EOFException e) { return true; } } catch (EOFException e) {
return true;
}
} }
// delegate method access to file methods table // delegate method access to file methods table
public LuaValue get( LuaValue key ) { public LuaValue get(LuaValue key) {
return filemethods.get(key); return filemethods.get(key);
} }
@@ -110,35 +133,38 @@ public class IoLib extends TwoArgFunction {
public int type() { public int type() {
return LuaValue.TUSERDATA; return LuaValue.TUSERDATA;
} }
public String typename() { public String typename() {
return "userdata"; return "userdata";
} }
// displays as "file" type // displays as "file" type
public String tojstring() { public String tojstring() {
return "file: " + Integer.toHexString(hashCode()); return "file: " + Integer.toHexString(hashCode());
} }
public void finalize() { public void finalize() {
if (!isclosed()) { if (!isclosed()) {
try { try {
close(); close();
} catch (IOException ignore) {} } catch (IOException ignore) {
}
} }
} }
} }
/** Enumerated value representing stdin */ /** Enumerated value representing stdin */
protected static final int FTYPE_STDIN = 0; protected static final int FTYPE_STDIN = 0;
/** Enumerated value representing stdout */ /** Enumerated value representing stdout */
protected static final int FTYPE_STDOUT = 1; protected static final int FTYPE_STDOUT = 1;
/** Enumerated value representing stderr */ /** Enumerated value representing stderr */
protected static final int FTYPE_STDERR = 2; protected static final int FTYPE_STDERR = 2;
/** Enumerated value representing a file type for a named file */ /** Enumerated value representing a file type for a named file */
protected static final int FTYPE_NAMED = 3; protected static final int FTYPE_NAMED = 3;
/** /**
* Wrap the standard input. * Wrap the standard input.
*
* @return File * @return File
* @throws IOException * @throws IOException
*/ */
@@ -146,32 +172,37 @@ public class IoLib extends TwoArgFunction {
/** /**
* Wrap the standard output. * Wrap the standard output.
*
* @return File * @return File
* @throws IOException * @throws IOException
*/ */
abstract protected File wrapStdout() throws IOException; abstract protected File wrapStdout() throws IOException;
/** /**
* Wrap the standard error output. * Wrap the standard error output.
*
* @return File * @return File
* @throws IOException * @throws IOException
*/ */
abstract protected File wrapStderr() throws IOException; abstract protected File wrapStderr() throws IOException;
/** /**
* Open a file in a particular mode. * Open a file in a particular mode.
*
* @param filename * @param filename
* @param readMode true if opening in read mode * @param readMode true if opening in read mode
* @param appendMode true if opening in append mode * @param appendMode true if opening in append mode
* @param updateMode true if opening in update mode * @param updateMode true if opening in update mode
* @param binaryMode true if opening in binary mode * @param binaryMode true if opening in binary mode
* @return File object if successful * @return File object if successful
* @throws IOException if could not be opened * @throws IOException if could not be opened
*/ */
abstract protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException; abstract protected File openFile(String filename, boolean readMode, boolean appendMode, boolean updateMode,
boolean binaryMode) throws IOException;
/** /**
* Open a temporary file. * Open a temporary file.
*
* @return File object if successful * @return File object if successful
* @throws IOException if could not be opened * @throws IOException if could not be opened
*/ */
@@ -179,6 +210,7 @@ public class IoLib extends TwoArgFunction {
/** /**
* Start a new process and return a file for input or output * Start a new process and return a file for input or output
*
* @param prog the program to execute * @param prog the program to execute
* @param mode "r" to read, "w" to write * @param mode "r" to read, "w" to write
* @return File to read to or write from * @return File to read to or write from
@@ -195,103 +227,88 @@ public class IoLib extends TwoArgFunction {
private static final LuaValue STDERR = valueOf("stderr"); private static final LuaValue STDERR = valueOf("stderr");
private static final LuaValue FILE = valueOf("file"); private static final LuaValue FILE = valueOf("file");
private static final LuaValue CLOSED_FILE = valueOf("closed file"); private static final LuaValue CLOSED_FILE = valueOf("closed file");
private static final int IO_CLOSE = 0;
private static final int IO_FLUSH = 1;
private static final int IO_INPUT = 2;
private static final int IO_LINES = 3;
private static final int IO_OPEN = 4;
private static final int IO_OUTPUT = 5;
private static final int IO_POPEN = 6;
private static final int IO_READ = 7;
private static final int IO_TMPFILE = 8;
private static final int IO_TYPE = 9;
private static final int IO_WRITE = 10;
private static final int FILE_CLOSE = 11; private static final int IO_CLOSE = 0;
private static final int FILE_FLUSH = 12; private static final int IO_FLUSH = 1;
private static final int FILE_LINES = 13; private static final int IO_INPUT = 2;
private static final int FILE_READ = 14; private static final int IO_LINES = 3;
private static final int FILE_SEEK = 15; private static final int IO_OPEN = 4;
private static final int FILE_SETVBUF = 16; private static final int IO_OUTPUT = 5;
private static final int FILE_WRITE = 17; private static final int IO_POPEN = 6;
private static final int IO_READ = 7;
private static final int IO_INDEX = 18; private static final int IO_TMPFILE = 8;
private static final int LINES_ITER = 19; private static final int IO_TYPE = 9;
private static final int IO_WRITE = 10;
public static final String[] IO_NAMES = { private static final int FILE_CLOSE = 11;
"close", private static final int FILE_FLUSH = 12;
"flush", private static final int FILE_LINES = 13;
"input", private static final int FILE_READ = 14;
"lines", private static final int FILE_SEEK = 15;
"open", private static final int FILE_SETVBUF = 16;
"output", private static final int FILE_WRITE = 17;
"popen",
"read", private static final int IO_INDEX = 18;
"tmpfile", private static final int LINES_ITER = 19;
"type",
"write", public static final String[] IO_NAMES = { "close", "flush", "input", "lines", "open", "output", "popen", "read",
}; "tmpfile", "type", "write", };
public static final String[] FILE_NAMES = { public static final String[] FILE_NAMES = { "close", "flush", "lines", "read", "seek", "setvbuf", "write", };
"close",
"flush",
"lines",
"read",
"seek",
"setvbuf",
"write",
};
LuaTable filemethods; LuaTable filemethods;
protected Globals globals; protected Globals globals;
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
// io lib functions // io lib functions
LuaTable t = new LuaTable(); LuaTable t = new LuaTable();
bind(t, IoLibV.class, IO_NAMES ); bind(t, IoLibV.class, IO_NAMES);
// create file methods table // create file methods table
filemethods = new LuaTable(); filemethods = new LuaTable();
bind(filemethods, IoLibV.class, FILE_NAMES, FILE_CLOSE ); bind(filemethods, IoLibV.class, FILE_NAMES, FILE_CLOSE);
// set up file metatable // set up file metatable
LuaTable mt = new LuaTable(); LuaTable mt = new LuaTable();
bind(mt, IoLibV.class, new String[] { "__index" }, IO_INDEX ); bind(mt, IoLibV.class, new String[] { "__index" }, IO_INDEX);
t.setmetatable( mt ); t.setmetatable(mt);
// all functions link to library instance // all functions link to library instance
setLibInstance( t ); setLibInstance(t);
setLibInstance( filemethods ); setLibInstance(filemethods);
setLibInstance( mt ); setLibInstance(mt);
// return the table // return the table
env.set("io", t); env.set("io", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("io", t); if (!env.get("package").isnil())
env.get("package").get("loaded").set("io", t);
return t; return t;
} }
private void setLibInstance(LuaTable t) { private void setLibInstance(LuaTable t) {
LuaValue[] k = t.keys(); LuaValue[] k = t.keys();
for ( int i=0, n=k.length; i<n; i++ ) for (int i = 0, n = k.length; i < n; i++)
((IoLibV) t.get(k[i])).iolib = this; ((IoLibV) t.get(k[i])).iolib = this;
} }
static final class IoLibV extends VarArgFunction { static final class IoLibV extends VarArgFunction {
private File f; private File f;
public IoLib iolib; public IoLib iolib;
private boolean toclose; private boolean toclose;
private Varargs args; private Varargs args;
public IoLibV() { public IoLibV() {
} }
public IoLibV(File f, String name, int opcode, IoLib iolib, boolean toclose, Varargs args) { public IoLibV(File f, String name, int opcode, IoLib iolib, boolean toclose, Varargs args) {
this(f, name, opcode, iolib); this(f, name, opcode, iolib);
this.toclose = toclose; this.toclose = toclose;
this.args = args.dealias(); this.args = args.dealias();
} }
public IoLibV(File f, String name, int opcode, IoLib iolib) { public IoLibV(File f, String name, int opcode, IoLib iolib) {
super(); super();
this.f = f; this.f = f;
@@ -302,45 +319,65 @@ public class IoLib extends TwoArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
try { try {
switch ( opcode ) { switch (opcode) {
case IO_FLUSH: return iolib._io_flush(); case IO_FLUSH:
case IO_TMPFILE: return iolib._io_tmpfile(); return iolib._io_flush();
case IO_CLOSE: return iolib._io_close(args.arg1()); case IO_TMPFILE:
case IO_INPUT: return iolib._io_input(args.arg1()); return iolib._io_tmpfile();
case IO_OUTPUT: return iolib._io_output(args.arg1()); case IO_CLOSE:
case IO_TYPE: return iolib._io_type(args.arg1()); return iolib._io_close(args.arg1());
case IO_POPEN: return iolib._io_popen(args.checkjstring(1),args.optjstring(2,"r")); case IO_INPUT:
case IO_OPEN: return iolib._io_open(args.checkjstring(1), args.optjstring(2,"r")); return iolib._io_input(args.arg1());
case IO_LINES: return iolib._io_lines(args); case IO_OUTPUT:
case IO_READ: return iolib._io_read(args); return iolib._io_output(args.arg1());
case IO_WRITE: return iolib._io_write(args); case IO_TYPE:
return iolib._io_type(args.arg1());
case FILE_CLOSE: return iolib._file_close(args.arg1()); case IO_POPEN:
case FILE_FLUSH: return iolib._file_flush(args.arg1()); return iolib._io_popen(args.checkjstring(1), args.optjstring(2, "r"));
case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,8192)); case IO_OPEN:
case FILE_LINES: return iolib._file_lines(args); return iolib._io_open(args.checkjstring(1), args.optjstring(2, "r"));
case FILE_READ: return iolib._file_read(args.arg1(),args.subargs(2)); case IO_LINES:
case FILE_SEEK: return iolib._file_seek(args.arg1(),args.optjstring(2,"cur"),args.optint(3,0)); return iolib._io_lines(args);
case FILE_WRITE: return iolib._file_write(args.arg1(),args.subargs(2)); case IO_READ:
return iolib._io_read(args);
case IO_WRITE:
return iolib._io_write(args);
case IO_INDEX: return iolib._io_index(args.arg(2)); case FILE_CLOSE:
case LINES_ITER: return iolib._lines_iter(f, toclose, this.args); return iolib._file_close(args.arg1());
case FILE_FLUSH:
return iolib._file_flush(args.arg1());
case FILE_SETVBUF:
return iolib._file_setvbuf(args.arg1(), args.checkjstring(2), args.optint(3, 8192));
case FILE_LINES:
return iolib._file_lines(args);
case FILE_READ:
return iolib._file_read(args.arg1(), args.subargs(2));
case FILE_SEEK:
return iolib._file_seek(args.arg1(), args.optjstring(2, "cur"), args.optint(3, 0));
case FILE_WRITE:
return iolib._file_write(args.arg1(), args.subargs(2));
case IO_INDEX:
return iolib._io_index(args.arg(2));
case LINES_ITER:
return iolib._lines_iter(f, toclose, this.args);
} }
} catch ( IOException ioe ) { } catch (IOException ioe) {
if (opcode == LINES_ITER) { if (opcode == LINES_ITER) {
String s = ioe.getMessage(); String s = ioe.getMessage();
error(s != null ? s : ioe.toString()); error(s != null? s: ioe.toString());
} }
return errorresult(ioe); return errorresult(ioe);
} }
return NONE; return NONE;
} }
} }
private File input() { private File input() {
return infile!=null? infile: (infile=ioopenfile(FTYPE_STDIN, "-","r")); return infile != null? infile: (infile = ioopenfile(FTYPE_STDIN, "-", "r"));
} }
// io.flush() -> bool // io.flush() -> bool
public Varargs _io_flush() throws IOException { public Varargs _io_flush() throws IOException {
checkopen(output()); checkopen(output());
@@ -362,31 +399,28 @@ public class IoLib extends TwoArgFunction {
// io.input([file]) -> file // io.input([file]) -> file
public Varargs _io_input(LuaValue file) { public Varargs _io_input(LuaValue file) {
infile = file.isnil()? input(): infile = file.isnil()? input()
file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(),"r"): : file.isstring()? ioopenfile(FTYPE_NAMED, file.checkjstring(), "r"): checkfile(file);
checkfile(file);
return infile; return infile;
} }
// io.output(filename) -> file // io.output(filename) -> file
public Varargs _io_output(LuaValue filename) { public Varargs _io_output(LuaValue filename) {
outfile = filename.isnil()? output(): outfile = filename.isnil()? output()
filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(),"w"): : filename.isstring()? ioopenfile(FTYPE_NAMED, filename.checkjstring(), "w"): checkfile(filename);
checkfile(filename);
return outfile; return outfile;
} }
// io.type(obj) -> "file" | "closed file" | nil // io.type(obj) -> "file" | "closed file" | nil
public Varargs _io_type(LuaValue obj) { public Varargs _io_type(LuaValue obj) {
File f = optfile(obj); File f = optfile(obj);
return f!=null? return f != null? f.isclosed()? CLOSED_FILE: FILE: NIL;
f.isclosed()? CLOSED_FILE: FILE:
NIL;
} }
// io.popen(prog, [mode]) -> file // io.popen(prog, [mode]) -> file
public Varargs _io_popen(String prog, String mode) throws IOException { public Varargs _io_popen(String prog, String mode) throws IOException {
if (!"r".equals(mode) && !"w".equals(mode)) argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'"); if (!"r".equals(mode) && !"w".equals(mode))
argerror(2, "invalid value: '" + mode + "'; must be one of 'r' or 'w'");
return openProgram(prog, mode); return openProgram(prog, mode);
} }
@@ -398,7 +432,7 @@ public class IoLib extends TwoArgFunction {
// io.lines(filename, ...) -> iterator // io.lines(filename, ...) -> iterator
public Varargs _io_lines(Varargs args) { public Varargs _io_lines(Varargs args) {
String filename = args.optjstring(1, null); String filename = args.optjstring(1, null);
File infile = filename==null? input(): ioopenfile(FTYPE_NAMED, filename,"r"); File infile = filename == null? input(): ioopenfile(FTYPE_NAMED, filename, "r");
checkopen(infile); checkopen(infile);
return lines(infile, filename != null, args.subargs(2)); return lines(infile, filename != null, args.subargs(2));
} }
@@ -406,13 +440,13 @@ public class IoLib extends TwoArgFunction {
// io.read(...) -> (...) // io.read(...) -> (...)
public Varargs _io_read(Varargs args) throws IOException { public Varargs _io_read(Varargs args) throws IOException {
checkopen(input()); checkopen(input());
return ioread(infile,args); return ioread(infile, args);
} }
// io.write(...) -> void // io.write(...) -> void
public Varargs _io_write(Varargs args) throws IOException { public Varargs _io_write(Varargs args) throws IOException {
checkopen(output()); checkopen(output());
return iowrite(outfile,args); return iowrite(outfile, args);
} }
// file:close() -> void // file:close() -> void
@@ -434,7 +468,7 @@ public class IoLib extends TwoArgFunction {
} else { } else {
argerror(1, "invalid value: '" + mode + "'; must be one of 'no', 'full' or 'line'"); argerror(1, "invalid value: '" + mode + "'; must be one of 'no', 'full' or 'line'");
} }
checkfile(file).setvbuf(mode,size); checkfile(file).setvbuf(mode, size);
return LuaValue.TRUE; return LuaValue.TRUE;
} }
@@ -445,7 +479,7 @@ public class IoLib extends TwoArgFunction {
// file:read(...) -> (...) // file:read(...) -> (...)
public Varargs _file_read(LuaValue file, Varargs subargs) throws IOException { public Varargs _file_read(LuaValue file, Varargs subargs) throws IOException {
return ioread(checkfile(file),subargs); return ioread(checkfile(file), subargs);
} }
// file:seek([whence][,offset]) -> pos | nil,error // file:seek([whence][,offset]) -> pos | nil,error
@@ -456,50 +490,51 @@ public class IoLib extends TwoArgFunction {
} else { } else {
argerror(1, "invalid value: '" + whence + "'; must be one of 'set', 'cur' or 'end'"); argerror(1, "invalid value: '" + whence + "'; must be one of 'set', 'cur' or 'end'");
} }
return valueOf( checkfile(file).seek(whence,offset) ); return valueOf(checkfile(file).seek(whence, offset));
} }
// file:write(...) -> void // file:write(...) -> void
public Varargs _file_write(LuaValue file, Varargs subargs) throws IOException { public Varargs _file_write(LuaValue file, Varargs subargs) throws IOException {
return iowrite(checkfile(file),subargs); return iowrite(checkfile(file), subargs);
} }
// __index, returns a field // __index, returns a field
public Varargs _io_index(LuaValue v) { public Varargs _io_index(LuaValue v) {
return v.equals(STDOUT)?output(): return v.equals(STDOUT)? output(): v.equals(STDIN)? input(): v.equals(STDERR)? errput(): NIL;
v.equals(STDIN)? input():
v.equals(STDERR)? errput(): NIL;
} }
// lines iterator(s,var) -> var' // lines iterator(s,var) -> var'
public Varargs _lines_iter(LuaValue file, boolean toclose, Varargs args) throws IOException { public Varargs _lines_iter(LuaValue file, boolean toclose, Varargs args) throws IOException {
File f = optfile(file); File f = optfile(file);
if ( f == null ) argerror(1, "not a file: " + file); if (f == null)
if ( f.isclosed() ) error("file is already closed"); argerror(1, "not a file: " + file);
if (f.isclosed())
error("file is already closed");
Varargs ret = ioread(f, args); Varargs ret = ioread(f, args);
if (toclose && ret.isnil(1) && f.eof()) f.close(); if (toclose && ret.isnil(1) && f.eof())
f.close();
return ret; return ret;
} }
private File output() { private File output() {
return outfile!=null? outfile: (outfile=ioopenfile(FTYPE_STDOUT,"-","w")); return outfile != null? outfile: (outfile = ioopenfile(FTYPE_STDOUT, "-", "w"));
} }
private File errput() { private File errput() {
return errfile!=null? errfile: (errfile=ioopenfile(FTYPE_STDERR,"-","w")); return errfile != null? errfile: (errfile = ioopenfile(FTYPE_STDERR, "-", "w"));
} }
private File ioopenfile(int filetype, String filename, String mode) { private File ioopenfile(int filetype, String filename, String mode) {
try { try {
return rawopenfile(filetype, filename, mode); return rawopenfile(filetype, filename, mode);
} catch ( Exception e ) { } catch (Exception e) {
error("io error: "+e.getMessage()); error("io error: " + e.getMessage());
return null; return null;
} }
} }
private static Varargs ioclose(File f) throws IOException { private static Varargs ioclose(File f) throws IOException {
if ( f.isstdfile() ) if (f.isstdfile())
return errorresult("cannot close standard file"); return errorresult("cannot close standard file");
else { else {
f.close(); f.close();
@@ -513,71 +548,80 @@ public class IoLib extends TwoArgFunction {
static Varargs errorresult(Exception ioe) { static Varargs errorresult(Exception ioe) {
String s = ioe.getMessage(); String s = ioe.getMessage();
return errorresult("io error: "+(s!=null? s: ioe.toString())); return errorresult("io error: " + (s != null? s: ioe.toString()));
} }
private static Varargs errorresult(String errortext) { private static Varargs errorresult(String errortext) {
return varargsOf(NIL, valueOf(errortext)); return varargsOf(NIL, valueOf(errortext));
} }
private Varargs lines(final File f, boolean toclose, Varargs args) { private Varargs lines(final File f, boolean toclose, Varargs args) {
try { try {
return new IoLibV(f,"lnext",LINES_ITER,this,toclose,args); return new IoLibV(f, "lnext", LINES_ITER, this, toclose, args);
} catch ( Exception e ) { } catch (Exception e) {
return error("lines: "+e); return error("lines: " + e);
} }
} }
private static Varargs iowrite(File f, Varargs args) throws IOException { private static Varargs iowrite(File f, Varargs args) throws IOException {
for ( int i=1, n=args.narg(); i<=n; i++ ) for (int i = 1, n = args.narg(); i <= n; i++)
f.write( args.checkstring(i) ); f.write(args.checkstring(i));
return f; return f;
} }
private Varargs ioread(File f, Varargs args) throws IOException { private Varargs ioread(File f, Varargs args) throws IOException {
int i,n=args.narg(); int i, n = args.narg();
if (n == 0) return freadline(f,false); if (n == 0)
return freadline(f, false);
LuaValue[] v = new LuaValue[n]; LuaValue[] v = new LuaValue[n];
LuaValue ai,vi; LuaValue ai, vi;
LuaString fmt; LuaString fmt;
for ( i=0; i<n; ) { for (i = 0; i < n;) {
item: switch ( (ai = args.arg(i+1)).type() ) { item: switch ((ai = args.arg(i+1)).type()) {
case LuaValue.TNUMBER: case LuaValue.TNUMBER:
vi = freadbytes(f,ai.toint()); vi = freadbytes(f, ai.toint());
break item; break item;
case LuaValue.TSTRING: case LuaValue.TSTRING:
fmt = ai.checkstring(); fmt = ai.checkstring();
if ( fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*' ) { if (fmt.m_length >= 2 && fmt.m_bytes[fmt.m_offset] == '*') {
switch ( fmt.m_bytes[fmt.m_offset+1] ) { switch (fmt.m_bytes[fmt.m_offset+1]) {
case 'n': vi = freadnumber(f); break item; case 'n':
case 'l': vi = freadline(f,false); break item; vi = freadnumber(f);
case 'L': vi = freadline(f,true); break item; break item;
case 'a': vi = freadall(f); break item; case 'l':
} vi = freadline(f, false);
break item;
case 'L':
vi = freadline(f, true);
break item;
case 'a':
vi = freadall(f);
break item;
} }
default: }
return argerror( i+1, "(invalid format)" ); default:
return argerror(i+1, "(invalid format)");
} }
if ( (v[i++] = vi).isnil() ) if ((v[i++] = vi).isnil())
break; break;
} }
return i==0? NIL: varargsOf(v, 0, i); return i == 0? NIL: varargsOf(v, 0, i);
} }
private static File checkfile(LuaValue val) { private static File checkfile(LuaValue val) {
File f = optfile(val); File f = optfile(val);
if ( f == null ) if (f == null)
argerror(1,"file"); argerror(1, "file");
checkopen( f ); checkopen(f);
return f; return f;
} }
private static File optfile(LuaValue val) { private static File optfile(LuaValue val) {
return (val instanceof File)? (File) val: null; return (val instanceof File)? (File) val: null;
} }
private static File checkopen(File file) { private static File checkopen(File file) {
if ( file.isclosed() ) if (file.isclosed())
error("attempt to use a closed file"); error("attempt to use a closed file");
return file; return file;
} }
@@ -586,99 +630,115 @@ public class IoLib extends TwoArgFunction {
int len = mode.length(); int len = mode.length();
for (int i = 0; i < len; i++) { // [rwa][+]?b* for (int i = 0; i < len; i++) { // [rwa][+]?b*
char ch = mode.charAt(i); char ch = mode.charAt(i);
if (i == 0 && "rwa".indexOf(ch) >= 0) continue; if (i == 0 && "rwa".indexOf(ch) >= 0)
if (i == 1 && ch == '+') continue; continue;
if (i >= 1 && ch == 'b') continue; if (i == 1 && ch == '+')
continue;
if (i >= 1 && ch == 'b')
continue;
len = -1; len = -1;
break; break;
} }
if (len <= 0) argerror(2, "invalid mode: '" + mode + "'"); if (len <= 0)
argerror(2, "invalid mode: '" + mode + "'");
switch (filetype) { switch (filetype) {
case FTYPE_STDIN: return wrapStdin(); case FTYPE_STDIN:
case FTYPE_STDOUT: return wrapStdout(); return wrapStdin();
case FTYPE_STDERR: return wrapStderr(); case FTYPE_STDOUT:
return wrapStdout();
case FTYPE_STDERR:
return wrapStderr();
} }
boolean isreadmode = mode.startsWith("r"); boolean isreadmode = mode.startsWith("r");
boolean isappend = mode.startsWith("a"); boolean isappend = mode.startsWith("a");
boolean isupdate = mode.indexOf('+') > 0; boolean isupdate = mode.indexOf('+') > 0;
boolean isbinary = mode.endsWith("b"); boolean isbinary = mode.endsWith("b");
return openFile( filename, isreadmode, isappend, isupdate, isbinary ); return openFile(filename, isreadmode, isappend, isupdate, isbinary);
} }
// ------------- file reading utilitied ------------------ // ------------- file reading utilitied ------------------
public static LuaValue freadbytes(File f, int count) throws IOException { public static LuaValue freadbytes(File f, int count) throws IOException {
if (count == 0) return f.eof() ? NIL : EMPTYSTRING; if (count == 0)
return f.eof()? NIL: EMPTYSTRING;
byte[] b = new byte[count]; byte[] b = new byte[count];
int r; int r;
if ( ( r = f.read(b,0,b.length) ) < 0 ) if ((r = f.read(b, 0, b.length)) < 0)
return NIL; return NIL;
return LuaString.valueUsing(b, 0, r); return LuaString.valueUsing(b, 0, r);
} }
public static LuaValue freaduntil(File f,boolean lineonly,boolean withend) throws IOException {
public static LuaValue freaduntil(File f, boolean lineonly, boolean withend) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
int c; int c;
try { try {
if ( lineonly ) { if (lineonly) {
loop: while ( (c = f.read()) >= 0 ) { loop: while ( (c = f.read()) >= 0 ) {
switch ( c ) { switch (c) {
case '\r': if (withend) baos.write(c); break; case '\r':
case '\n': if (withend) baos.write(c); break loop; if (withend)
default: baos.write(c); break; baos.write(c);
break;
case '\n':
if (withend)
baos.write(c);
break loop;
default:
baos.write(c);
break;
} }
} }
} else { } else {
while ( (c = f.read()) >= 0 ) while ( (c = f.read()) >= 0 )
baos.write(c); baos.write(c);
} }
} catch ( EOFException e ) { } catch (EOFException e) {
c = -1; c = -1;
} }
return ( c < 0 && baos.size() == 0 )? return (c < 0 && baos.size() == 0)? (LuaValue) NIL: (LuaValue) LuaString.valueUsing(baos.toByteArray());
(LuaValue) NIL:
(LuaValue) LuaString.valueUsing(baos.toByteArray());
} }
public static LuaValue freadline(File f,boolean withend) throws IOException {
return freaduntil(f,true,withend); public static LuaValue freadline(File f, boolean withend) throws IOException {
return freaduntil(f, true, withend);
} }
public static LuaValue freadall(File f) throws IOException { public static LuaValue freadall(File f) throws IOException {
int n = f.remaining(); int n = f.remaining();
if ( n >= 0 ) { if (n >= 0) {
return n == 0 ? EMPTYSTRING : freadbytes(f, n); return n == 0? EMPTYSTRING: freadbytes(f, n);
} else { } else {
return freaduntil(f,false,false); return freaduntil(f, false, false);
} }
} }
public static LuaValue freadnumber(File f) throws IOException { public static LuaValue freadnumber(File f) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
freadchars(f," \t\r\n",null); freadchars(f, " \t\r\n", null);
freadchars(f,"-+",baos); freadchars(f, "-+", baos);
//freadchars(f,"0",baos); //freadchars(f,"0",baos);
//freadchars(f,"xX",baos); //freadchars(f,"xX",baos);
freadchars(f,"0123456789",baos); freadchars(f, "0123456789", baos);
freadchars(f,".",baos); freadchars(f, ".", baos);
freadchars(f,"0123456789",baos); freadchars(f, "0123456789", baos);
//freadchars(f,"eEfFgG",baos); //freadchars(f,"eEfFgG",baos);
// freadchars(f,"+-",baos); // freadchars(f,"+-",baos);
//freadchars(f,"0123456789",baos); //freadchars(f,"0123456789",baos);
String s = baos.toString(); String s = baos.toString();
return s.length()>0? valueOf( Double.parseDouble(s) ): NIL; return s.length() > 0? valueOf(Double.parseDouble(s)): NIL;
} }
private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException { private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException {
int c; int c;
while ( true ) { while ( true ) {
c = f.peek(); c = f.peek();
if ( chars.indexOf(c) < 0 ) { if (chars.indexOf(c) < 0) {
return; return;
} }
f.read(); f.read();
if ( baos != null ) if (baos != null)
baos.write( c ); baos.write(c);
} }
} }
} }

View File

@@ -29,20 +29,20 @@ import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LuaFunction} common to Java functions exposed to lua. * Subclass of {@link LuaFunction} common to Java functions exposed to lua.
* <p> * <p>
* To provide for common implementations in JME and JSE, * To provide for common implementations in JME and JSE, library functions are
* library functions are typically grouped on one or more library classes * typically grouped on one or more library classes and an opcode per library
* and an opcode per library function is defined and used to key the switch * function is defined and used to key the switch to the correct function within
* to the correct function within the library. * the library.
* <p> * <p>
* Since lua functions can be called with too few or too many arguments, * Since lua functions can be called with too few or too many arguments, and
* and there are overloaded {@link LuaValue#call()} functions with varying * there are overloaded {@link LuaValue#call()} functions with varying number of
* number of arguments, a Java function exposed in lua needs to handle the * arguments, a Java function exposed in lua needs to handle the argument fixup
* argument fixup when a function is called with a number of arguments * when a function is called with a number of arguments differs from that
* differs from that expected. * expected.
* <p> * <p>
* To simplify the creation of library functions, * To simplify the creation of library functions, there are 5 direct subclasses
* there are 5 direct subclasses to handle common cases based on number of * to handle common cases based on number of argument values and number of
* argument values and number of return return values. * return return values.
* <ul> * <ul>
* <li>{@link ZeroArgFunction}</li> * <li>{@link ZeroArgFunction}</li>
* <li>{@link OneArgFunction}</li> * <li>{@link OneArgFunction}</li>
@@ -51,13 +51,15 @@ import org.luaj.vm2.Varargs;
* <li>{@link VarArgFunction}</li> * <li>{@link VarArgFunction}</li>
* </ul> * </ul>
* <p> * <p>
* To be a Java library that can be loaded via {@code require}, it should have * To be a Java library that can be loaded via {@code require}, it should have a
* a public constructor that returns a {@link LuaValue} that, when executed, * public constructor that returns a {@link LuaValue} that, when executed,
* initializes the library. * initializes the library.
* <p> * <p>
* For example, the following code will implement a library called "hyperbolic" * For example, the following code will implement a library called "hyperbolic"
* with two functions, "sinh", and "cosh": * with two functions, "sinh", and "cosh":
<pre> {@code *
* <pre>
* {@code
* import org.luaj.vm2.LuaValue; * import org.luaj.vm2.LuaValue;
* import org.luaj.vm2.lib.*; * import org.luaj.vm2.lib.*;
* *
@@ -85,18 +87,22 @@ import org.luaj.vm2.Varargs;
* } * }
* } * }
*} *}
*}</pre> *}
* The default constructor is used to instantiate the library * </pre>
* in response to {@code require 'hyperbolic'} statement, *
* provided it is on Java&quot;s class path. * The default constructor is used to instantiate the library in response to
* This instance is then invoked with 2 arguments: the name supplied to require(), * {@code require 'hyperbolic'} statement, provided it is on Java&quot;s class
* and the environment for this function. The library may ignore these, or use * path. This instance is then invoked with 2 arguments: the name supplied to
* them to leave side effects in the global environment, for example. * require(), and the environment for this function. The library may ignore
* In the previous example, two functions are created, 'sinh', and 'cosh', and placed * these, or use them to leave side effects in the global environment, for
* into a global table called 'hyperbolic' using the supplied 'env' argument. * example. In the previous example, two functions are created, 'sinh', and
* 'cosh', and placed into a global table called 'hyperbolic' using the supplied
* 'env' argument.
* <p> * <p>
* To test it, a script such as this can be used: * To test it, a script such as this can be used:
* <pre> {@code *
* <pre>
* {@code
* local t = require('hyperbolic') * local t = require('hyperbolic')
* print( 't', t ) * print( 't', t )
* print( 'hyperbolic', hyperbolic ) * print( 'hyperbolic', hyperbolic )
@@ -105,118 +111,147 @@ import org.luaj.vm2.Varargs;
* end * end
* print( 'sinh(.5)', hyperbolic.sinh(.5) ) * print( 'sinh(.5)', hyperbolic.sinh(.5) )
* print( 'cosh(.5)', hyperbolic.cosh(.5) ) * print( 'cosh(.5)', hyperbolic.cosh(.5) )
* }</pre> * }
* </pre>
* <p> * <p>
* It should produce something like: * It should produce something like:
* <pre> {@code *
* <pre>
* {@code
* t table: 3dbbd23f * t table: 3dbbd23f
* hyperbolic table: 3dbbd23f * hyperbolic table: 3dbbd23f
* k,v cosh function: 3dbbd128 * k,v cosh function: 3dbbd128
* k,v sinh function: 3dbbd242 * k,v sinh function: 3dbbd242
* sinh(.5) 0.5210953 * sinh(.5) 0.5210953
* cosh(.5) 1.127626 * cosh(.5) 1.127626
* }</pre> * }
* </pre>
* <p> * <p>
* See the source code in any of the library functions * See the source code in any of the library functions such as {@link BaseLib}
* such as {@link BaseLib} or {@link TableLib} for other examples. * or {@link TableLib} for other examples.
*/ */
abstract public class LibFunction extends LuaFunction { abstract public class LibFunction extends LuaFunction {
/** User-defined opcode to differentiate between instances of the library function class. /**
* User-defined opcode to differentiate between instances of the library
* function class.
* <p> * <p>
* Subclass will typicall switch on this value to provide the specific behavior for each function. * Subclass will typicall switch on this value to provide the specific
* behavior for each function.
*/ */
protected int opcode; protected int opcode;
/** The common name for this function, useful for debugging. /**
* The common name for this function, useful for debugging.
* <p> * <p>
* Binding functions initialize this to the name to which it is bound. * Binding functions initialize this to the name to which it is bound.
*/ */
protected String name; protected String name;
/** Default constructor for use by subclasses */ /** Default constructor for use by subclasses */
protected LibFunction() { protected LibFunction() {
} }
public String tojstring() { public String tojstring() {
return name != null ? "function: " + name : super.tojstring(); return name != null? "function: " + name: super.tojstring();
} }
/** /**
* Bind a set of library functions. * Bind a set of library functions.
* <p> * <p>
* An array of names is provided, and the first name is bound * An array of names is provided, and the first name is bound with opcode =
* with opcode = 0, second with 1, etc. * 0, second with 1, etc.
* @param env The environment to apply to each bound function *
* @param env The environment to apply to each bound function
* @param factory the Class to instantiate for each bound function * @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function. * @param names array of String names, one for each function.
* @see #bind(LuaValue, Class, String[], int) * @see #bind(LuaValue, Class, String[], int)
*/ */
protected void bind(LuaValue env, Class factory, String[] names ) { protected void bind(LuaValue env, Class factory, String[] names) {
bind( env, factory, names, 0 ); bind(env, factory, names, 0);
} }
/** /**
* Bind a set of library functions, with an offset * Bind a set of library functions, with an offset
* <p> * <p>
* An array of names is provided, and the first name is bound * An array of names is provided, and the first name is bound with opcode =
* with opcode = {@code firstopcode}, second with {@code firstopcode+1}, etc. * {@code firstopcode}, second with {@code firstopcode+1}, etc.
* @param env The environment to apply to each bound function *
* @param factory the Class to instantiate for each bound function * @param env The environment to apply to each bound function
* @param names array of String names, one for each function. * @param factory the Class to instantiate for each bound function
* @param names array of String names, one for each function.
* @param firstopcode the first opcode to use * @param firstopcode the first opcode to use
* @see #bind(LuaValue, Class, String[]) * @see #bind(LuaValue, Class, String[])
*/ */
protected void bind(LuaValue env, Class factory, String[] names, int firstopcode ) { protected void bind(LuaValue env, Class factory, String[] names, int firstopcode) {
try { try {
for ( int i=0, n=names.length; i<n; i++ ) { for (int i = 0, n = names.length; i < n; i++) {
LibFunction f = (LibFunction) factory.newInstance(); LibFunction f = (LibFunction) factory.newInstance();
f.opcode = firstopcode + i; f.opcode = firstopcode+i;
f.name = names[i]; f.name = names[i];
env.set(f.name, f); env.set(f.name, f);
} }
} catch ( Exception e ) { } catch (Exception e) {
throw new LuaError( "bind failed: "+e ); throw new LuaError("bind failed: " + e);
} }
} }
/** Java code generation utility to allocate storage for upvalue, leave it empty */ /**
* Java code generation utility to allocate storage for upvalue, leave it
* empty
*/
protected static LuaValue[] newupe() { protected static LuaValue[] newupe() {
return new LuaValue[1]; return new LuaValue[1];
} }
/** Java code generation utility to allocate storage for upvalue, initialize with nil */ /**
* Java code generation utility to allocate storage for upvalue, initialize
* with nil
*/
protected static LuaValue[] newupn() { protected static LuaValue[] newupn() {
return new LuaValue[] { NIL }; return new LuaValue[] { NIL };
} }
/** Java code generation utility to allocate storage for upvalue, initialize with value */ /**
* Java code generation utility to allocate storage for upvalue, initialize
* with value
*/
protected static LuaValue[] newupl(LuaValue v) { protected static LuaValue[] newupl(LuaValue v) {
return new LuaValue[] { v }; return new LuaValue[] { v };
} }
public LuaValue call() { public LuaValue call() {
return argerror(1,"value expected"); return argerror(1, "value expected");
} }
public LuaValue call(LuaValue a) { public LuaValue call(LuaValue a) {
return call(); return call();
} }
public LuaValue call(LuaValue a, LuaValue b) { public LuaValue call(LuaValue a, LuaValue b) {
return call(a); return call(a);
} }
public LuaValue call(LuaValue a, LuaValue b, LuaValue c) { public LuaValue call(LuaValue a, LuaValue b, LuaValue c) {
return call(a,b); return call(a, b);
} }
public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) { public LuaValue call(LuaValue a, LuaValue b, LuaValue c, LuaValue d) {
return call(a,b,c); return call(a, b, c);
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
switch(args.narg()) { switch (args.narg()) {
case 0: return call(); case 0:
case 1: return call(args.arg1()); return call();
case 2: return call(args.arg1(),args.arg(2)); case 1:
case 3: return call(args.arg1(),args.arg(2),args.arg(3)); return call(args.arg1());
default: return call(args.arg1(),args.arg(2),args.arg(3),args.arg(4)); case 2:
return call(args.arg1(), args.arg(2));
case 3:
return call(args.arg1(), args.arg(2), args.arg(3));
default:
return call(args.arg1(), args.arg(2), args.arg(3), args.arg(4));
} }
} }
} }

View File

@@ -29,13 +29,13 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua standard {@code math} * Subclass of {@link LibFunction} which implements the lua standard
* library. * {@code math} library.
* <p> * <p>
* It contains only the math library support that is possible on JME. * It contains only the math library support that is possible on JME. For a more
* For a more complete implementation based on math functions specific to JSE * complete implementation based on math functions specific to JSE use
* use {@link org.luaj.vm2.lib.jse.JseMathLib}. * {@link org.luaj.vm2.lib.jse.JseMathLib}. In Particular the following math
* In Particular the following math functions are <b>not</b> implemented by this library: * functions are <b>not</b> implemented by this library:
* <ul> * <ul>
* <li>acos</li> * <li>acos</li>
* <li>asin</li> * <li>asin</li>
@@ -48,60 +48,81 @@ import org.luaj.vm2.Varargs;
* </ul> * </ul>
* <p> * <p>
* The implementations of {@code exp()} and {@code pow()} are constructed by * The implementations of {@code exp()} and {@code pow()} are constructed by
* hand for JME, so will be slower and less accurate than when executed on the JSE platform. * hand for JME, so will be slower and less accurate than when executed on the
* JSE platform.
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * {
* } </pre> * &#64;code
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example, * Globals globals = JsePlatform.standardGlobals();
* the subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will * System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* be included, which also includes this base functionality. * }
* </pre>
*
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example, the
* subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will be included, which also
* includes this base functionality.
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new MathLib()); * Globals globals = new Globals();
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* Doing so will ensure the library is properly initialized * globals.load(new MathLib());
* and loaded into the globals table. * System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
*
* Doing so will ensure the library is properly initialized and loaded into the
* globals table.
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseMathLib * @see org.luaj.vm2.lib.jse.JseMathLib
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib
* Reference</a>
*/ */
public class MathLib extends TwoArgFunction { public class MathLib extends TwoArgFunction {
/** Pointer to the latest MathLib instance, used only to dispatch /**
* math.exp to tha correct platform math library. * Pointer to the latest MathLib instance, used only to dispatch math.exp to
* tha correct platform math library.
*/ */
public static MathLib MATHLIB = null; public static MathLib MATHLIB = null;
/** Construct a MathLib, which can be initialized by calling it with a /**
* Construct a MathLib, which can be initialized by calling it with a
* modname string, and a global environment table as arguments using * modname string, and a global environment table as arguments using
* {@link #call(LuaValue, LuaValue)}. */ * {@link #call(LuaValue, LuaValue)}.
*/
public MathLib() { public MathLib() {
MATHLIB = this; MATHLIB = this;
} }
/** Perform one-time initialization on the library by creating a table /**
* containing the library functions, adding that table to the supplied environment, * Perform one-time initialization on the library by creating a table
* adding the table to package.loaded, and returning table as the return value. * containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance. * @param env the environment to load into, typically a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable math = new LuaTable(0,30); LuaTable math = new LuaTable(0, 30);
math.set("abs", new abs()); math.set("abs", new abs());
math.set("ceil", new ceil()); math.set("ceil", new ceil());
math.set("cos", new cos()); math.set("cos", new cos());
@@ -110,12 +131,12 @@ public class MathLib extends TwoArgFunction {
math.set("floor", new floor()); math.set("floor", new floor());
math.set("fmod", new fmod()); math.set("fmod", new fmod());
math.set("frexp", new frexp()); math.set("frexp", new frexp());
math.set("huge", LuaDouble.POSINF ); math.set("huge", LuaDouble.POSINF);
math.set("ldexp", new ldexp()); math.set("ldexp", new ldexp());
math.set("max", new max()); math.set("max", new max());
math.set("min", new min()); math.set("min", new min());
math.set("modf", new modf()); math.set("modf", new modf());
math.set("pi", Math.PI ); math.set("pi", Math.PI);
math.set("pow", new pow()); math.set("pow", new pow());
random r; random r;
math.set("random", r = new random()); math.set("random", r = new random());
@@ -125,14 +146,16 @@ public class MathLib extends TwoArgFunction {
math.set("sqrt", new sqrt()); math.set("sqrt", new sqrt());
math.set("tan", new tan()); math.set("tan", new tan());
env.set("math", math); env.set("math", math);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("math", math); if (!env.get("package").isnil())
env.get("package").get("loaded").set("math", math);
return math; return math;
} }
abstract protected static class UnaryOp extends OneArgFunction { abstract protected static class UnaryOp extends OneArgFunction {
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return valueOf(call(arg.checkdouble())); return valueOf(call(arg.checkdouble()));
} }
abstract protected double call(double d); abstract protected double call(double d);
} }
@@ -140,43 +163,74 @@ public class MathLib extends TwoArgFunction {
public LuaValue call(LuaValue x, LuaValue y) { public LuaValue call(LuaValue x, LuaValue y) {
return valueOf(call(x.checkdouble(), y.checkdouble())); return valueOf(call(x.checkdouble(), y.checkdouble()));
} }
abstract protected double call(double x, double y); abstract protected double call(double x, double y);
} }
static final class abs extends UnaryOp { protected double call(double d) { return Math.abs(d); } } static final class abs extends UnaryOp {
static final class ceil extends UnaryOp { protected double call(double d) { return Math.ceil(d); } } protected double call(double d) { return Math.abs(d); }
static final class cos extends UnaryOp { protected double call(double d) { return Math.cos(d); } } }
static final class deg extends UnaryOp { protected double call(double d) { return Math.toDegrees(d); } }
static final class floor extends UnaryOp { protected double call(double d) { return Math.floor(d); } } static final class ceil extends UnaryOp {
static final class rad extends UnaryOp { protected double call(double d) { return Math.toRadians(d); } } protected double call(double d) { return Math.ceil(d); }
static final class sin extends UnaryOp { protected double call(double d) { return Math.sin(d); } } }
static final class sqrt extends UnaryOp { protected double call(double d) { return Math.sqrt(d); } }
static final class tan extends UnaryOp { protected double call(double d) { return Math.tan(d); } } static final class cos extends UnaryOp {
protected double call(double d) { return Math.cos(d); }
}
static final class deg extends UnaryOp {
protected double call(double d) { return Math.toDegrees(d); }
}
static final class floor extends UnaryOp {
protected double call(double d) { return Math.floor(d); }
}
static final class rad extends UnaryOp {
protected double call(double d) { return Math.toRadians(d); }
}
static final class sin extends UnaryOp {
protected double call(double d) { return Math.sin(d); }
}
static final class sqrt extends UnaryOp {
protected double call(double d) { return Math.sqrt(d); }
}
static final class tan extends UnaryOp {
protected double call(double d) { return Math.tan(d); }
}
static final class exp extends UnaryOp { static final class exp extends UnaryOp {
final MathLib mathlib; final MathLib mathlib;
exp(MathLib mathlib) { exp(MathLib mathlib) {
this.mathlib = mathlib; this.mathlib = mathlib;
} }
protected double call(double d) { protected double call(double d) {
return mathlib.dpow_lib(Math.E,d); return mathlib.dpow_lib(Math.E, d);
} }
} }
static final class fmod extends TwoArgFunction { static final class fmod extends TwoArgFunction {
public LuaValue call(LuaValue xv, LuaValue yv) { public LuaValue call(LuaValue xv, LuaValue yv) {
if (xv.islong() && yv.islong()) { if (xv.islong() && yv.islong()) {
return valueOf(xv.tolong() % yv.tolong()); return valueOf(xv.tolong()%yv.tolong());
} }
return valueOf(xv.checkdouble() % yv.checkdouble()); return valueOf(xv.checkdouble()%yv.checkdouble());
} }
} }
static final class ldexp extends BinaryOp { static final class ldexp extends BinaryOp {
protected double call(double x, double y) { protected double call(double x, double y) {
// This is the behavior on os-x, windows differs in rounding behavior. // This is the behavior on os-x, windows differs in rounding behavior.
return x * Double.longBitsToDouble((((long) y) + 1023) << 52); return x*Double.longBitsToDouble((((long) y)+1023)<<52);
} }
} }
static final class pow extends BinaryOp { static final class pow extends BinaryOp {
protected double call(double x, double y) { protected double call(double x, double y) {
return MathLib.dpow_default(x, y); return MathLib.dpow_default(x, y);
@@ -186,117 +240,127 @@ public class MathLib extends TwoArgFunction {
static class frexp extends VarArgFunction { static class frexp extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
double x = args.checkdouble(1); double x = args.checkdouble(1);
if ( x == 0 ) return varargsOf(ZERO,ZERO); if (x == 0)
long bits = Double.doubleToLongBits( x ); return varargsOf(ZERO, ZERO);
double m = ((bits & (~(-1L<<52))) + (1L<<52)) * ((bits >= 0)? (.5 / (1L<<52)): (-.5 / (1L<<52))); long bits = Double.doubleToLongBits(x);
double e = (((int) (bits >> 52)) & 0x7ff) - 1022; double m = ((bits & (~(-1L<<52)))+(1L<<52))*((bits >= 0)? (.5/(1L<<52)): (-.5/(1L<<52)));
return varargsOf( valueOf(m), valueOf(e) ); double e = (((int) (bits>>52)) & 0x7ff)-1022;
return varargsOf(valueOf(m), valueOf(e));
} }
} }
static class max extends VarArgFunction { static class max extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue m = args.checkvalue(1); LuaValue m = args.checkvalue(1);
for ( int i=2,n=args.narg(); i<=n; ++i ) { for (int i = 2, n = args.narg(); i <= n; ++i) {
LuaValue v = args.checkvalue(i); LuaValue v = args.checkvalue(i);
if (m.lt_b(v)) m = v; if (m.lt_b(v))
m = v;
} }
return m; return m;
} }
} }
static class min extends VarArgFunction { static class min extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue m = args.checkvalue(1); LuaValue m = args.checkvalue(1);
for ( int i=2,n=args.narg(); i<=n; ++i ) { for (int i = 2, n = args.narg(); i <= n; ++i) {
LuaValue v = args.checkvalue(i); LuaValue v = args.checkvalue(i);
if (v.lt_b(m)) m = v; if (v.lt_b(m))
m = v;
} }
return m; return m;
} }
} }
static class modf extends VarArgFunction { static class modf extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue n = args.arg1(); LuaValue n = args.arg1();
/* number is its own integer part, no fractional part */ /* number is its own integer part, no fractional part */
if (n.islong()) return varargsOf(n, valueOf(0.0)); if (n.islong())
return varargsOf(n, valueOf(0.0));
double x = n.checkdouble(); double x = n.checkdouble();
/* integer part (rounds toward zero) */ /* integer part (rounds toward zero) */
double intPart = ( x > 0 ) ? Math.floor( x ) : Math.ceil( x ); double intPart = (x > 0)? Math.floor(x): Math.ceil(x);
/* fractional part (test needed for inf/-inf) */ /* fractional part (test needed for inf/-inf) */
double fracPart = x == intPart ? 0.0 : x - intPart; double fracPart = x == intPart? 0.0: x-intPart;
return varargsOf( valueOf(intPart), valueOf(fracPart) ); return varargsOf(valueOf(intPart), valueOf(fracPart));
} }
} }
static class random extends LibFunction { static class random extends LibFunction {
Random random = new Random(); Random random = new Random();
public LuaValue call() { public LuaValue call() {
return valueOf( random.nextDouble() ); return valueOf(random.nextDouble());
} }
public LuaValue call(LuaValue a) { public LuaValue call(LuaValue a) {
int m = a.checkint(); int m = a.checkint();
if (m<1) argerror(1, "interval is empty"); if (m < 1)
return valueOf( 1 + random.nextInt(m) ); argerror(1, "interval is empty");
return valueOf(1+random.nextInt(m));
} }
public LuaValue call(LuaValue a, LuaValue b) { public LuaValue call(LuaValue a, LuaValue b) {
int m = a.checkint(); int m = a.checkint();
int n = b.checkint(); int n = b.checkint();
if (n<m) argerror(2, "interval is empty"); if (n < m)
return valueOf( m + random.nextInt(n+1-m) ); argerror(2, "interval is empty");
return valueOf(m+random.nextInt(n+1-m));
} }
} }
static class randomseed extends OneArgFunction { static class randomseed extends OneArgFunction {
final random random; final random random;
randomseed(random random) { randomseed(random random) {
this.random = random; this.random = random;
} }
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
long seed = arg.checklong(); long seed = arg.checklong();
random.random = new Random(seed); random.random = new Random(seed);
return NONE; return NONE;
} }
} }
/** compute power using installed math library, or default if there is no math library installed */ /**
* compute power using installed math library, or default if there is no
* math library installed
*/
public static LuaValue dpow(double a, double b) { public static LuaValue dpow(double a, double b) {
return LuaDouble.valueOf( return LuaDouble.valueOf(MATHLIB != null? MATHLIB.dpow_lib(a, b): dpow_default(a, b));
MATHLIB!=null?
MATHLIB.dpow_lib(a,b):
dpow_default(a,b) );
} }
public static double dpow_d(double a, double b) { public static double dpow_d(double a, double b) {
return MATHLIB!=null? return MATHLIB != null? MATHLIB.dpow_lib(a, b): dpow_default(a, b);
MATHLIB.dpow_lib(a,b):
dpow_default(a,b);
} }
/** /**
* Hook to override default dpow behavior with faster implementation. * Hook to override default dpow behavior with faster implementation.
*/ */
public double dpow_lib(double a, double b) { public double dpow_lib(double a, double b) {
return dpow_default(a,b); return dpow_default(a, b);
} }
/** /**
* Default JME version computes using longhand heuristics. * Default JME version computes using longhand heuristics.
*/ */
protected static double dpow_default(double a, double b) { protected static double dpow_default(double a, double b) {
if ( b < 0 ) if (b < 0)
return 1 / dpow_default( a, -b ); return 1/dpow_default(a, -b);
double p = 1; double p = 1;
int whole = (int) b; int whole = (int) b;
for ( double v=a; whole > 0; whole>>=1, v*=v ) for (double v = a; whole > 0; whole >>= 1, v *= v)
if ( (whole & 1) != 0 ) if ((whole & 1) != 0)
p *= v; p *= v;
if ( (b -= whole) > 0 ) { if ((b -= whole) > 0) {
int frac = (int) (0x10000 * b); int frac = (int) (0x10000*b);
for ( ; (frac&0xffff)!=0; frac<<=1 ) { for (; (frac & 0xffff) != 0; frac <<= 1) {
a = Math.sqrt(a); a = Math.sqrt(a);
if ( (frac & 0x8000) != 0 ) if ((frac & 0x8000) != 0)
p *= a; p *= a;
} }
} }

View File

@@ -24,21 +24,23 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take one argument and /**
* return one value. * Abstract base class for Java function implementations that take one argument
* and return one value.
* <p> * <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue)} to complete this class, * Subclasses need only implement {@link LuaValue#call(LuaValue)} to complete
* simplifying development. * this class, simplifying development. All other uses of {@link #call()},
* All other uses of {@link #call()}, {@link #invoke(Varargs)},etc, * {@link #invoke(Varargs)},etc, are routed through this method by this class,
* are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required. * dropping or extending arguments with {@code nil} values as required.
* <p> * <p>
* If more than one argument are required, or no arguments are required, * If more than one argument are required, or no arguments are required, or
* or variable argument or variable return values, * variable argument or variable return values, then use one of the related
* then use one of the related function * function {@link ZeroArgFunction}, {@link TwoArgFunction},
* {@link ZeroArgFunction}, {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}. * {@link ThreeArgFunction}, or {@link VarArgFunction}.
* <p> * <p>
* See {@link LibFunction} for more information on implementation libraries and library functions. * See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue) * @see #call(LuaValue)
* @see LibFunction * @see LibFunction
* @see ZeroArgFunction * @see ZeroArgFunction
@@ -49,11 +51,11 @@ import org.luaj.vm2.Varargs;
abstract public class OneArgFunction extends LibFunction { abstract public class OneArgFunction extends LibFunction {
abstract public LuaValue call(LuaValue arg); abstract public LuaValue call(LuaValue arg);
/** Default constructor */ /** Default constructor */
public OneArgFunction() { public OneArgFunction() {
} }
public final LuaValue call() { public final LuaValue call() {
return call(NIL); return call(NIL);
} }
@@ -69,4 +71,4 @@ abstract public class OneArgFunction extends LibFunction {
public Varargs invoke(Varargs varargs) { public Varargs invoke(Varargs varargs) {
return call(varargs.arg1()); return call(varargs.arg1());
} }
} }

View File

@@ -32,21 +32,21 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the standard lua {@code os} library. * Subclass of {@link LibFunction} which implements the standard lua {@code os}
* library.
* <p> * <p>
* It is a usable base with simplified stub functions * It is a usable base with simplified stub functions for library functions that
* for library functions that cannot be implemented uniformly * cannot be implemented uniformly on Jse and Jme.
* on Jse and Jme.
* <p> * <p>
* This can be installed as-is on either platform, or extended * This can be installed as-is on either platform, or extended and refined to be
* and refined to be used in a complete Jse implementation. * used in a complete Jse implementation.
* <p> * <p>
* Because the nature of the {@code os} library is to encapsulate * Because the nature of the {@code os} library is to encapsulate os-specific
* os-specific features, the behavior of these functions varies considerably * features, the behavior of these functions varies considerably from their
* from their counterparts in the C platform. * counterparts in the C platform.
* <p> * <p>
* The following functions have limited implementations of features * The following functions have limited implementations of features that are not
* that are not supported well on Jme: * supported well on Jme:
* <ul> * <ul>
* <li>{@code execute()}</li> * <li>{@code execute()}</li>
* <li>{@code remove()}</li> * <li>{@code remove()}</li>
@@ -55,33 +55,46 @@ import org.luaj.vm2.Varargs;
* </ul> * </ul>
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* <pre> {@code * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* Globals globals = JsePlatform.standardGlobals(); *
* System.out.println( globals.get("os").get("time").call() ); * <pre>
* } </pre> * {
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseOsLib} library will be loaded, which will include * &#64;code
* the base functionality provided by this class. * Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
*
* In this example the platform-specific {@link org.luaj.vm2.lib.jse.JseOsLib}
* library will be loaded, which will include the base functionality provided by
* this class.
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new OsLib()); * Globals globals = new Globals();
* System.out.println( globals.get("os").get("time").call() ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* globals.load(new OsLib());
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
* <p> * <p>
* @see LibFunction *
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JseOsLib * @see org.luaj.vm2.lib.jse.JseOsLib
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a> * @see <a href=
* "http://www.lua.org/manual/5.1/manual.html#5.8">http://www.lua.org/manual/5.1/manual.html#5.8</a>
*/ */
public class OsLib extends TwoArgFunction { public class OsLib extends TwoArgFunction {
public static final String TMP_PREFIX = ".luaj"; public static final String TMP_PREFIX = ".luaj";
public static final String TMP_SUFFIX = "tmp"; public static final String TMP_SUFFIX = "tmp";
private static final int CLOCK = 0; private static final int CLOCK = 0;
private static final int DATE = 1; private static final int DATE = 1;
@@ -95,36 +108,29 @@ public class OsLib extends TwoArgFunction {
private static final int TIME = 9; private static final int TIME = 9;
private static final int TMPNAME = 10; private static final int TMPNAME = 10;
private static final String[] NAMES = { private static final String[] NAMES = { "clock", "date", "difftime", "execute", "exit", "getenv", "remove",
"clock", "rename", "setlocale", "time", "tmpname", };
"date",
"difftime", private static final long t0 = System.currentTimeMillis();
"execute", private static long tmpnames = t0;
"exit",
"getenv",
"remove",
"rename",
"setlocale",
"time",
"tmpname",
};
private static final long t0 = System.currentTimeMillis();
private static long tmpnames = t0;
protected Globals globals; protected Globals globals;
/** /**
* Create and OsLib instance. * Create and OsLib instance.
*/ */
public OsLib() { public OsLib() {
} }
/** Perform one-time initialization on the library by creating a table /**
* containing the library functions, adding that table to the supplied environment, * Perform one-time initialization on the library by creating a table
* adding the table to package.loaded, and returning table as the return value. * containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance. * @param env the environment to load into, typically a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
@@ -132,7 +138,8 @@ public class OsLib extends TwoArgFunction {
for (int i = 0; i < NAMES.length; ++i) for (int i = 0; i < NAMES.length; ++i)
os.set(NAMES[i], new OsLibFunc(i, NAMES[i])); os.set(NAMES[i], new OsLibFunc(i, NAMES[i]));
env.set("os", os); env.set("os", os);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("os", os); if (!env.get("package").isnil())
env.get("package").get("loaded").set("os", os);
return os; return os;
} }
@@ -141,9 +148,10 @@ public class OsLib extends TwoArgFunction {
this.opcode = opcode; this.opcode = opcode;
this.name = name; this.name = name;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
try { try {
switch ( opcode ) { switch (opcode) {
case CLOCK: case CLOCK:
return valueOf(clock()); return valueOf(clock());
case DATE: { case DATE: {
@@ -151,7 +159,7 @@ public class OsLib extends TwoArgFunction {
double t = args.isnumber(2)? args.todouble(2): time(null); double t = args.isnumber(2)? args.todouble(2): time(null);
if (s.equals("*t")) { if (s.equals("*t")) {
Calendar d = Calendar.getInstance(); Calendar d = Calendar.getInstance();
d.setTime(new Date((long)(t*1000))); d.setTime(new Date((long) (t*1000)));
LuaTable tbl = LuaValue.tableOf(); LuaTable tbl = LuaValue.tableOf();
tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR))); tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR)));
tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1)); tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1));
@@ -164,10 +172,10 @@ public class OsLib extends TwoArgFunction {
tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d))); tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d)));
return tbl; return tbl;
} }
return valueOf( date(s, t==-1? time(null): t) ); return valueOf(date(s, t == -1? time(null): t));
} }
case DIFFTIME: case DIFFTIME:
return valueOf(difftime(args.checkdouble(1),args.checkdouble(2))); return valueOf(difftime(args.checkdouble(1), args.checkdouble(2)));
case EXECUTE: case EXECUTE:
return execute(args.optjstring(1, null)); return execute(args.optjstring(1, null));
case EXIT: case EXIT:
@@ -175,7 +183,7 @@ public class OsLib extends TwoArgFunction {
return NONE; return NONE;
case GETENV: { case GETENV: {
final String val = getenv(args.checkjstring(1)); final String val = getenv(args.checkjstring(1));
return val!=null? valueOf(val): NIL; return val != null? valueOf(val): NIL;
} }
case REMOVE: case REMOVE:
remove(args.checkjstring(1)); remove(args.checkjstring(1));
@@ -184,8 +192,8 @@ public class OsLib extends TwoArgFunction {
rename(args.checkjstring(1), args.checkjstring(2)); rename(args.checkjstring(1), args.checkjstring(2));
return LuaValue.TRUE; return LuaValue.TRUE;
case SETLOCALE: { case SETLOCALE: {
String s = setlocale(args.optjstring(1,null), args.optjstring(2, "all")); String s = setlocale(args.optjstring(1, null), args.optjstring(2, "all"));
return s!=null? valueOf(s): NIL; return s != null? valueOf(s): NIL;
} }
case TIME: case TIME:
return valueOf(time(args.opttable(1, null))); return valueOf(time(args.opttable(1, null)));
@@ -193,78 +201,79 @@ public class OsLib extends TwoArgFunction {
return valueOf(tmpname()); return valueOf(tmpname());
} }
return NONE; return NONE;
} catch ( IOException e ) { } catch (IOException e) {
return varargsOf(NIL, valueOf(e.getMessage())); return varargsOf(NIL, valueOf(e.getMessage()));
} }
} }
} }
/** /**
* @return an approximation of the amount in seconds of CPU time used by * @return an approximation of the amount in seconds of CPU time used by the
* the program. For luaj this simple returns the elapsed time since the * program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded. * OsLib class was loaded.
*/ */
protected double clock() { protected double clock() {
return (System.currentTimeMillis()-t0) / 1000.; return (System.currentTimeMillis()-t0)/1000.;
} }
/** /**
* Returns the number of seconds from time t1 to time t2. * Returns the number of seconds from time t1 to time t2. In POSIX, Windows,
* In POSIX, Windows, and some other systems, this value is exactly t2-t1. * and some other systems, this value is exactly t2-t1.
*
* @param t2 * @param t2
* @param t1 * @param t1
* @return diffeence in time values, in seconds * @return diffeence in time values, in seconds
*/ */
protected double difftime(double t2, double t1) { protected double difftime(double t2, double t1) {
return t2 - t1; return t2-t1;
} }
/** /**
* If the time argument is present, this is the time to be formatted * If the time argument is present, this is the time to be formatted (see
* (see the os.time function for a description of this value). * the os.time function for a description of this value). Otherwise, date
* Otherwise, date formats the current time. * formats the current time.
* *
* Date returns the date as a string, * Date returns the date as a string, formatted according to the same rules
* formatted according to the same rules as ANSII strftime, but without * as ANSII strftime, but without support for %g, %G, or %V.
* support for %g, %G, or %V.
* *
* When called without arguments, date returns a reasonable date and * When called without arguments, date returns a reasonable date and time
* time representation that depends on the host system and on the * representation that depends on the host system and on the current locale
* current locale (that is, os.date() is equivalent to os.date("%c")). * (that is, os.date() is equivalent to os.date("%c")).
* *
* @param format * @param format
* @param time time since epoch, or -1 if not supplied * @param time time since epoch, or -1 if not supplied
* @return a LString or a LTable containing date and time, * @return a LString or a LTable containing date and time, formatted
* formatted according to the given string format. * according to the given string format.
*/ */
public String date(String format, double time) { public String date(String format, double time) {
Calendar d = Calendar.getInstance(); Calendar d = Calendar.getInstance();
d.setTime(new Date((long)(time*1000))); d.setTime(new Date((long) (time*1000)));
if (format.startsWith("!")) { if (format.startsWith("!")) {
time -= timeZoneOffset(d); time -= timeZoneOffset(d);
d.setTime(new Date((long)(time*1000))); d.setTime(new Date((long) (time*1000)));
format = format.substring(1); format = format.substring(1);
} }
byte[] fmt = format.getBytes(); byte[] fmt = format.getBytes();
final int n = fmt.length; final int n = fmt.length;
Buffer result = new Buffer(n); Buffer result = new Buffer(n);
byte c; byte c;
for ( int i = 0; i < n; ) { for (int i = 0; i < n;) {
switch ( c = fmt[i++ ] ) { switch (c = fmt[i++]) {
case '\n': case '\n':
result.append( "\n" ); result.append("\n");
break; break;
default: default:
result.append( c ); result.append(c);
break; break;
case '%': case '%':
if (i >= n) break; if (i >= n)
switch ( c = fmt[i++ ] ) { break;
switch (c = fmt[i++]) {
default: default:
LuaValue.argerror(1, "invalid conversion specifier '%"+c+"'"); LuaValue.argerror(1, "invalid conversion specifier '%" + c + "'");
break; break;
case '%': case '%':
result.append( (byte)'%' ); result.append((byte) '%');
break; break;
case 'a': case 'a':
result.append(WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]); result.append(WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
@@ -292,7 +301,7 @@ public class OsLib extends TwoArgFunction {
break; break;
case 'j': { // day of year. case 'j': { // day of year.
Calendar y0 = beginningOfYear(d); Calendar y0 = beginningOfYear(d);
int dayOfYear = (int) ((d.getTime().getTime() - y0.getTime().getTime()) / (24 * 3600L * 1000L)); int dayOfYear = (int) ((d.getTime().getTime()-y0.getTime().getTime())/(24*3600L*1000L));
result.append(String.valueOf(1001+dayOfYear).substring(1)); result.append(String.valueOf(1001+dayOfYear).substring(1));
break; break;
} }
@@ -330,11 +339,11 @@ public class OsLib extends TwoArgFunction {
result.append(String.valueOf(d.get(Calendar.YEAR))); result.append(String.valueOf(d.get(Calendar.YEAR)));
break; break;
case 'z': { case 'z': {
final int tzo = timeZoneOffset(d) / 60; final int tzo = timeZoneOffset(d)/60;
final int a = Math.abs(tzo); final int a = Math.abs(tzo);
final String h = String.valueOf(100 + a / 60).substring(1); final String h = String.valueOf(100+a/60).substring(1);
final String m = String.valueOf(100 + a % 60).substring(1); final String m = String.valueOf(100+a%60).substring(1);
result.append((tzo>=0? "+": "-") + h + m); result.append((tzo >= 0? "+": "-")+h+m);
break; break;
} }
} }
@@ -342,11 +351,14 @@ public class OsLib extends TwoArgFunction {
} }
return result.tojstring(); return result.tojstring();
} }
private static final String[] WeekdayNameAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; private static final String[] WeekdayNameAbbrev = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; private static final String[] WeekdayName = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; "Friday", "Saturday" };
private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; private static final String[] MonthNameAbbrev = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec" };
private static final String[] MonthName = { "January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December" };
private Calendar beginningOfYear(Calendar d) { private Calendar beginningOfYear(Calendar d) {
Calendar y0 = Calendar.getInstance(); Calendar y0 = Calendar.getInstance();
@@ -359,42 +371,35 @@ public class OsLib extends TwoArgFunction {
y0.set(Calendar.MILLISECOND, 0); y0.set(Calendar.MILLISECOND, 0);
return y0; return y0;
} }
private int weekNumber(Calendar d, int startDay) { private int weekNumber(Calendar d, int startDay) {
Calendar y0 = beginningOfYear(d); Calendar y0 = beginningOfYear(d);
y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7); y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
if (y0.after(d)) { if (y0.after(d)) {
y0.set(Calendar.YEAR, y0.get(Calendar.YEAR) - 1); y0.set(Calendar.YEAR, y0.get(Calendar.YEAR)-1);
y0.set(Calendar.DAY_OF_MONTH, 1 + (startDay + 8 - y0.get(Calendar.DAY_OF_WEEK)) % 7); y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
} }
long dt = d.getTime().getTime() - y0.getTime().getTime(); long dt = d.getTime().getTime()-y0.getTime().getTime();
return 1 + (int) (dt / (7L * 24L * 3600L * 1000L)); return 1+(int) (dt/(7L*24L*3600L*1000L));
} }
private int timeZoneOffset(Calendar d) { private int timeZoneOffset(Calendar d) {
int localStandarTimeMillis = ( int localStandarTimeMillis = (d.get(Calendar.HOUR_OF_DAY)*3600+d.get(Calendar.MINUTE)*60+d.get(Calendar.SECOND))
d.get(Calendar.HOUR_OF_DAY) * 3600 + *1000;
d.get(Calendar.MINUTE) * 60 + return d.getTimeZone().getOffset(1, d.get(Calendar.YEAR), d.get(Calendar.MONTH), d.get(Calendar.DAY_OF_MONTH),
d.get(Calendar.SECOND)) * 1000; d.get(Calendar.DAY_OF_WEEK), localStandarTimeMillis)/1000;
return d.getTimeZone().getOffset(
1,
d.get(Calendar.YEAR),
d.get(Calendar.MONTH),
d.get(Calendar.DAY_OF_MONTH),
d.get(Calendar.DAY_OF_WEEK),
localStandarTimeMillis) / 1000;
} }
private boolean isDaylightSavingsTime(Calendar d) { private boolean isDaylightSavingsTime(Calendar d) {
return timeZoneOffset(d) != d.getTimeZone().getRawOffset() / 1000; return timeZoneOffset(d) != d.getTimeZone().getRawOffset()/1000;
} }
/** /**
* This function is equivalent to the C function system. * This function is equivalent to the C function system. It passes command
* It passes command to be executed by an operating system shell. * to be executed by an operating system shell. It returns a status code,
* It returns a status code, which is system-dependent. * which is system-dependent. If command is absent, then it returns nonzero
* If command is absent, then it returns nonzero if a shell * if a shell is available and zero otherwise.
* is available and zero otherwise. *
* @param command command to pass to the system * @param command command to pass to the system
*/ */
protected Varargs execute(String command) { protected Varargs execute(String command) {
@@ -402,7 +407,9 @@ public class OsLib extends TwoArgFunction {
} }
/** /**
* Calls the C function exit, with an optional code, to terminate the host program. * Calls the C function exit, with an optional code, to terminate the host
* program.
*
* @param code * @param code
*/ */
protected void exit(int code) { protected void exit(int code) {
@@ -410,19 +417,19 @@ public class OsLib extends TwoArgFunction {
} }
/** /**
* Returns the value of the process environment variable varname, * Returns the value of the process environment variable varname, or the
* or the System property value for varname, * System property value for varname, or null if the variable is not defined
* or null if the variable is not defined in either environment. * in either environment.
* *
* The default implementation, which is used by the JmePlatform, * The default implementation, which is used by the JmePlatform, only
* only queryies System.getProperty(). * queryies System.getProperty().
* *
* The JsePlatform overrides this behavior and returns the * The JsePlatform overrides this behavior and returns the environment
* environment variable value using System.getenv() if it exists, * variable value using System.getenv() if it exists, or the System property
* or the System property value if it does not. * value if it does not.
*
* A SecurityException may be thrown if access is not allowed for 'varname'.
* *
* A SecurityException may be thrown if access is not allowed
* for 'varname'.
* @param varname * @param varname
* @return String value, or null if not defined * @return String value, or null if not defined
*/ */
@@ -431,57 +438,57 @@ public class OsLib extends TwoArgFunction {
} }
/** /**
* Deletes the file or directory with the given name. * Deletes the file or directory with the given name. Directories must be
* Directories must be empty to be removed. * empty to be removed. If this function fails, it throws and IOException
* If this function fails, it throws and IOException
* *
* @param filename * @param filename
* @throws IOException if it fails * @throws IOException if it fails
*/ */
protected void remove(String filename) throws IOException { protected void remove(String filename) throws IOException {
throw new IOException( "not implemented" ); throw new IOException("not implemented");
} }
/** /**
* Renames file or directory named oldname to newname. * Renames file or directory named oldname to newname. If this function
* If this function fails,it throws and IOException * fails,it throws and IOException
* *
* @param oldname old file name * @param oldname old file name
* @param newname new file name * @param newname new file name
* @throws IOException if it fails * @throws IOException if it fails
*/ */
protected void rename(String oldname, String newname) throws IOException { protected void rename(String oldname, String newname) throws IOException {
throw new IOException( "not implemented" ); throw new IOException("not implemented");
} }
/** /**
* Sets the current locale of the program. locale is a string specifying * Sets the current locale of the program. locale is a string specifying a
* a locale; category is an optional string describing which category to change: * locale; category is an optional string describing which category to
* "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category * change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the
* is "all". * default category is "all".
* *
* If locale is the empty string, the current locale is set to an implementation- * If locale is the empty string, the current locale is set to an
* defined native locale. If locale is the string "C", the current locale is set * implementation- defined native locale. If locale is the string "C", the
* to the standard C locale. * current locale is set to the standard C locale.
* *
* When called with null as the first argument, this function only returns the * When called with null as the first argument, this function only returns
* name of the current locale for the given category. * the name of the current locale for the given category.
* *
* @param locale * @param locale
* @param category * @param category
* @return the name of the new locale, or null if the request * @return the name of the new locale, or null if the request cannot be
* cannot be honored. * honored.
*/ */
protected String setlocale(String locale, String category) { protected String setlocale(String locale, String category) {
return "C"; return "C";
} }
/** /**
* Returns the current time when called without arguments, * Returns the current time when called without arguments, or a time
* or a time representing the date and time specified by the given table. * representing the date and time specified by the given table. This table
* This table must have fields year, month, and day, * must have fields year, month, and day, and may have fields hour, min,
* and may have fields hour, min, sec, and isdst * sec, and isdst (for a description of these fields, see the os.date
* (for a description of these fields, see the os.date function). * function).
*
* @param table * @param table
* @return long value for the time * @return long value for the time
*/ */
@@ -500,7 +507,7 @@ public class OsLib extends TwoArgFunction {
c.set(Calendar.MILLISECOND, 0); c.set(Calendar.MILLISECOND, 0);
d = c.getTime(); d = c.getTime();
} }
return d.getTime() / 1000.; return d.getTime()/1000.;
} }
/** /**
@@ -508,16 +515,16 @@ public class OsLib extends TwoArgFunction {
* The file must be explicitly opened before its use and explicitly removed * The file must be explicitly opened before its use and explicitly removed
* when no longer needed. * when no longer needed.
* *
* On some systems (POSIX), this function also creates a file with that name, * On some systems (POSIX), this function also creates a file with that
* to avoid security risks. (Someone else might create the file with wrong * name, to avoid security risks. (Someone else might create the file with
* permissions in the time between getting the name and creating the file.) * wrong permissions in the time between getting the name and creating the
* You still have to open the file to use it and to remove it (even if you * file.) You still have to open the file to use it and to remove it (even
* do not use it). * if you do not use it).
* *
* @return String filename to use * @return String filename to use
*/ */
protected String tmpname() { protected String tmpname() {
synchronized ( OsLib.class ) { synchronized (OsLib.class) {
return TMP_PREFIX+(tmpnames++)+TMP_SUFFIX; return TMP_PREFIX+(tmpnames++)+TMP_SUFFIX;
} }
} }

View File

@@ -31,56 +31,73 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua standard package and module * Subclass of {@link LibFunction} which implements the lua standard package and
* library functions. * module library functions.
* *
* <h3>Lua Environment Variables</h3> * <h3>Lua Environment Variables</h3> The following variables are available to
* The following variables are available to lua scrips when this library has been loaded: * lua scrips when this library has been loaded:
* <ul> * <ul>
* <li><code>"package.loaded"</code> Lua table of loaded modules. * <li><code>"package.loaded"</code> Lua table of loaded modules.
* <li><code>"package.path"</code> Search path for lua scripts. * <li><code>"package.path"</code> Search path for lua scripts.
* <li><code>"package.preload"</code> Lua table of uninitialized preload functions. * <li><code>"package.preload"</code> Lua table of uninitialized preload
* <li><code>"package.searchers"</code> Lua table of functions that search for object to load. * functions.
* <li><code>"package.searchers"</code> Lua table of functions that search for
* object to load.
* </ul> * </ul>
* *
* <h3>Java Environment Variables</h3> * <h3>Java Environment Variables</h3> These Java environment variables affect
* These Java environment variables affect the library behavior: * the library behavior:
* <ul> * <ul>
* <li><code>"luaj.package.path"</code> Initial value for <code>"package.path"</code>. Default value is <code>"?.lua"</code> * <li><code>"luaj.package.path"</code> Initial value for
* <code>"package.path"</code>. Default value is <code>"?.lua"</code>
* </ul> * </ul>
* *
* <h3>Loading</h3> * <h3>Loading</h3> Typically, this library is included as part of a call to
* Typically, this library is included as part of a call to either * either {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code *
* <pre>
* {@code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("require").call"foo") ); * System.out.println( globals.get("require").call"foo") );
* } </pre> * }
* </pre>
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* System.out.println( globals.get("require").call("foo") ); * Globals globals = new Globals();
* } </pre> * globals.load(new JseBaseLib());
* <h3>Limitations</h3> * globals.load(new PackageLib());
* This library has been implemented to match as closely as possible the behavior in the corresponding library in C. * System.out.println(globals.get("require").call("foo"));
* However, the default filesystem search semantics are different and delegated to the bas library * }
* as outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib} documentation. * </pre>
*
* <h3>Limitations</h3> This library has been implemented to match as closely as
* possible the behavior in the corresponding library in C. However, the default
* filesystem search semantics are different and delegated to the bas library as
* outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib}
* documentation.
* <p> * <p>
*
* @see LibFunction * @see LibFunction
* @see BaseLib * @see BaseLib
* @see org.luaj.vm2.lib.jse.JseBaseLib * @see org.luaj.vm2.lib.jse.JseBaseLib
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.3">Lua 5.2 Package
* Lib Reference</a>
*/ */
public class PackageLib extends TwoArgFunction { public class PackageLib extends TwoArgFunction {
/** The default value to use for package.path. This can be set with the system property /**
* <code>"luaj.package.path"</code>, and is <code>"?.lua"</code> by default. */ * The default value to use for package.path. This can be set with the
* system property <code>"luaj.package.path"</code>, and is
* <code>"?.lua"</code> by default.
*/
public static final String DEFAULT_LUA_PATH; public static final String DEFAULT_LUA_PATH;
static { static {
String path = null; String path = null;
@@ -95,40 +112,49 @@ public class PackageLib extends TwoArgFunction {
DEFAULT_LUA_PATH = path; DEFAULT_LUA_PATH = path;
} }
static final LuaString _LOADED = valueOf("loaded"); static final LuaString _LOADED = valueOf("loaded");
private static final LuaString _LOADLIB = valueOf("loadlib"); private static final LuaString _LOADLIB = valueOf("loadlib");
static final LuaString _PRELOAD = valueOf("preload"); static final LuaString _PRELOAD = valueOf("preload");
static final LuaString _PATH = valueOf("path"); static final LuaString _PATH = valueOf("path");
static final LuaString _SEARCHPATH = valueOf("searchpath"); static final LuaString _SEARCHPATH = valueOf("searchpath");
static final LuaString _SEARCHERS = valueOf("searchers"); static final LuaString _SEARCHERS = valueOf("searchers");
/** The globals that were used to load this library. */ /** The globals that were used to load this library. */
Globals globals; Globals globals;
/** The table for this package. */ /** The table for this package. */
LuaTable package_; LuaTable package_;
/** Loader that loads from {@code preload} table if found there */ /** Loader that loads from {@code preload} table if found there */
public preload_searcher preload_searcher; public preload_searcher preload_searcher;
/** Loader that loads as a lua script using the lua path currently in {@link path} */ /**
* Loader that loads as a lua script using the lua path currently in
* {@link path}
*/
public lua_searcher lua_searcher; public lua_searcher lua_searcher;
/** Loader that loads as a Java class. Class must have public constructor and be a LuaValue. */ /**
* Loader that loads as a Java class. Class must have public constructor and
* be a LuaValue.
*/
public java_searcher java_searcher; public java_searcher java_searcher;
private static final LuaString _SENTINEL = valueOf("\u0001"); private static final LuaString _SENTINEL = valueOf("\u0001");
private static final String FILE_SEP = System.getProperty("file.separator"); private static final String FILE_SEP = System.getProperty("file.separator");
public PackageLib() {} public PackageLib() {}
/** Perform one-time initialization on the library by adding package functions /**
* to the supplied environment, and returning it as the return value. * Perform one-time initialization on the library by adding package
* It also creates the package.preload and package.loaded tables for use by * functions to the supplied environment, and returning it as the return
* other libraries. * value. It also creates the package.preload and package.loaded tables for
* use by other libraries.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance. * @param env the environment to load into, typically a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
@@ -141,8 +167,8 @@ public class PackageLib extends TwoArgFunction {
package_.set(_SEARCHPATH, new searchpath()); package_.set(_SEARCHPATH, new searchpath());
LuaTable searchers = new LuaTable(); LuaTable searchers = new LuaTable();
searchers.set(1, preload_searcher = new preload_searcher()); searchers.set(1, preload_searcher = new preload_searcher());
searchers.set(2, lua_searcher = new lua_searcher()); searchers.set(2, lua_searcher = new lua_searcher());
searchers.set(3, java_searcher = new java_searcher()); searchers.set(3, java_searcher = new java_searcher());
package_.set(_SEARCHERS, searchers); package_.set(_SEARCHERS, searchers);
package_.set("config", FILE_SEP + "\n;\n?\n!\n-\n"); package_.set("config", FILE_SEP + "\n;\n?\n!\n-\n");
package_.get(_LOADED).set("package", package_); package_.get(_LOADED).set("package", package_);
@@ -150,94 +176,100 @@ public class PackageLib extends TwoArgFunction {
globals.package_ = this; globals.package_ = this;
return env; return env;
} }
/** Allow packages to mark themselves as loaded */ /** Allow packages to mark themselves as loaded */
public void setIsLoaded(String name, LuaTable value) { public void setIsLoaded(String name, LuaTable value) {
package_.get(_LOADED).set(name, value); package_.get(_LOADED).set(name, value);
} }
/**
/** Set the lua path used by this library instance to a new value. * Set the lua path used by this library instance to a new value. Merely
* Merely sets the value of {@link path} to be used in subsequent searches. */ * sets the value of {@link path} to be used in subsequent searches.
public void setLuaPath( String newLuaPath ) { */
public void setLuaPath(String newLuaPath) {
package_.set(_PATH, LuaValue.valueOf(newLuaPath)); package_.set(_PATH, LuaValue.valueOf(newLuaPath));
} }
public String tojstring() { public String tojstring() {
return "package"; return "package";
} }
// ======================== Package loading ============================= // ======================== Package loading =============================
/** /**
* require (modname) * require (modname)
* *
* Loads the given module. The function starts by looking into the package.loaded table * Loads the given module. The function starts by looking into the
* to determine whether modname is already loaded. If it is, then require returns the value * package.loaded table to determine whether modname is already loaded. If
* stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module. * it is, then require returns the value stored at package.loaded[modname].
* Otherwise, it tries to find a loader for the module.
* *
* To find a loader, require is guided by the package.searchers sequence. * To find a loader, require is guided by the package.searchers sequence. By
* By changing this sequence, we can change how require looks for a module. * changing this sequence, we can change how require looks for a module. The
* The following explanation is based on the default configuration for package.searchers. * following explanation is based on the default configuration for
* package.searchers.
* *
* First require queries package.preload[modname]. If it has a value, this value * First require queries package.preload[modname]. If it has a value, this
* (which should be a function) is the loader. Otherwise require searches for a Lua loader using * value (which should be a function) is the loader. Otherwise require
* the path stored in package.path. If that also fails, it searches for a Java loader using * searches for a Lua loader using the path stored in package.path. If that
* the classpath, using the public default constructor, and casting the instance to LuaFunction. * also fails, it searches for a Java loader using the classpath, using the
* public default constructor, and casting the instance to LuaFunction.
* *
* Once a loader is found, require calls the loader with two arguments: modname and an extra value * Once a loader is found, require calls the loader with two arguments:
* dependent on how it got the loader. If the loader came from a file, this extra value is the file name. * modname and an extra value dependent on how it got the loader. If the
* If the loader is a Java instance of LuaFunction, this extra value is the environment. * loader came from a file, this extra value is the file name. If the loader
* If the loader returns any non-nil value, require assigns the returned value to package.loaded[modname]. * is a Java instance of LuaFunction, this extra value is the environment.
* If the loader does not return a non-nil value and has not assigned any value to package.loaded[modname], * If the loader returns any non-nil value, require assigns the returned
* then require assigns true to this entry. * value to package.loaded[modname]. If the loader does not return a non-nil
* In any case, require returns the final value of package.loaded[modname]. * value and has not assigned any value to package.loaded[modname], then
* require assigns true to this entry. In any case, require returns the
* final value of package.loaded[modname].
* *
* If there is any error loading or running the module, or if it cannot find any loader for the module, * If there is any error loading or running the module, or if it cannot find
* then require raises an error. * any loader for the module, then require raises an error.
*/ */
public class require extends OneArgFunction { public class require extends OneArgFunction {
public LuaValue call( LuaValue arg ) { public LuaValue call(LuaValue arg) {
LuaString name = arg.checkstring(); LuaString name = arg.checkstring();
LuaValue loaded = package_.get(_LOADED); LuaValue loaded = package_.get(_LOADED);
LuaValue result = loaded.get(name); LuaValue result = loaded.get(name);
if ( result.toboolean() ) { if (result.toboolean()) {
if ( result == _SENTINEL ) if (result == _SENTINEL)
error("loop or previous error loading module '"+name+"'"); error("loop or previous error loading module '" + name + "'");
return result; return result;
} }
/* else must load it; iterate over available loaders */ /* else must load it; iterate over available loaders */
LuaTable tbl = package_.get(_SEARCHERS).checktable(); LuaTable tbl = package_.get(_SEARCHERS).checktable();
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
Varargs loader = null; Varargs loader = null;
for ( int i=1; true; i++ ) { for (int i = 1; true; i++) {
LuaValue searcher = tbl.get(i); LuaValue searcher = tbl.get(i);
if ( searcher.isnil() ) { if (searcher.isnil()) {
error( "module '"+name+"' not found: "+name+sb ); error("module '" + name + "' not found: " + name + sb);
} }
/* call loader with module name as argument */ /* call loader with module name as argument */
loader = searcher.invoke(name); loader = searcher.invoke(name);
if ( loader.isfunction(1) ) if (loader.isfunction(1))
break; break;
if ( loader.isstring(1) ) if (loader.isstring(1))
sb.append( loader.tojstring(1) ); sb.append(loader.tojstring(1));
} }
// load the module using the loader // load the module using the loader
loaded.set(name, _SENTINEL); loaded.set(name, _SENTINEL);
result = loader.arg1().call(name, loader.arg(2)); result = loader.arg1().call(name, loader.arg(2));
if ( ! result.isnil() ) if (!result.isnil())
loaded.set( name, result ); loaded.set(name, result);
else if ( (result = loaded.get(name)) == _SENTINEL ) else if ((result = loaded.get(name)) == _SENTINEL)
loaded.set( name, result = LuaValue.TRUE ); loaded.set(name, result = LuaValue.TRUE);
return result; return result;
} }
} }
public static class loadlib extends VarArgFunction { public static class loadlib extends VarArgFunction {
public Varargs invoke( Varargs args ) { public Varargs invoke(Varargs args) {
args.checkstring(1); args.checkstring(1);
return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent")); return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent"));
} }
@@ -247,36 +279,34 @@ public class PackageLib extends TwoArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1); LuaString name = args.checkstring(1);
LuaValue val = package_.get(_PRELOAD).get(name); LuaValue val = package_.get(_PRELOAD).get(name);
return val.isnil()? return val.isnil()? valueOf("\n\tno field package.preload['" + name + "']"): val;
valueOf("\n\tno field package.preload['"+name+"']"):
val;
} }
} }
public class lua_searcher extends VarArgFunction { public class lua_searcher extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaString name = args.checkstring(1); LuaString name = args.checkstring(1);
// get package path // get package path
LuaValue path = package_.get(_PATH); LuaValue path = package_.get(_PATH);
if ( ! path.isstring() ) if (!path.isstring())
return valueOf("package.path is not a string"); return valueOf("package.path is not a string");
// get the searchpath function. // get the searchpath function.
Varargs v = package_.get(_SEARCHPATH).invoke(varargsOf(name, path)); Varargs v = package_.get(_SEARCHPATH).invoke(varargsOf(name, path));
// Did we get a result? // Did we get a result?
if (!v.isstring(1)) if (!v.isstring(1))
return v.arg(2).tostring(); return v.arg(2).tostring();
LuaString filename = v.arg1().strvalue(); LuaString filename = v.arg1().strvalue();
// Try to load the file. // Try to load the file.
v = globals.loadfile(filename.tojstring()); v = globals.loadfile(filename.tojstring());
if ( v.arg1().isfunction() ) if (v.arg1().isfunction())
return LuaValue.varargsOf(v.arg1(), filename); return LuaValue.varargsOf(v.arg1(), filename);
// report error // report error
return varargsOf(NIL, valueOf("'"+filename+"': "+v.arg(2).tojstring())); return varargsOf(NIL, valueOf("'" + filename + "': " + v.arg(2).tojstring()));
} }
} }
@@ -286,90 +316,91 @@ public class PackageLib extends TwoArgFunction {
String path = args.checkjstring(2); String path = args.checkjstring(2);
String sep = args.optjstring(3, "."); String sep = args.optjstring(3, ".");
String rep = args.optjstring(4, FILE_SEP); String rep = args.optjstring(4, FILE_SEP);
// check the path elements // check the path elements
int e = -1; int e = -1;
int n = path.length(); int n = path.length();
StringBuffer sb = null; StringBuffer sb = null;
name = name.replace(sep.charAt(0), rep.charAt(0)); name = name.replace(sep.charAt(0), rep.charAt(0));
while ( e < n ) { while ( e < n ) {
// find next template // find next template
int b = e+1; int b = e+1;
e = path.indexOf(';',b); e = path.indexOf(';', b);
if ( e < 0 ) if (e < 0)
e = path.length(); e = path.length();
String template = path.substring(b,e); String template = path.substring(b, e);
// create filename // create filename
int q = template.indexOf('?'); int q = template.indexOf('?');
String filename = template; String filename = template;
if ( q >= 0 ) { if (q >= 0) {
filename = template.substring(0,q) + name + template.substring(q+1); filename = template.substring(0, q)+name+template.substring(q+1);
} }
// try opening the file // try opening the file
InputStream is = globals.finder.findResource(filename); InputStream is = globals.finder.findResource(filename);
if (is != null) { if (is != null) {
try { is.close(); } catch ( java.io.IOException ioe ) {} try {
is.close();
} catch (java.io.IOException ioe) {
}
return valueOf(filename); return valueOf(filename);
} }
// report error // report error
if ( sb == null ) if (sb == null)
sb = new StringBuffer(); sb = new StringBuffer();
sb.append( "\n\t"+filename ); sb.append("\n\t" + filename);
} }
return varargsOf(NIL, valueOf(sb.toString())); return varargsOf(NIL, valueOf(sb.toString()));
} }
} }
public class java_searcher extends VarArgFunction { public class java_searcher extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
String name = args.checkjstring(1); String name = args.checkjstring(1);
String classname = toClassname( name ); String classname = toClassname(name);
Class c = null; Class c = null;
LuaValue v = null; LuaValue v = null;
try { try {
c = Class.forName(classname); c = Class.forName(classname);
v = (LuaValue) c.newInstance(); v = (LuaValue) c.newInstance();
if (v.isfunction()) if (v.isfunction())
((LuaFunction)v).initupvalue1(globals); ((LuaFunction) v).initupvalue1(globals);
return varargsOf(v, globals); return varargsOf(v, globals);
} catch ( ClassNotFoundException cnfe ) { } catch (ClassNotFoundException cnfe) {
return valueOf("\n\tno class '"+classname+"'" ); return valueOf("\n\tno class '" + classname + "'");
} catch ( Exception e ) { } catch (Exception e) {
return valueOf("\n\tjava load failed on '"+classname+"', "+e ); return valueOf("\n\tjava load failed on '" + classname + "', " + e);
} }
} }
} }
/** Convert lua filename to valid class name */ /** Convert lua filename to valid class name */
public static final String toClassname( String filename ) { public static final String toClassname(String filename) {
int n=filename.length(); int n = filename.length();
int j=n; int j = n;
if ( filename.endsWith(".lua") ) if (filename.endsWith(".lua"))
j -= 4; j -= 4;
for ( int k=0; k<j; k++ ) { for (int k = 0; k < j; k++) {
char c = filename.charAt(k); char c = filename.charAt(k);
if ( (!isClassnamePart(c)) || (c=='/') || (c=='\\') ) { if ((!isClassnamePart(c)) || (c == '/') || (c == '\\')) {
StringBuffer sb = new StringBuffer(j); StringBuffer sb = new StringBuffer(j);
for ( int i=0; i<j; i++ ) { for (int i = 0; i < j; i++) {
c = filename.charAt(i); c = filename.charAt(i);
sb.append( sb.append((isClassnamePart(c))? c: ((c == '/') || (c == '\\'))? '.': '_');
(isClassnamePart(c))? c:
((c=='/') || (c=='\\'))? '.': '_' );
} }
return sb.toString(); return sb.toString();
} }
} }
return n==j? filename: filename.substring(0,j); return n == j? filename: filename.substring(0, j);
} }
private static final boolean isClassnamePart(char c) { private static final boolean isClassnamePart(char c) {
if ( (c>='a'&&c<='z') || (c>='A'&&c<='Z') || (c>='0'&&c<='9') ) if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
return true; return true;
switch ( c ) { switch (c) {
case '.': case '.':
case '$': case '$':
case '_': case '_':

View File

@@ -25,35 +25,36 @@ import java.io.InputStream;
import org.luaj.vm2.Globals; import org.luaj.vm2.Globals;
/** /**
* Interface for opening application resource files such as scripts sources. * Interface for opening application resource files such as scripts sources.
* <p> * <p>
* This is used by required to load files that are part of * This is used by required to load files that are part of the application, and
* the application, and implemented by BaseLib * implemented by BaseLib for both the Jme and Jse platforms.
* for both the Jme and Jse platforms.
* <p> * <p>
* The Jme version of base lib {@link BaseLib} * The Jme version of base lib {@link BaseLib} implements {@link Globals#finder}
* implements {@link Globals#finder} via {@link Class#getResourceAsStream(String)}, * via {@link Class#getResourceAsStream(String)}, while the Jse version
* while the Jse version {@link org.luaj.vm2.lib.jse.JseBaseLib} implements it using {@link java.io.File#File(String)}. * {@link org.luaj.vm2.lib.jse.JseBaseLib} implements it using
* {@link java.io.File#File(String)}.
* <p> * <p>
* The io library does not use this API for file manipulation. * The io library does not use this API for file manipulation.
* <p> * <p>
*
* @see BaseLib * @see BaseLib
* @see Globals#finder * @see Globals#finder
* @see org.luaj.vm2.lib.jse.JseBaseLib * @see org.luaj.vm2.lib.jse.JseBaseLib
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
*/ */
public interface ResourceFinder { public interface ResourceFinder {
/** /**
* Try to open a file, or return null if not found. * Try to open a file, or return null if not found.
* *
* @see org.luaj.vm2.lib.BaseLib * @see org.luaj.vm2.lib.BaseLib
* @see org.luaj.vm2.lib.jse.JseBaseLib * @see org.luaj.vm2.lib.jse.JseBaseLib
* *
* @param filename * @param filename
* @return InputStream, or null if not found. * @return InputStream, or null if not found.
*/ */
public InputStream findResource( String filename ); public InputStream findResource(String filename);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -26,40 +26,56 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua standard {@code table} * Subclass of {@link LibFunction} which implements the lua standard
* library. * {@code table} library.
* *
* <p> * <p>
* Typically, this library is included as part of a call to either * Typically, this library is included as part of a call to either
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} or
* <pre> {@code * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* Globals globals = JsePlatform.standardGlobals(); *
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) ); * <pre>
* } </pre> * {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* }
* </pre>
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as: * {@link LuaValue#load(LuaValue)} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new TableLib()); * Globals globals = new Globals();
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* globals.load(new TableLib());
* System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* }
* </pre>
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.5">Lua 5.2 Table Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.5">Lua 5.2 Table
* Lib Reference</a>
*/ */
public class TableLib extends TwoArgFunction { public class TableLib extends TwoArgFunction {
/** Perform one-time initialization on the library by creating a table /**
* containing the library functions, adding that table to the supplied environment, * Perform one-time initialization on the library by creating a table
* adding the table to package.loaded, and returning table as the return value. * containing the library functions, adding that table to the supplied
* environment, adding the table to package.loaded, and returning table as
* the return value.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, typically a Globals instance. * @param env the environment to load into, typically a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable table = new LuaTable(); LuaTable table = new LuaTable();
@@ -70,23 +86,27 @@ public class TableLib extends TwoArgFunction {
table.set("sort", new sort()); table.set("sort", new sort());
table.set("unpack", new unpack()); table.set("unpack", new unpack());
env.set("table", table); env.set("table", table);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("table", table); if (!env.get("package").isnil())
env.get("package").get("loaded").set("table", table);
return NIL; return NIL;
} }
// "concat" (table [, sep [, i [, j]]]) -> string // "concat" (table [, sep [, i [, j]]]) -> string
static class concat extends TableLibFunction { static class concat extends TableLibFunction {
public LuaValue call(LuaValue list) { public LuaValue call(LuaValue list) {
return list.checktable().concat(EMPTYSTRING,1,list.length()); return list.checktable().concat(EMPTYSTRING, 1, list.length());
} }
public LuaValue call(LuaValue list, LuaValue sep) { public LuaValue call(LuaValue list, LuaValue sep) {
return list.checktable().concat(sep.checkstring(),1,list.length()); return list.checktable().concat(sep.checkstring(), 1, list.length());
} }
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i) { public LuaValue call(LuaValue list, LuaValue sep, LuaValue i) {
return list.checktable().concat(sep.checkstring(),i.checkint(),list.length()); return list.checktable().concat(sep.checkstring(), i.checkint(), list.length());
} }
public LuaValue call(LuaValue list, LuaValue sep, LuaValue i, LuaValue j) { public LuaValue call(LuaValue list, LuaValue sep, LuaValue i, LuaValue j) {
return list.checktable().concat(sep.checkstring(),i.checkint(),j.checkint()); return list.checktable().concat(sep.checkstring(), i.checkint(), j.checkint());
} }
} }
@@ -96,14 +116,15 @@ public class TableLib extends TwoArgFunction {
switch (args.narg()) { switch (args.narg()) {
case 2: { case 2: {
LuaTable table = args.checktable(1); LuaTable table = args.checktable(1);
table.insert(table.length()+1,args.arg(2)); table.insert(table.length()+1, args.arg(2));
return NONE; return NONE;
} }
case 3: { case 3: {
LuaTable table = args.checktable(1); LuaTable table = args.checktable(1);
int pos = args.checkint(2); int pos = args.checkint(2);
int max = table.length() + 1; int max = table.length()+1;
if (pos < 1 || pos > max) argerror(2, "position out of bounds: " + pos + " not between 1 and " + max); if (pos < 1 || pos > max)
argerror(2, "position out of bounds: " + pos + " not between 1 and " + max);
table.insert(pos, args.arg(3)); table.insert(pos, args.arg(3));
return NONE; return NONE;
} }
@@ -113,7 +134,7 @@ public class TableLib extends TwoArgFunction {
} }
} }
} }
// "pack" (...) -> table // "pack" (...) -> table
static class pack extends VarArgFunction { static class pack extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
@@ -129,8 +150,8 @@ public class TableLib extends TwoArgFunction {
LuaTable table = args.checktable(1); LuaTable table = args.checktable(1);
int size = table.length(); int size = table.length();
int pos = args.optint(2, size); int pos = args.optint(2, size);
if (pos != size && (pos < 1 || pos > size + 1)) { if (pos != size && (pos < 1 || pos > size+1)) {
argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size + 1)); argerror(2, "position out of bounds: " + pos + " not between 1 and " + (size+1));
} }
return table.remove(pos); return table.remove(pos);
} }
@@ -139,19 +160,17 @@ public class TableLib extends TwoArgFunction {
// "sort" (table [, comp]) // "sort" (table [, comp])
static class sort extends VarArgFunction { static class sort extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
args.checktable(1).sort( args.checktable(1).sort(args.isnil(2)? NIL: args.checkfunction(2));
args.isnil(2)? NIL: args.checkfunction(2));
return NONE; return NONE;
} }
} }
// "unpack", // (list [,i [,j]]) -> result1, ... // "unpack", // (list [,i [,j]]) -> result1, ...
static class unpack extends VarArgFunction { static class unpack extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaTable t = args.checktable(1); LuaTable t = args.checktable(1);
// do not waste resource for calc rawlen if arg3 is not nil // do not waste resource for calc rawlen if arg3 is not nil
int len = args.arg(3).isnil() ? t.length() : 0; int len = args.arg(3).isnil()? t.length(): 0;
return t.unpack(args.optint(2, 1), args.optint(3, len)); return t.unpack(args.optint(2, 1), args.optint(3, len));
} }
} }

View File

@@ -24,21 +24,24 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take two arguments and /**
* return one value. * Abstract base class for Java function implementations that take two arguments
* and return one value.
* <p> * <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue,LuaValue)} to complete this class, * Subclasses need only implement
* simplifying development. * {@link LuaValue#call(LuaValue,LuaValue,LuaValue)} to complete this class,
* All other uses of {@link #call()}, {@link #invoke(Varargs)},etc, * simplifying development. All other uses of {@link #call()},
* are routed through this method by this class, * {@link #invoke(Varargs)},etc, are routed through this method by this class,
* dropping or extending arguments with {@code nil} values as required. * dropping or extending arguments with {@code nil} values as required.
* <p> * <p>
* If more or less than three arguments are required, * If more or less than three arguments are required, or variable argument or
* or variable argument or variable return values, * variable return values, then use one of the related function
* then use one of the related function * {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link VarArgFunction}. * {@link VarArgFunction}.
* <p> * <p>
* See {@link LibFunction} for more information on implementation libraries and library functions. * See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue,LuaValue,LuaValue) * @see #call(LuaValue,LuaValue,LuaValue)
* @see LibFunction * @see LibFunction
* @see ZeroArgFunction * @see ZeroArgFunction
@@ -49,11 +52,11 @@ import org.luaj.vm2.Varargs;
abstract public class ThreeArgFunction extends LibFunction { abstract public class ThreeArgFunction extends LibFunction {
abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3); abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3);
/** Default constructor */ /** Default constructor */
public ThreeArgFunction() { public ThreeArgFunction() {
} }
public final LuaValue call() { public final LuaValue call() {
return call(NIL, NIL, NIL); return call(NIL, NIL, NIL);
} }
@@ -65,9 +68,9 @@ abstract public class ThreeArgFunction extends LibFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(arg1, arg2, NIL); return call(arg1, arg2, NIL);
} }
public Varargs invoke(Varargs varargs) { public Varargs invoke(Varargs varargs) {
return call(varargs.arg1(),varargs.arg(2),varargs.arg(3)); return call(varargs.arg1(), varargs.arg(2), varargs.arg(3));
} }
} }

View File

@@ -24,21 +24,24 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take two arguments and /**
* return one value. * Abstract base class for Java function implementations that take two arguments
* and return one value.
* <p> * <p>
* Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue)} to complete this class, * Subclasses need only implement {@link LuaValue#call(LuaValue,LuaValue)} to
* simplifying development. * complete this class, simplifying development. All other uses of
* All other uses of {@link #call()}, {@link #invoke(Varargs)},etc, * {@link #call()}, {@link #invoke(Varargs)},etc, are routed through this method
* are routed through this method by this class, * by this class, dropping or extending arguments with {@code nil} values as
* dropping or extending arguments with {@code nil} values as required. * required.
* <p> * <p>
* If more or less than two arguments are required, * If more or less than two arguments are required, or variable argument or
* or variable argument or variable return values, * variable return values, then use one of the related function
* then use one of the related function * {@link ZeroArgFunction}, {@link OneArgFunction}, {@link ThreeArgFunction}, or
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}. * {@link VarArgFunction}.
* <p> * <p>
* See {@link LibFunction} for more information on implementation libraries and library functions. * See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call(LuaValue,LuaValue) * @see #call(LuaValue,LuaValue)
* @see LibFunction * @see LibFunction
* @see ZeroArgFunction * @see ZeroArgFunction
@@ -49,11 +52,11 @@ import org.luaj.vm2.Varargs;
abstract public class TwoArgFunction extends LibFunction { abstract public class TwoArgFunction extends LibFunction {
abstract public LuaValue call(LuaValue arg1, LuaValue arg2); abstract public LuaValue call(LuaValue arg1, LuaValue arg2);
/** Default constructor */ /** Default constructor */
public TwoArgFunction() { public TwoArgFunction() {
} }
public final LuaValue call() { public final LuaValue call() {
return call(NIL, NIL); return call(NIL, NIL);
} }
@@ -65,9 +68,9 @@ abstract public class TwoArgFunction extends LibFunction {
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call(arg1, arg2); return call(arg1, arg2);
} }
public Varargs invoke(Varargs varargs) { public Varargs invoke(Varargs varargs) {
return call(varargs.arg1(),varargs.arg(2)); return call(varargs.arg1(), varargs.arg(2));
} }
} }

View File

@@ -24,20 +24,23 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that takes varaiable arguments and /**
* returns multiple return values. * Abstract base class for Java function implementations that takes varaiable
* arguments and returns multiple return values.
* <p> * <p>
* Subclasses need only implement {@link LuaValue#invoke(Varargs)} to complete this class, * Subclasses need only implement {@link LuaValue#invoke(Varargs)} to complete
* simplifying development. * this class, simplifying development. All other uses of
* All other uses of {@link #call(LuaValue)}, {@link #invoke()},etc, * {@link #call(LuaValue)}, {@link #invoke()},etc, are routed through this
* are routed through this method by this class, * method by this class, converting arguments to {@link Varargs} and dropping or
* converting arguments to {@link Varargs} and * extending return values with {@code nil} values as required.
* dropping or extending return values with {@code nil} values as required.
* <p> * <p>
* If between one and three arguments are required, and only one return value is returned, * If between one and three arguments are required, and only one return value is
* {@link ZeroArgFunction}, {@link OneArgFunction}, {@link TwoArgFunction}, or {@link ThreeArgFunction}. * returned, {@link ZeroArgFunction}, {@link OneArgFunction},
* {@link TwoArgFunction}, or {@link ThreeArgFunction}.
* <p> * <p>
* See {@link LibFunction} for more information on implementation libraries and library functions. * See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #invoke(Varargs) * @see #invoke(Varargs)
* @see LibFunction * @see LibFunction
* @see ZeroArgFunction * @see ZeroArgFunction
@@ -49,7 +52,7 @@ abstract public class VarArgFunction extends LibFunction {
public VarArgFunction() { public VarArgFunction() {
} }
public LuaValue call() { public LuaValue call() {
return invoke(NONE).arg1(); return invoke(NONE).arg1();
} }
@@ -59,25 +62,25 @@ abstract public class VarArgFunction extends LibFunction {
} }
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return invoke(varargsOf(arg1,arg2)).arg1(); return invoke(varargsOf(arg1, arg2)).arg1();
} }
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return invoke(varargsOf(arg1,arg2,arg3)).arg1(); return invoke(varargsOf(arg1, arg2, arg3)).arg1();
} }
/** /**
* Subclass responsibility. * Subclass responsibility. May not have expected behavior for tail calls.
* May not have expected behavior for tail calls. * Should not be used if: - function has a possibility of returning a
* Should not be used if: * TailcallVarargs
* - function has a possibility of returning a TailcallVarargs *
* @param args the arguments to the function call. * @param args the arguments to the function call.
*/ */
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return onInvoke(args).eval(); return onInvoke(args).eval();
} }
public Varargs onInvoke(Varargs args) { public Varargs onInvoke(Varargs args) {
return invoke(args); return invoke(args);
} }
} }

View File

@@ -24,19 +24,21 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** Abstract base class for Java function implementations that take no arguments and /**
* return one value. * Abstract base class for Java function implementations that take no arguments
* and return one value.
* <p> * <p>
* Subclasses need only implement {@link LuaValue#call()} to complete this class, * Subclasses need only implement {@link LuaValue#call()} to complete this
* simplifying development. * class, simplifying development. All other uses of {@link #call(LuaValue)},
* All other uses of {@link #call(LuaValue)}, {@link #invoke(Varargs)},etc, * {@link #invoke(Varargs)},etc, are routed through this method by this class.
* are routed through this method by this class.
* <p> * <p>
* If one or more arguments are required, or variable argument or variable return values, * If one or more arguments are required, or variable argument or variable
* then use one of the related function * return values, then use one of the related function {@link OneArgFunction},
* {@link OneArgFunction}, {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}. * {@link TwoArgFunction}, {@link ThreeArgFunction}, or {@link VarArgFunction}.
* <p> * <p>
* See {@link LibFunction} for more information on implementation libraries and library functions. * See {@link LibFunction} for more information on implementation libraries and
* library functions.
*
* @see #call() * @see #call()
* @see LibFunction * @see LibFunction
* @see OneArgFunction * @see OneArgFunction
@@ -51,7 +53,7 @@ abstract public class ZeroArgFunction extends LibFunction {
/** Default constructor */ /** Default constructor */
public ZeroArgFunction() { public ZeroArgFunction() {
} }
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return call(); return call();
} }
@@ -67,4 +69,4 @@ abstract public class ZeroArgFunction extends LibFunction {
public Varargs invoke(Varargs varargs) { public Varargs invoke(Varargs varargs) {
return call(); return call();
} }
} }

View File

@@ -34,61 +34,74 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.IoLib; import org.luaj.vm2.lib.IoLib;
import org.luaj.vm2.lib.LibFunction; import org.luaj.vm2.lib.LibFunction;
/** /**
* Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io} * Subclass of {@link IoLib} and therefore {@link LibFunction} which implements
* library for the JSE platform. * the lua standard {@code io} library for the JSE platform.
* <p>
* The implementation of the is based on CLDC 1.0 and StreamConnection.
* However, seek is not supported.
* <p> * <p>
* Typically, this library is included as part of a call to * The implementation of the is based on CLDC 1.0 and StreamConnection. However,
* seek is not supported.
* <p>
* Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JmePlatform.standardGlobals(); * <pre>
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * {
* } </pre> * &#64;code
* Globals globals = JmePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired, a minimal
* a minimal set of libraries could be loaded * set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* directly via {@link Globals#load(LuaValue)} using code such as: * using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JmeBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new JmeIoLib()); * Globals globals = new Globals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * globals.load(new JmeBaseLib());
* } </pre> * globals.load(new PackageLib());
* <p>However, other libraries such as <em>MathLib</em> are not loaded in this case. * globals.load(new JmeIoLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * However, other libraries such as <em>MathLib</em> are not loaded in this
* case.
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see IoLib * @see IoLib
* @see org.luaj.vm2.lib.jse.JseIoLib * @see org.luaj.vm2.lib.jse.JseIoLib
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.8">Lua 5.2 I/O Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.8">Lua 5.2 I/O Lib
* Reference</a>
*/ */
public class JmeIoLib extends IoLib { public class JmeIoLib extends IoLib {
protected File wrapStdin() throws IOException { protected File wrapStdin() throws IOException {
return new FileImpl(globals.STDIN); return new FileImpl(globals.STDIN);
} }
protected File wrapStdout() throws IOException { protected File wrapStdout() throws IOException {
return new FileImpl(globals.STDOUT); return new FileImpl(globals.STDOUT);
} }
protected File wrapStderr() throws IOException { protected File wrapStderr() throws IOException {
return new FileImpl(globals.STDERR); return new FileImpl(globals.STDERR);
} }
protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException { protected File openFile(String filename, boolean readMode, boolean appendMode, boolean updateMode,
boolean binaryMode) throws IOException {
String url = "file:///" + filename; String url = "file:///" + filename;
int mode = readMode? Connector.READ: Connector.READ_WRITE; int mode = readMode? Connector.READ: Connector.READ_WRITE;
StreamConnection conn = (StreamConnection) Connector.open( url, mode ); StreamConnection conn = (StreamConnection) Connector.open(url, mode);
File f = readMode? File f = readMode? new FileImpl(conn, conn.openInputStream(), null)
new FileImpl(conn, conn.openInputStream(), null): : new FileImpl(conn, conn.openInputStream(), conn.openOutputStream());
new FileImpl(conn, conn.openInputStream(), conn.openOutputStream());
/* /*
if ( appendMode ) { if ( appendMode ) {
f.seek("end",0); f.seek("end",0);
@@ -99,11 +112,11 @@ public class JmeIoLib extends IoLib {
*/ */
return f; return f;
} }
private static void notimplemented() throws IOException { private static void notimplemented() throws IOException {
throw new IOException("not implemented"); throw new IOException("not implemented");
} }
protected File openProgram(String prog, String mode) throws IOException { protected File openProgram(String prog, String mode) throws IOException {
notimplemented(); notimplemented();
return null; return null;
@@ -113,52 +126,62 @@ public class JmeIoLib extends IoLib {
notimplemented(); notimplemented();
return null; return null;
} }
private final class FileImpl extends File { private final class FileImpl extends File {
private final StreamConnection conn; private final StreamConnection conn;
private final InputStream is; private final InputStream is;
private final OutputStream os; private final OutputStream os;
private boolean closed = false; private boolean closed = false;
private boolean nobuffer = false; private boolean nobuffer = false;
private int lookahead = -1; private int lookahead = -1;
private FileImpl( StreamConnection conn, InputStream is, OutputStream os ) {
private FileImpl(StreamConnection conn, InputStream is, OutputStream os) {
this.conn = conn; this.conn = conn;
this.is = is; this.is = is;
this.os = os; this.os = os;
} }
private FileImpl( InputStream i ) {
this( null, i, null ); private FileImpl(InputStream i) {
this(null, i, null);
} }
private FileImpl( OutputStream o ) {
this( null, null, o ); private FileImpl(OutputStream o) {
this(null, null, o);
} }
public String tojstring() { public String tojstring() {
return "file ("+this.hashCode()+")"; return "file (" + this.hashCode() + ")";
} }
public boolean isstdfile() { public boolean isstdfile() {
return conn == null; return conn == null;
} }
public void close() throws IOException {
public void close() throws IOException {
closed = true; closed = true;
if ( conn != null ) { if (conn != null) {
conn.close(); conn.close();
} }
} }
public void flush() throws IOException { public void flush() throws IOException {
if ( os != null ) if (os != null)
os.flush(); os.flush();
} }
public void write(LuaString s) throws IOException { public void write(LuaString s) throws IOException {
if ( os != null ) if (os != null)
os.write( s.m_bytes, s.m_offset, s.m_length ); os.write(s.m_bytes, s.m_offset, s.m_length);
else else
notimplemented(); notimplemented();
if ( nobuffer ) if (nobuffer)
flush(); flush();
} }
public boolean isclosed() { public boolean isclosed() {
return closed; return closed;
} }
public int seek(String option, int pos) throws IOException { public int seek(String option, int pos) throws IOException {
/* /*
if ( conn != null ) { if ( conn != null ) {
@@ -177,6 +200,7 @@ public class JmeIoLib extends IoLib {
notimplemented(); notimplemented();
return 0; return 0;
} }
public void setvbuf(String mode, int size) { public void setvbuf(String mode, int size) {
nobuffer = "no".equals(mode); nobuffer = "no".equals(mode);
} }
@@ -185,22 +209,22 @@ public class JmeIoLib extends IoLib {
public int remaining() throws IOException { public int remaining() throws IOException {
return -1; return -1;
} }
// peek ahead one character // peek ahead one character
public int peek() throws IOException { public int peek() throws IOException {
if ( lookahead < 0 ) if (lookahead < 0)
lookahead = is.read(); lookahead = is.read();
return lookahead; return lookahead;
} }
// return char if read, -1 if eof, throw IOException on other exception // return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException { public int read() throws IOException {
if ( lookahead >= 0 ) { if (lookahead >= 0) {
int c = lookahead; int c = lookahead;
lookahead = -1; lookahead = -1;
return c; return c;
} }
if ( is != null ) if (is != null)
return is.read(); return is.read();
notimplemented(); notimplemented();
return 0; return 0;
@@ -208,17 +232,17 @@ public class JmeIoLib extends IoLib {
// return number of bytes read if positive, -1 if eof, throws IOException // return number of bytes read if positive, -1 if eof, throws IOException
public int read(byte[] bytes, int offset, int length) throws IOException { public int read(byte[] bytes, int offset, int length) throws IOException {
int n,i=0; int n, i = 0;
if (is!=null) { if (is != null) {
if ( length > 0 && lookahead >= 0 ) { if (length > 0 && lookahead >= 0) {
bytes[offset] = (byte) lookahead; bytes[offset] = (byte) lookahead;
lookahead = -1; lookahead = -1;
i += 1; i += 1;
} }
for ( ; i<length; ) { for (; i < length;) {
n = is.read(bytes, offset+i, length-i); n = is.read(bytes, offset+i, length-i);
if ( n < 0 ) if (n < 0)
return ( i > 0 ? i : -1 ); return (i > 0? i: -1);
i += n; i += n;
} }
} else { } else {

View File

@@ -37,40 +37,60 @@ import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.StringLib; import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.TableLib; import org.luaj.vm2.lib.TableLib;
/** The {@link org.luaj.vm2.lib.jme.JmePlatform} class is a convenience class to standardize /**
* how globals tables are initialized for the JME platform. * The {@link org.luaj.vm2.lib.jme.JmePlatform} class is a convenience class to
* standardize how globals tables are initialized for the JME platform.
* <p> * <p>
* The JME platform, being limited, cannot implement all libraries in all aspects. The main limitations are * The JME platform, being limited, cannot implement all libraries in all
* aspects. The main limitations are
* <ul> * <ul>
* <li>Some math functions are not implemented, see {@link MathLib} for details</li> * <li>Some math functions are not implemented, see {@link MathLib} for
* <li>Scripts are loaded via Class.getResourceAsStream(), see {@link BaseLib} for details</li> * details</li>
* <li>OS functions execute(), remove(), rename(), and tmpname() vary, see {@link OsLib} for details</li> * <li>Scripts are loaded via Class.getResourceAsStream(), see {@link BaseLib}
* <li>I/O seek is not implemented, see {@link org.luaj.vm2.lib.jme.JmeIoLib} for details</li> * for details</li>
* <li>luajava is not available, see {@link org.luaj.vm2.lib.jse.LuajavaLib} for details</li> * <li>OS functions execute(), remove(), rename(), and tmpname() vary, see
* {@link OsLib} for details</li>
* <li>I/O seek is not implemented, see {@link org.luaj.vm2.lib.jme.JmeIoLib}
* for details</li>
* <li>luajava is not available, see {@link org.luaj.vm2.lib.jse.LuajavaLib} for
* details</li>
* </ul> * </ul>
* <p> * <p>
* It is used to allocate either a set of standard globals using * It is used to allocate either a set of standard globals using
* {@link #standardGlobals()} or debug globals using {@link #debugGlobals()} * {@link #standardGlobals()} or debug globals using {@link #debugGlobals()}
* <p> * <p>
* A simple example of initializing globals and using them from Java is: * A simple example of initializing globals and using them from Java is:
* <pre> {@code *
* Globals global = JmePlatform.standardGlobals(); * <pre>
* global.get("print").call(LuaValue.valueOf("hello, world")); * {
* } </pre> * &#64;code
* Globals global = JmePlatform.standardGlobals();
* global.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p> * <p>
* Once globals are created, a simple way to load and run a script is: * Once globals are created, a simple way to load and run a script is:
* <pre> {@code *
* <pre>
* {@code
* LoadState.load( getClass().getResourceAsStream("main.lua"), "main.lua", globals ).call(); * LoadState.load( getClass().getResourceAsStream("main.lua"), "main.lua", globals ).call();
* } </pre> * }
* </pre>
* <p> * <p>
* although {@code require} could also be used: * although {@code require} could also be used:
* <pre> {@code *
* <pre>
* {@code
* globals.get("require").call(LuaValue.valueOf("main")); * globals.get("require").call(LuaValue.valueOf("main"));
* } </pre> * }
* For this to succeed, the file "main.lua" must be a resource in the class path. * </pre>
* See {@link BaseLib} for details on finding scripts using {@link ResourceFinder}. *
* For this to succeed, the file "main.lua" must be a resource in the class
* path. See {@link BaseLib} for details on finding scripts using
* {@link ResourceFinder}.
* <p> * <p>
* The standard globals will contain all standard libraries in their JME flavors: * The standard globals will contain all standard libraries in their JME
* flavors:
* <ul> * <ul>
* <li>{@link Globals}</li> * <li>{@link Globals}</li>
* <li>{@link BaseLib}</li> * <li>{@link BaseLib}</li>
@@ -83,12 +103,14 @@ import org.luaj.vm2.lib.TableLib;
* <li>{@link org.luaj.vm2.lib.jme.JmeIoLib}</li> * <li>{@link org.luaj.vm2.lib.jme.JmeIoLib}</li>
* <li>{@link OsLib}</li> * <li>{@link OsLib}</li>
* </ul> * </ul>
* In addition, the {@link LuaC} compiler is installed so lua files may be loaded in their source form. * In addition, the {@link LuaC} compiler is installed so lua files may be
* <p> * loaded in their source form.
* The debug globals are simply the standard globals plus the {@code debug} library {@link DebugLib}. * <p>
* The debug globals are simply the standard globals plus the {@code debug}
* library {@link DebugLib}.
* <p> * <p>
* <p> * <p>
* The class ensures that initialization is done in the correct order. * The class ensures that initialization is done in the correct order.
* *
* @see Globals * @see Globals
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
@@ -116,12 +138,14 @@ public class JmePlatform {
globals.load(new JmeIoLib()); globals.load(new JmeIoLib());
LoadState.install(globals); LoadState.install(globals);
LuaC.install(globals); LuaC.install(globals);
return globals; return globals;
} }
/** Create standard globals including the {@link DebugLib} library. /**
* Create standard globals including the {@link DebugLib} library.
* *
* @return Table of globals initialized with the standard JSE and debug libraries * @return Table of globals initialized with the standard JSE and debug
* libraries
* @see #standardGlobals() * @see #standardGlobals()
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform

View File

@@ -1,3 +1,4 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2009-2012 Luaj.org. All rights reserved. * Copyright (c) 2009-2012 Luaj.org. All rights reserved.
* *
@@ -38,37 +39,30 @@ import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.jse.JsePlatform; import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC; import org.luaj.vm2.luajc.LuaJC;
/** /**
* lua command for use in JSE environments. * lua command for use in JSE environments.
*/ */
public class lua { public class lua {
private static final String version = Lua._VERSION + " Copyright (c) 2012 Luaj.org.org"; private static final String version = Lua._VERSION + " Copyright (c) 2012 Luaj.org.org";
private static final String usage = private static final String usage = "usage: java -cp luaj-jse.jar lua [options] [script [args]].\n"
"usage: java -cp luaj-jse.jar lua [options] [script [args]].\n" + + "Available options are:\n" + " -e stat execute string 'stat'\n" + " -l name require library 'name'\n"
"Available options are:\n" + + " -i enter interactive mode after executing 'script'\n" + " -v show version information\n"
" -e stat execute string 'stat'\n" + + " -b use luajc bytecode-to-bytecode compiler (requires bcel on class path)\n"
" -l name require library 'name'\n" + + " -n nodebug - do not load debug library by default\n" + " -p print the prototype\n"
" -i enter interactive mode after executing 'script'\n" + + " -c enc use the supplied encoding 'enc' for input files\n" + " -- stop handling options\n"
" -v show version information\n" + + " - execute stdin and stop handling options";
" -b use luajc bytecode-to-bytecode compiler (requires bcel on class path)\n" +
" -n nodebug - do not load debug library by default\n" +
" -p print the prototype\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -- stop handling options\n" +
" - execute stdin and stop handling options";
private static void usageExit() { private static void usageExit() {
System.out.println(usage); System.out.println(usage);
System.exit(-1); System.exit(-1);
} }
private static Globals globals; private static Globals globals;
private static boolean print = false; private static boolean print = false;
private static String encoding = null; private static String encoding = null;
public static void main( String[] args ) throws IOException { public static void main(String[] args) throws IOException {
// process args // process args
boolean interactive = (args.length == 0); boolean interactive = (args.length == 0);
@@ -79,17 +73,17 @@ public class lua {
Vector libs = null; Vector libs = null;
try { try {
// stateful argument processing // stateful argument processing
for ( int i=0; i<args.length; i++ ) { for (int i = 0; i < args.length; i++) {
if ( ! processing || ! args[i].startsWith("-") ) { if (!processing || !args[i].startsWith("-")) {
// input file - defer to last stage // input file - defer to last stage
break; break;
} else if ( args[i].length() <= 1 ) { } else if (args[i].length() <= 1) {
// input file - defer to last stage // input file - defer to last stage
break; break;
} else { } else {
switch ( args[i].charAt(1) ) { switch (args[i].charAt(1)) {
case 'e': case 'e':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
// input script - defer to last stage // input script - defer to last stage
break; break;
@@ -97,10 +91,10 @@ public class lua {
luajc = true; luajc = true;
break; break;
case 'l': case 'l':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
libs = libs!=null? libs: new Vector(); libs = libs != null? libs: new Vector();
libs.addElement( args[i] ); libs.addElement(args[i]);
break; break;
case 'i': case 'i':
interactive = true; interactive = true;
@@ -115,12 +109,12 @@ public class lua {
print = true; print = true;
break; break;
case 'c': case 'c':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
encoding = args[i]; encoding = args[i];
break; break;
case '-': case '-':
if ( args[i].length() > 2 ) if (args[i].length() > 2)
usageExit(); usageExit();
processing = false; processing = false;
break; break;
@@ -132,33 +126,34 @@ public class lua {
} }
// echo version // echo version
if ( versioninfo ) if (versioninfo)
System.out.println(version); System.out.println(version);
// new lua state // new lua state
globals = nodebug? JsePlatform.standardGlobals(): JsePlatform.debugGlobals(); globals = nodebug? JsePlatform.standardGlobals(): JsePlatform.debugGlobals();
if ( luajc ) LuaJC.install(globals); if (luajc)
for ( int i=0, n=libs!=null? libs.size(): 0; i<n; i++ ) LuaJC.install(globals);
loadLibrary( (String) libs.elementAt(i) ); for (int i = 0, n = libs != null? libs.size(): 0; i < n; i++)
loadLibrary((String) libs.elementAt(i));
// input script processing // input script processing
processing = true; processing = true;
for ( int i=0; i<args.length; i++ ) { for (int i = 0; i < args.length; i++) {
if ( ! processing || ! args[i].startsWith("-") ) { if (!processing || !args[i].startsWith("-")) {
processScript( new FileInputStream(args[i]), args[i], args, i ); processScript(new FileInputStream(args[i]), args[i], args, i);
break; break;
} else if ( "-".equals( args[i] ) ) { } else if ("-".equals(args[i])) {
processScript( System.in, "=stdin", args, i ); processScript(System.in, "=stdin", args, i);
break; break;
} else { } else {
switch ( args[i].charAt(1) ) { switch (args[i].charAt(1)) {
case 'l': case 'l':
case 'c': case 'c':
++i; ++i;
break; break;
case 'e': case 'e':
++i; ++i;
processScript( new ByteArrayInputStream(args[i].getBytes()), "string", args, i ); processScript(new ByteArrayInputStream(args[i].getBytes()), "string", args, i);
break; break;
case '-': case '-':
processing = false; processing = false;
@@ -166,49 +161,49 @@ public class lua {
} }
} }
} }
if ( interactive ) if (interactive)
interactiveMode(); interactiveMode();
} catch ( IOException ioe ) { } catch (IOException ioe) {
System.err.println( ioe.toString() ); System.err.println(ioe.toString());
System.exit(-2); System.exit(-2);
} }
} }
private static void loadLibrary( String libname ) throws IOException { private static void loadLibrary(String libname) throws IOException {
LuaValue slibname =LuaValue.valueOf(libname); LuaValue slibname = LuaValue.valueOf(libname);
try { try {
// load via plain require // load via plain require
globals.get("require").call(slibname); globals.get("require").call(slibname);
} catch ( Exception e ) { } catch (Exception e) {
try { try {
// load as java class // load as java class
LuaValue v = (LuaValue) Class.forName(libname).newInstance(); LuaValue v = (LuaValue) Class.forName(libname).newInstance();
v.call(slibname, globals); v.call(slibname, globals);
} catch ( Exception f ) { } catch (Exception f) {
throw new IOException("loadLibrary("+libname+") failed: "+e+","+f ); throw new IOException("loadLibrary(" + libname + ") failed: " + e + "," + f);
} }
} }
} }
private static void processScript( InputStream script, String chunkname, String[] args, int firstarg ) throws IOException { private static void processScript(InputStream script, String chunkname, String[] args, int firstarg)
throws IOException {
try { try {
LuaValue c; LuaValue c;
try { try {
script = new BufferedInputStream(script); script = new BufferedInputStream(script);
c = encoding != null? c = encoding != null? globals.load(new InputStreamReader(script, encoding), chunkname)
globals.load(new InputStreamReader(script, encoding), chunkname): : globals.load(script, chunkname, "bt", globals);
globals.load(script, chunkname, "bt", globals);
} finally { } finally {
script.close(); script.close();
} }
if (print && c.isclosure()) if (print && c.isclosure())
Print.print(c.checkclosure().p); Print.print(c.checkclosure().p);
Varargs scriptargs = setGlobalArg(chunkname, args, firstarg, globals); Varargs scriptargs = setGlobalArg(chunkname, args, firstarg, globals);
c.invoke( scriptargs ); c.invoke(scriptargs);
} catch ( Exception e ) { } catch (Exception e) {
e.printStackTrace( System.err ); e.printStackTrace(System.err);
} }
} }
@@ -216,23 +211,23 @@ public class lua {
if (args == null) if (args == null)
return LuaValue.NONE; return LuaValue.NONE;
LuaTable arg = LuaValue.tableOf(); LuaTable arg = LuaValue.tableOf();
for ( int j=0; j<args.length; j++ ) for (int j = 0; j < args.length; j++)
arg.set( j-i, LuaValue.valueOf(args[j]) ); arg.set(j-i, LuaValue.valueOf(args[j]));
arg.set(0, LuaValue.valueOf(chunkname)); arg.set(0, LuaValue.valueOf(chunkname));
arg.set(-1, LuaValue.valueOf("luaj")); arg.set(-1, LuaValue.valueOf("luaj"));
globals.set("arg", arg); globals.set("arg", arg);
return arg.unpack(); return arg.unpack();
} }
private static void interactiveMode( ) throws IOException { private static void interactiveMode() throws IOException {
BufferedReader reader = new BufferedReader( new InputStreamReader( System.in ) ); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while ( true ) { while ( true ) {
System.out.print("> "); System.out.print("> ");
System.out.flush(); System.out.flush();
String line = reader.readLine(); String line = reader.readLine();
if ( line == null ) if (line == null)
return; return;
processScript( new ByteArrayInputStream(line.getBytes()), "=stdin", null, 0 ); processScript(new ByteArrayInputStream(line.getBytes()), "=stdin", null, 0);
} }
} }
} }

View File

@@ -1,3 +1,4 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved. * Copyright (c) 2009 Luaj.org. All rights reserved.
* *
@@ -35,63 +36,56 @@ import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.DumpState; import org.luaj.vm2.compiler.DumpState;
import org.luaj.vm2.lib.jse.JsePlatform; import org.luaj.vm2.lib.jse.JsePlatform;
/** /**
* Compiler for lua files to lua bytecode. * Compiler for lua files to lua bytecode.
*/ */
public class luac { public class luac {
private static final String version = Lua._VERSION + "Copyright (C) 2009 luaj.org"; private static final String version = Lua._VERSION + "Copyright (C) 2009 luaj.org";
private static final String usage = private static final String usage = "usage: java -cp luaj-jse.jar luac [options] [filenames].\n"
"usage: java -cp luaj-jse.jar luac [options] [filenames].\n" + + "Available options are:\n" + " - process stdin\n" + " -l list\n"
"Available options are:\n" + + " -o name output to file 'name' (default is \"luac.out\")\n" + " -p parse only\n"
" - process stdin\n" + + " -s strip debug information\n" + " -e little endian format for numbers\n"
" -l list\n" + + " -i<n> number format 'n', (n=0,1 or 4, default=" + DumpState.NUMBER_FORMAT_DEFAULT + ")\n"
" -o name output to file 'name' (default is \"luac.out\")\n" + + " -v show version information\n" + " -c enc use the supplied encoding 'enc' for input files\n"
" -p parse only\n" + + " -- stop handling options\n";
" -s strip debug information\n" +
" -e little endian format for numbers\n" +
" -i<n> number format 'n', (n=0,1 or 4, default="+DumpState.NUMBER_FORMAT_DEFAULT+")\n" +
" -v show version information\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -- stop handling options\n";
private static void usageExit() { private static void usageExit() {
System.out.println(usage); System.out.println(usage);
System.exit(-1); System.exit(-1);
} }
private boolean list = false; private boolean list = false;
private String output = "luac.out"; private String output = "luac.out";
private boolean parseonly = false; private boolean parseonly = false;
private boolean stripdebug = false; private boolean stripdebug = false;
private boolean littleendian = false; private boolean littleendian = false;
private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT; private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT;
private boolean versioninfo = false; private boolean versioninfo = false;
private boolean processing = true; private boolean processing = true;
private String encoding = null; private String encoding = null;
public static void main( String[] args ) throws IOException { public static void main(String[] args) throws IOException {
new luac( args ); new luac(args);
} }
private luac( String[] args ) throws IOException { private luac(String[] args) throws IOException {
// process args // process args
try { try {
// get stateful args // get stateful args
for ( int i=0; i<args.length; i++ ) { for (int i = 0; i < args.length; i++) {
if ( ! processing || ! args[i].startsWith("-") ) { if (!processing || !args[i].startsWith("-")) {
// input file - defer to next stage // input file - defer to next stage
} else if ( args[i].length() <= 1 ) { } else if (args[i].length() <= 1) {
// input file - defer to next stage // input file - defer to next stage
} else { } else {
switch ( args[i].charAt(1) ) { switch (args[i].charAt(1)) {
case 'l': case 'l':
list = true; list = true;
break; break;
case 'o': case 'o':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
output = args[i]; output = args[i];
break; break;
@@ -105,7 +99,7 @@ public class luac {
littleendian = true; littleendian = true;
break; break;
case 'i': case 'i':
if ( args[i].length() <= 2 ) if (args[i].length() <= 2)
usageExit(); usageExit();
numberformat = Integer.parseInt(args[i].substring(2)); numberformat = Integer.parseInt(args[i].substring(2));
break; break;
@@ -113,12 +107,12 @@ public class luac {
versioninfo = true; versioninfo = true;
break; break;
case 'c': case 'c':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
encoding = args[i]; encoding = args[i];
break; break;
case '-': case '-':
if ( args[i].length() > 2 ) if (args[i].length() > 2)
usageExit(); usageExit();
processing = false; processing = false;
break; break;
@@ -128,26 +122,26 @@ public class luac {
} }
} }
} }
// echo version // echo version
if ( versioninfo ) if (versioninfo)
System.out.println(version); System.out.println(version);
// open output file // open output file
OutputStream fos = new FileOutputStream( output ); OutputStream fos = new FileOutputStream(output);
// process input files // process input files
try { try {
Globals globals = JsePlatform.standardGlobals(); Globals globals = JsePlatform.standardGlobals();
processing = true; processing = true;
for ( int i=0; i<args.length; i++ ) { for (int i = 0; i < args.length; i++) {
if ( ! processing || ! args[i].startsWith("-") ) { if (!processing || !args[i].startsWith("-")) {
String chunkname = args[i].substring(0,args[i].length()-4); String chunkname = args[i].substring(0, args[i].length()-4);
processScript( globals, new FileInputStream(args[i]), chunkname, fos ); processScript(globals, new FileInputStream(args[i]), chunkname, fos);
} else if ( args[i].length() <= 1 ) { } else if (args[i].length() <= 1) {
processScript( globals, System.in, "=stdin", fos ); processScript(globals, System.in, "=stdin", fos);
} else { } else {
switch ( args[i].charAt(1) ) { switch (args[i].charAt(1)) {
case 'o': case 'o':
case 'c': case 'c':
++i; ++i;
@@ -161,32 +155,33 @@ public class luac {
} finally { } finally {
fos.close(); fos.close();
} }
} catch ( IOException ioe ) { } catch (IOException ioe) {
System.err.println( ioe.toString() ); System.err.println(ioe.toString());
System.exit(-2); System.exit(-2);
} }
} }
private void processScript( Globals globals, InputStream script, String chunkname, OutputStream out ) throws IOException { private void processScript(Globals globals, InputStream script, String chunkname, OutputStream out)
throws IOException {
try { try {
// create the chunk // create the chunk
script = new BufferedInputStream(script); script = new BufferedInputStream(script);
Prototype chunk = encoding != null? Prototype chunk = encoding != null
globals.compilePrototype(new InputStreamReader(script, encoding), chunkname): ? globals.compilePrototype(new InputStreamReader(script, encoding), chunkname)
globals.compilePrototype(script, chunkname); : globals.compilePrototype(script, chunkname);
// list the chunk // list the chunk
if (list) if (list)
Print.printCode(chunk); Print.printCode(chunk);
// write out the chunk // write out the chunk
if (!parseonly) { if (!parseonly) {
DumpState.dump(chunk, out, stripdebug, numberformat, littleendian); DumpState.dump(chunk, out, stripdebug, numberformat, littleendian);
} }
} catch ( Exception e ) { } catch (Exception e) {
e.printStackTrace( System.err ); e.printStackTrace(System.err);
} finally { } finally {
script.close(); script.close();
} }

View File

@@ -1,3 +1,4 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2009-2012 Luaj.org. All rights reserved. * Copyright (c) 2009-2012 Luaj.org. All rights reserved.
* *
@@ -36,62 +37,57 @@ import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC; import org.luaj.vm2.luajc.LuaJC;
/** /**
* Compiler for lua files to compile lua sources or lua binaries into java classes. * Compiler for lua files to compile lua sources or lua binaries into java
* classes.
*/ */
public class luajc { public class luajc {
private static final String version = Lua._VERSION + " Copyright (C) 2012 luaj.org"; private static final String version = Lua._VERSION + " Copyright (C) 2012 luaj.org";
private static final String usage = private static final String usage = "usage: java -cp luaj-jse.jar,bcel-5.2.jar luajc [options] fileordir [, fileordir ...]\n"
"usage: java -cp luaj-jse.jar,bcel-5.2.jar luajc [options] fileordir [, fileordir ...]\n" + + "Available options are:\n" + " - process stdin\n" + " -s src source directory\n"
"Available options are:\n" + + " -d dir destination directory\n" + " -p pkg package prefix to apply to all classes\n"
" - process stdin\n" + + " -m generate main(String[]) function for JSE\n" + " -r recursively compile all\n"
" -s src source directory\n" + + " -l load classes to verify generated bytecode\n"
" -d dir destination directory\n" + + " -c enc use the supplied encoding 'enc' for input files\n" + " -v verbose\n";
" -p pkg package prefix to apply to all classes\n" +
" -m generate main(String[]) function for JSE\n" +
" -r recursively compile all\n" +
" -l load classes to verify generated bytecode\n" +
" -c enc use the supplied encoding 'enc' for input files\n" +
" -v verbose\n";
private static void usageExit() { private static void usageExit() {
System.out.println(usage); System.out.println(usage);
System.exit(-1); System.exit(-1);
} }
private String srcdir = "."; private String srcdir = ".";
private String destdir = "."; private String destdir = ".";
private boolean genmain = false; private boolean genmain = false;
private boolean recurse = false; private boolean recurse = false;
private boolean verbose = false; private boolean verbose = false;
private boolean loadclasses = false; private boolean loadclasses = false;
private String encoding = null; private String encoding = null;
private String pkgprefix = null; private String pkgprefix = null;
private List files = new ArrayList(); private List files = new ArrayList();
private Globals globals; private Globals globals;
public static void main( String[] args ) throws IOException { public static void main(String[] args) throws IOException {
new luajc( args ); new luajc(args);
} }
private luajc( String[] args ) throws IOException { private luajc(String[] args) throws IOException {
// process args // process args
List seeds = new ArrayList (); List seeds = new ArrayList();
// get stateful args // get stateful args
for ( int i=0; i<args.length; i++ ) { for (int i = 0; i < args.length; i++) {
if ( ! args[i].startsWith("-") ) { if (!args[i].startsWith("-")) {
seeds.add(args[i]); seeds.add(args[i]);
} else { } else {
switch ( args[i].charAt(1) ) { switch (args[i].charAt(1)) {
case 's': case 's':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
srcdir = args[i]; srcdir = args[i];
break; break;
case 'd': case 'd':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
destdir = args[i]; destdir = args[i];
break; break;
@@ -99,7 +95,7 @@ public class luajc {
loadclasses = true; loadclasses = true;
break; break;
case 'p': case 'p':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
pkgprefix = args[i]; pkgprefix = args[i];
break; break;
@@ -110,7 +106,7 @@ public class luajc {
recurse = true; recurse = true;
break; break;
case 'c': case 'c':
if ( ++i >= args.length ) if (++i >= args.length)
usageExit(); usageExit();
encoding = args[i]; encoding = args[i];
break; break;
@@ -123,60 +119,61 @@ public class luajc {
} }
} }
} }
// echo version // echo version
if ( verbose ) { if (verbose) {
System.out.println(version); System.out.println(version);
System.out.println("srcdir: "+srcdir); System.out.println("srcdir: " + srcdir);
System.out.println("destdir: "+destdir); System.out.println("destdir: " + destdir);
System.out.println("files: "+seeds); System.out.println("files: " + seeds);
System.out.println("recurse: "+recurse); System.out.println("recurse: " + recurse);
} }
// need at least one seed // need at least one seed
if ( seeds.size() <= 0 ) { if (seeds.size() <= 0) {
System.err.println(usage); System.err.println(usage);
System.exit(-1); System.exit(-1);
} }
// collect up files to process // collect up files to process
for ( int i=0; i<seeds.size(); i++ ) for (int i = 0; i < seeds.size(); i++)
collectFiles( srcdir+"/"+seeds.get(i) ); collectFiles(srcdir + "/" + seeds.get(i));
// check for at least one file // check for at least one file
if ( files.size() <= 0 ) { if (files.size() <= 0) {
System.err.println("no files found in "+seeds); System.err.println("no files found in " + seeds);
System.exit(-1); System.exit(-1);
} }
// process input files // process input files
globals = JsePlatform.standardGlobals(); globals = JsePlatform.standardGlobals();
for ( int i=0,n=files.size(); i<n; i++ ) for (int i = 0, n = files.size(); i < n; i++)
processFile( (InputFile) files.get(i) ); processFile((InputFile) files.get(i));
} }
private void collectFiles(String path) { private void collectFiles(String path) {
File f = new File(path); File f = new File(path);
if ( f.isDirectory() && recurse ) if (f.isDirectory() && recurse)
scandir(f,pkgprefix); scandir(f, pkgprefix);
else if ( f.isFile() ) { else if (f.isFile()) {
File dir = f.getAbsoluteFile().getParentFile(); File dir = f.getAbsoluteFile().getParentFile();
if ( dir != null ) if (dir != null)
scanfile( dir, f, pkgprefix ); scanfile(dir, f, pkgprefix);
} }
} }
private void scandir(File dir, String javapackage) { private void scandir(File dir, String javapackage) {
File[] f = dir.listFiles(); File[] f = dir.listFiles();
for ( int i=0; i<f.length; i++ ) for (int i = 0; i < f.length; i++)
scanfile( dir, f[i], javapackage ); scanfile(dir, f[i], javapackage);
} }
private void scanfile(File dir, File f, String javapackage) { private void scanfile(File dir, File f, String javapackage) {
if ( f.exists() ) { if (f.exists()) {
if ( f.isDirectory() && recurse ) if (f.isDirectory() && recurse)
scandir( f, (javapackage!=null? javapackage+"."+f.getName(): f.getName()) ); scandir(f, (javapackage != null? javapackage + "." + f.getName(): f.getName()));
else if ( f.isFile() && f.getName().endsWith(".lua") ) else if (f.isFile() && f.getName().endsWith(".lua"))
files.add( new InputFile(dir,f,javapackage) ); files.add(new InputFile(dir, f, javapackage));
} }
} }
@@ -188,82 +185,84 @@ public class luajc {
} }
public Class findClass(String classname) throws ClassNotFoundException { public Class findClass(String classname) throws ClassNotFoundException {
byte[] bytes = (byte[]) t.get(classname); byte[] bytes = (byte[]) t.get(classname);
if ( bytes != null ) if (bytes != null)
return defineClass(classname, bytes, 0, bytes.length); return defineClass(classname, bytes, 0, bytes.length);
return super.findClass(classname); return super.findClass(classname);
} }
} }
class InputFile { class InputFile {
public String luachunkname; public String luachunkname;
public String srcfilename; public String srcfilename;
public File infile; public File infile;
public File outdir; public File outdir;
public String javapackage; public String javapackage;
public InputFile(File dir, File f, String javapackage) { public InputFile(File dir, File f, String javapackage) {
this.infile = f; this.infile = f;
String subdir = javapackage!=null? javapackage.replace('.', '/'): null; String subdir = javapackage != null? javapackage.replace('.', '/'): null;
String outdirpath = subdir!=null? destdir+"/"+subdir: destdir; String outdirpath = subdir != null? destdir + "/" + subdir: destdir;
this.javapackage = javapackage; this.javapackage = javapackage;
this.srcfilename = (subdir!=null? subdir+"/": "")+infile.getName(); this.srcfilename = (subdir != null? subdir + "/": "")+infile.getName();
this.luachunkname = (subdir!=null? subdir+"/": "")+infile.getName().substring( 0, infile.getName().lastIndexOf('.') ); this.luachunkname = (subdir != null? subdir + "/": "")
+infile.getName().substring(0, infile.getName().lastIndexOf('.'));
this.infile = f; this.infile = f;
this.outdir = new File(outdirpath); this.outdir = new File(outdirpath);
} }
} }
private void processFile( InputFile inf ) { private void processFile(InputFile inf) {
inf.outdir.mkdirs(); inf.outdir.mkdirs();
try { try {
if ( verbose ) if (verbose)
System.out.println("chunk="+inf.luachunkname+" srcfile="+inf.srcfilename); System.out.println("chunk=" + inf.luachunkname + " srcfile=" + inf.srcfilename);
// create the chunk // create the chunk
FileInputStream fis = new FileInputStream( inf.infile ); FileInputStream fis = new FileInputStream(inf.infile);
final Hashtable t = encoding != null? final Hashtable t = encoding != null
LuaJC.instance.compileAll( new InputStreamReader(fis, encoding), inf.luachunkname, inf.srcfilename, globals, genmain): ? LuaJC.instance.compileAll(new InputStreamReader(fis, encoding), inf.luachunkname, inf.srcfilename,
LuaJC.instance.compileAll( fis, inf.luachunkname, inf.srcfilename, globals, genmain); globals, genmain)
: LuaJC.instance.compileAll(fis, inf.luachunkname, inf.srcfilename, globals, genmain);
fis.close(); fis.close();
// write out the chunk
for ( Enumeration e = t.keys(); e.hasMoreElements(); ) {
String key = (String) e.nextElement();
byte[] bytes = (byte[]) t.get(key);
if ( key.indexOf('/')>=0 ) {
String d = (destdir!=null? destdir+"/": "")+key.substring(0,key.lastIndexOf('/'));
new File(d).mkdirs();
}
String destpath = (destdir!=null? destdir+"/": "") + key + ".class";
if ( verbose )
System.out.println( " "+destpath +" ("+bytes.length+" bytes)");
FileOutputStream fos = new FileOutputStream( destpath );
fos.write( bytes );
fos.close();
}
// try to load the files // write out the chunk
if ( loadclasses ) { for (Enumeration e = t.keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
byte[] bytes = (byte[]) t.get(key);
if (key.indexOf('/') >= 0) {
String d = (destdir != null? destdir + "/": "")+key.substring(0, key.lastIndexOf('/'));
new File(d).mkdirs();
}
String destpath = (destdir != null? destdir + "/": "") + key + ".class";
if (verbose)
System.out.println(" " + destpath + " (" + bytes.length + " bytes)");
FileOutputStream fos = new FileOutputStream(destpath);
fos.write(bytes);
fos.close();
}
// try to load the files
if (loadclasses) {
ClassLoader loader = new LocalClassLoader(t); ClassLoader loader = new LocalClassLoader(t);
for ( Enumeration e = t.keys(); e.hasMoreElements(); ) { for (Enumeration e = t.keys(); e.hasMoreElements();) {
String classname = (String) e.nextElement(); String classname = (String) e.nextElement();
try { try {
Class c = loader.loadClass(classname); Class c = loader.loadClass(classname);
Object o = c.newInstance(); Object o = c.newInstance();
if ( verbose ) if (verbose)
System.out.println(" loaded "+classname+" as "+o ); System.out.println(" loaded " + classname + " as " + o);
} catch ( Exception ex ) { } catch (Exception ex) {
System.out.flush(); System.out.flush();
System.err.println(" failed to load "+classname+": "+ex ); System.err.println(" failed to load " + classname + ": " + ex);
System.err.flush(); System.err.flush();
} }
} }
} }
} catch ( Exception e ) { } catch (Exception e) {
System.err.println(" failed to load "+inf.srcfilename+": "+e ); System.err.println(" failed to load " + inf.srcfilename + ": " + e);
e.printStackTrace( System.err ); e.printStackTrace(System.err);
System.err.flush(); System.err.flush();
} }
} }

View File

@@ -25,12 +25,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class Block extends Stat { public class Block extends Stat {
public List<Stat> stats = new ArrayList<Stat>(); public List<Stat> stats = new ArrayList<Stat>();
public NameScope scope; public NameScope scope;
public void add(Stat s) { public void add(Stat s) {
if ( s == null ) if (s == null)
return; return;
stats.add(s); stats.add(s);
} }

View File

@@ -23,12 +23,12 @@ package org.luaj.vm2.ast;
public class Chunk extends SyntaxElement { public class Chunk extends SyntaxElement {
public final Block block; public final Block block;
public Chunk(Block b) { public Chunk(Block b) {
this.block = b; this.block = b;
} }
public void accept( Visitor visitor ) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }

View File

@@ -24,16 +24,15 @@ package org.luaj.vm2.ast;
import org.luaj.vm2.Lua; import org.luaj.vm2.Lua;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
abstract abstract public class Exp extends SyntaxElement {
public class Exp extends SyntaxElement {
abstract public void accept(Visitor visitor); abstract public void accept(Visitor visitor);
public static Exp constant(LuaValue value) { public static Exp constant(LuaValue value) {
return new Constant(value); return new Constant(value);
} }
public static Exp numberconstant(String token) { public static Exp numberconstant(String token) {
return new Constant( LuaValue.valueOf(token).tonumber() ); return new Constant(LuaValue.valueOf(token).tonumber());
} }
public static Exp varargs() { public static Exp varargs() {
@@ -45,59 +44,78 @@ public class Exp extends SyntaxElement {
} }
public static Exp unaryexp(int op, Exp rhs) { public static Exp unaryexp(int op, Exp rhs) {
if ( rhs instanceof BinopExp ) { if (rhs instanceof BinopExp) {
BinopExp b = (BinopExp) rhs; BinopExp b = (BinopExp) rhs;
if ( precedence(op) > precedence(b.op) ) if (precedence(op) > precedence(b.op))
return binaryexp( unaryexp(op, b.lhs), b.op, b.rhs ); return binaryexp(unaryexp(op, b.lhs), b.op, b.rhs);
} }
return new UnopExp(op, rhs); return new UnopExp(op, rhs);
} }
public static Exp binaryexp(Exp lhs, int op, Exp rhs) { public static Exp binaryexp(Exp lhs, int op, Exp rhs) {
if ( lhs instanceof UnopExp ) { if (lhs instanceof UnopExp) {
UnopExp u = (UnopExp) lhs; UnopExp u = (UnopExp) lhs;
if ( precedence(op) > precedence(u.op) ) if (precedence(op) > precedence(u.op))
return unaryexp( u.op, binaryexp( u.rhs, op, rhs ) ); return unaryexp(u.op, binaryexp(u.rhs, op, rhs));
} }
// TODO: cumulate string concatenations together // TODO: cumulate string concatenations together
// TODO: constant folding // TODO: constant folding
if ( lhs instanceof BinopExp ) { if (lhs instanceof BinopExp) {
BinopExp b = (BinopExp) lhs; BinopExp b = (BinopExp) lhs;
if ( (precedence(op) > precedence(b.op)) || if ((precedence(op) > precedence(b.op)) || ((precedence(op) == precedence(b.op)) && isrightassoc(op)))
((precedence(op) == precedence(b.op)) && isrightassoc(op)) ) return binaryexp(b.lhs, b.op, binaryexp(b.rhs, op, rhs));
return binaryexp( b.lhs, b.op, binaryexp( b.rhs, op, rhs ) );
} }
if ( rhs instanceof BinopExp ) { if (rhs instanceof BinopExp) {
BinopExp b = (BinopExp) rhs; BinopExp b = (BinopExp) rhs;
if ( (precedence(op) > precedence(b.op)) || if ((precedence(op) > precedence(b.op)) || ((precedence(op) == precedence(b.op)) && !isrightassoc(op)))
((precedence(op) == precedence(b.op)) && ! isrightassoc(op)) ) return binaryexp(binaryexp(lhs, op, b.lhs), b.op, b.rhs);
return binaryexp( binaryexp( lhs, op, b.lhs ), b.op, b.rhs );
} }
return new BinopExp(lhs, op, rhs); return new BinopExp(lhs, op, rhs);
} }
static boolean isrightassoc(int op) { static boolean isrightassoc(int op) {
switch ( op ) { switch (op) {
case Lua.OP_CONCAT: case Lua.OP_CONCAT:
case Lua.OP_POW: return true; case Lua.OP_POW:
default: return false; return true;
default:
return false;
} }
} }
static int precedence(int op) { static int precedence(int op) {
switch ( op ) { switch (op) {
case Lua.OP_OR: return 0; case Lua.OP_OR:
case Lua.OP_AND: return 1; return 0;
case Lua.OP_LT: case Lua.OP_GT: case Lua.OP_LE: case Lua.OP_GE: case Lua.OP_NEQ: case Lua.OP_EQ: return 2; case Lua.OP_AND:
case Lua.OP_CONCAT: return 3; return 1;
case Lua.OP_ADD: case Lua.OP_SUB: return 4; case Lua.OP_LT:
case Lua.OP_MUL: case Lua.OP_DIV: case Lua.OP_MOD: return 5; case Lua.OP_GT:
case Lua.OP_NOT: case Lua.OP_UNM: case Lua.OP_LEN: return 6; case Lua.OP_LE:
case Lua.OP_POW: return 7; case Lua.OP_GE:
default: throw new IllegalStateException("precedence of bad op "+op); case Lua.OP_NEQ:
case Lua.OP_EQ:
return 2;
case Lua.OP_CONCAT:
return 3;
case Lua.OP_ADD:
case Lua.OP_SUB:
return 4;
case Lua.OP_MUL:
case Lua.OP_DIV:
case Lua.OP_MOD:
return 5;
case Lua.OP_NOT:
case Lua.OP_UNM:
case Lua.OP_LEN:
return 6;
case Lua.OP_POW:
return 7;
default:
throw new IllegalStateException("precedence of bad op " + op);
} }
} }
public static Exp anonymousfunction(FuncBody funcbody) { public static Exp anonymousfunction(FuncBody funcbody) {
return new AnonFuncDef(funcbody); return new AnonFuncDef(funcbody);
} }
@@ -143,11 +161,12 @@ public class Exp extends SyntaxElement {
public boolean isvarargexp() { public boolean isvarargexp() {
return false; return false;
} }
abstract public static class PrimaryExp extends Exp { abstract public static class PrimaryExp extends Exp {
public boolean isvarexp() { public boolean isvarexp() {
return false; return false;
} }
public boolean isfunccall() { public boolean isfunccall() {
return false; return false;
} }
@@ -157,64 +176,71 @@ public class Exp extends SyntaxElement {
public boolean isvarexp() { public boolean isvarexp() {
return true; return true;
} }
public void markHasAssignment() { public void markHasAssignment() {
} }
} }
public static class NameExp extends VarExp { public static class NameExp extends VarExp {
public final Name name; public final Name name;
public NameExp(String name) { public NameExp(String name) {
this.name = new Name(name); this.name = new Name(name);
} }
public void markHasAssignment() { public void markHasAssignment() {
name.variable.hasassignments = true; name.variable.hasassignments = true;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class ParensExp extends PrimaryExp { public static class ParensExp extends PrimaryExp {
public final Exp exp; public final Exp exp;
public ParensExp(Exp exp) { public ParensExp(Exp exp) {
this.exp = exp; this.exp = exp;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class FieldExp extends VarExp { public static class FieldExp extends VarExp {
public final PrimaryExp lhs; public final PrimaryExp lhs;
public final Name name; public final Name name;
public FieldExp(PrimaryExp lhs, String name) { public FieldExp(PrimaryExp lhs, String name) {
this.lhs = lhs; this.lhs = lhs;
this.name = new Name(name); this.name = new Name(name);
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class IndexExp extends VarExp { public static class IndexExp extends VarExp {
public final PrimaryExp lhs; public final PrimaryExp lhs;
public final Exp exp; public final Exp exp;
public IndexExp(PrimaryExp lhs, Exp exp) { public IndexExp(PrimaryExp lhs, Exp exp) {
this.lhs = lhs; this.lhs = lhs;
this.exp = exp; this.exp = exp;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class FuncCall extends PrimaryExp { public static class FuncCall extends PrimaryExp {
public final PrimaryExp lhs; public final PrimaryExp lhs;
public final FuncArgs args; public final FuncArgs args;
public FuncCall(PrimaryExp lhs, FuncArgs args) { public FuncCall(PrimaryExp lhs, FuncArgs args) {
this.lhs = lhs; this.lhs = lhs;
this.args = args; this.args = args;
@@ -223,19 +249,19 @@ public class Exp extends SyntaxElement {
public boolean isfunccall() { public boolean isfunccall() {
return true; return true;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
public boolean isvarargexp() { public boolean isvarargexp() {
return true; return true;
} }
} }
public static class MethodCall extends FuncCall { public static class MethodCall extends FuncCall {
public final String name; public final String name;
public MethodCall(PrimaryExp lhs, String name, FuncArgs args) { public MethodCall(PrimaryExp lhs, String name, FuncArgs args) {
super(lhs, args); super(lhs, args);
this.name = new String(name); this.name = new String(name);
@@ -244,7 +270,7 @@ public class Exp extends SyntaxElement {
public boolean isfunccall() { public boolean isfunccall() {
return true; return true;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -252,29 +278,31 @@ public class Exp extends SyntaxElement {
public static class Constant extends Exp { public static class Constant extends Exp {
public final LuaValue value; public final LuaValue value;
public Constant(LuaValue value) { public Constant(LuaValue value) {
this.value = value; this.value = value;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class VarargsExp extends Exp { public static class VarargsExp extends Exp {
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
public boolean isvarargexp() { public boolean isvarargexp() {
return true; return true;
} }
} }
public static class UnopExp extends Exp { public static class UnopExp extends Exp {
public final int op; public final int op;
public final Exp rhs; public final Exp rhs;
public UnopExp(int op, Exp rhs) { public UnopExp(int op, Exp rhs) {
this.op = op; this.op = op;
this.rhs = rhs; this.rhs = rhs;
@@ -282,12 +310,13 @@ public class Exp extends SyntaxElement {
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class BinopExp extends Exp { public static class BinopExp extends Exp {
public final Exp lhs,rhs; public final Exp lhs, rhs;
public final int op; public final int op;
public BinopExp(Exp lhs, int op, Exp rhs) { public BinopExp(Exp lhs, int op, Exp rhs) {
this.lhs = lhs; this.lhs = lhs;
this.op = op; this.op = op;
@@ -296,18 +325,19 @@ public class Exp extends SyntaxElement {
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
public static class AnonFuncDef extends Exp { public static class AnonFuncDef extends Exp {
public final FuncBody body; public final FuncBody body;
public AnonFuncDef(FuncBody funcbody) { public AnonFuncDef(FuncBody funcbody) {
this.body = funcbody; this.body = funcbody;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
} }
} }

View File

@@ -29,7 +29,7 @@ import org.luaj.vm2.LuaString;
public class FuncArgs extends SyntaxElement { public class FuncArgs extends SyntaxElement {
public final List<Exp> exps; public final List<Exp> exps;
/** exp1,exp2... */ /** exp1,exp2... */
public static FuncArgs explist(List<Exp> explist) { public static FuncArgs explist(List<Exp> explist) {
return new FuncArgs(explist); return new FuncArgs(explist);
@@ -51,12 +51,12 @@ public class FuncArgs extends SyntaxElement {
public FuncArgs(LuaString string) { public FuncArgs(LuaString string) {
this.exps = new ArrayList<Exp>(); this.exps = new ArrayList<Exp>();
this.exps.add( Exp.constant(string) ); this.exps.add(Exp.constant(string));
} }
public FuncArgs(TableConstructor table) { public FuncArgs(TableConstructor table) {
this.exps = new ArrayList<Exp>(); this.exps = new ArrayList<Exp>();
this.exps.add( table ); this.exps.add(table);
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {

View File

@@ -22,14 +22,15 @@
package org.luaj.vm2.ast; package org.luaj.vm2.ast;
public class FuncBody extends SyntaxElement { public class FuncBody extends SyntaxElement {
public ParList parlist; public ParList parlist;
public Block block; public Block block;
public NameScope scope; public NameScope scope;
public FuncBody(ParList parlist, Block block) { public FuncBody(ParList parlist, Block block) {
this.parlist = parlist!=null? parlist: ParList.EMPTY_PARLIST; this.parlist = parlist != null? parlist: ParList.EMPTY_PARLIST;
this.block = block; this.block = block;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }

View File

@@ -26,24 +26,24 @@ import java.util.List;
public class FuncName extends SyntaxElement { public class FuncName extends SyntaxElement {
// example: a.b.c.d:e // example: a.b.c.d:e
// initial base name: "a" // initial base name: "a"
public final Name name; public final Name name;
// intermediate field accesses: "b", "c", "d" // intermediate field accesses: "b", "c", "d"
public List<String> dots; public List<String> dots;
// optional final method name: "e" // optional final method name: "e"
public String method; public String method;
public FuncName( String name ) { public FuncName(String name) {
this.name = new Name(name); this.name = new Name(name);
} }
public void adddot(String dot) { public void adddot(String dot) {
if ( dots == null ) if (dots == null)
dots = new ArrayList<String>(); dots = new ArrayList<String>();
dots.add(dot); dots.add(dot);
} }
} }

View File

@@ -21,10 +21,10 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2.ast; package org.luaj.vm2.ast;
public class Name { public class Name {
public final String name; public final String name;
public Variable variable; public Variable variable;
public Name(String name) { public Name(String name) {
this.name = name; this.name = name;
} }

View File

@@ -13,10 +13,10 @@ import org.luaj.vm2.ast.Stat.LocalAssign;
import org.luaj.vm2.ast.Stat.LocalFuncDef; import org.luaj.vm2.ast.Stat.LocalFuncDef;
import org.luaj.vm2.ast.Stat.NumericFor; import org.luaj.vm2.ast.Stat.NumericFor;
/** /**
* Visitor that resolves names to scopes. * Visitor that resolves names to scopes. Each Name is resolved to a
* Each Name is resolved to a NamedVarible, possibly in a NameScope * NamedVarible, possibly in a NameScope if it is a local, or in no named scope
* if it is a local, or in no named scope if it is a global. * if it is a global.
*/ */
public class NameResolver extends Visitor { public class NameResolver extends Visitor {
@@ -25,12 +25,13 @@ public class NameResolver extends Visitor {
private void pushScope() { private void pushScope() {
scope = new NameScope(scope); scope = new NameScope(scope);
} }
private void popScope() { private void popScope() {
scope = scope.outerScope; scope = scope.outerScope;
} }
public void visit(NameScope scope) { public void visit(NameScope scope) {
} }
public void visit(Block block) { public void visit(Block block) {
pushScope(); pushScope();
@@ -38,7 +39,7 @@ public class NameResolver extends Visitor {
super.visit(block); super.visit(block);
popScope(); popScope();
} }
public void visit(FuncBody body) { public void visit(FuncBody body) {
pushScope(); pushScope();
scope.functionNestingCount++; scope.functionNestingCount++;
@@ -46,7 +47,7 @@ public class NameResolver extends Visitor {
super.visit(body); super.visit(body);
popScope(); popScope();
} }
public void visit(LocalFuncDef stat) { public void visit(LocalFuncDef stat) {
defineLocalVar(stat.name); defineLocalVar(stat.name);
super.visit(stat); super.visit(stat);
@@ -63,7 +64,7 @@ public class NameResolver extends Visitor {
public void visit(GenericFor stat) { public void visit(GenericFor stat) {
pushScope(); pushScope();
stat.scope = scope; stat.scope = scope;
defineLocalVars( stat.names ); defineLocalVars(stat.names);
super.visit(stat); super.visit(stat);
popScope(); popScope();
} }
@@ -72,16 +73,16 @@ public class NameResolver extends Visitor {
exp.name.variable = resolveNameReference(exp.name); exp.name.variable = resolveNameReference(exp.name);
super.visit(exp); super.visit(exp);
} }
public void visit(FuncDef stat) { public void visit(FuncDef stat) {
stat.name.name.variable = resolveNameReference(stat.name.name); stat.name.name.variable = resolveNameReference(stat.name.name);
stat.name.name.variable.hasassignments = true; stat.name.name.variable.hasassignments = true;
super.visit(stat); super.visit(stat);
} }
public void visit(Assign stat) { public void visit(Assign stat) {
super.visit(stat); super.visit(stat);
for ( int i=0, n=stat.vars.size(); i<n; i++ ) { for (int i = 0, n = stat.vars.size(); i < n; i++) {
VarExp v = (VarExp) stat.vars.get(i); VarExp v = (VarExp) stat.vars.get(i);
v.markHasAssignment(); v.markHasAssignment();
} }
@@ -89,38 +90,38 @@ public class NameResolver extends Visitor {
public void visit(LocalAssign stat) { public void visit(LocalAssign stat) {
visitExps(stat.values); visitExps(stat.values);
defineLocalVars( stat.names ); defineLocalVars(stat.names);
int n = stat.names.size(); int n = stat.names.size();
int m = stat.values!=null? stat.values.size(): 0; int m = stat.values != null? stat.values.size(): 0;
boolean isvarlist = m>0 && m<n && ((Exp)stat.values.get(m-1)).isvarargexp(); boolean isvarlist = m > 0 && m < n && ((Exp) stat.values.get(m-1)).isvarargexp();
for ( int i=0; i<n && i<(isvarlist?m-1:m); i++ ) for (int i = 0; i < n && i < (isvarlist? m-1: m); i++)
if ( stat.values.get(i) instanceof Constant ) if (stat.values.get(i) instanceof Constant)
((Name)stat.names.get(i)).variable.initialValue = ((Constant) stat.values.get(i)).value; ((Name) stat.names.get(i)).variable.initialValue = ((Constant) stat.values.get(i)).value;
if ( !isvarlist ) if (!isvarlist)
for ( int i=m; i<n; i++ ) for (int i = m; i < n; i++)
((Name)stat.names.get(i)).variable.initialValue = LuaValue.NIL; ((Name) stat.names.get(i)).variable.initialValue = LuaValue.NIL;
} }
public void visit(ParList pars) { public void visit(ParList pars) {
if ( pars.names != null ) if (pars.names != null)
defineLocalVars(pars.names); defineLocalVars(pars.names);
if ( pars.isvararg ) if (pars.isvararg)
scope.define("arg"); scope.define("arg");
super.visit(pars); super.visit(pars);
} }
protected void defineLocalVars(List<Name> names) { protected void defineLocalVars(List<Name> names) {
for ( int i=0, n=names.size(); i<n; i++ ) for (int i = 0, n = names.size(); i < n; i++)
defineLocalVar((Name) names.get(i)); defineLocalVar((Name) names.get(i));
} }
protected void defineLocalVar(Name name) { protected void defineLocalVar(Name name) {
name.variable = scope.define(name.name); name.variable = scope.define(name.name);
} }
protected Variable resolveNameReference(Name name) { protected Variable resolveNameReference(Name name) {
Variable v = scope.find(name.name); Variable v = scope.find(name.name);
if ( v.isLocal() && scope.functionNestingCount != v.definingScope.functionNestingCount ) if (v.isLocal() && scope.functionNestingCount != v.definingScope.functionNestingCount)
v.isupvalue = true; v.isupvalue = true;
return v; return v;
} }

View File

@@ -26,60 +26,62 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class NameScope { public class NameScope {
private static final Set<String> LUA_KEYWORDS = new HashSet<String>(); private static final Set<String> LUA_KEYWORDS = new HashSet<String>();
static { static {
String[] k = new String[] { String[] k = new String[] { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if",
"and", "break", "do", "else", "elseif", "end", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" };
"false", "for", "function", "if", "in", "local", for (int i = 0; i < k.length; i++)
"nil", "not", "or", "repeat", "return", LUA_KEYWORDS.add(k[i]);
"then", "true", "until", "while" };
for ( int i=0; i<k.length; i++ )
LUA_KEYWORDS.add( k[i] );
} }
public final Map<String,Variable> namedVariables = new HashMap<String,Variable>(); public final Map<String, Variable> namedVariables = new HashMap<String, Variable>();
public final NameScope outerScope; public final NameScope outerScope;
public int functionNestingCount; public int functionNestingCount;
/** Construct default names scope */ /** Construct default names scope */
public NameScope() { public NameScope() {
this.outerScope = null; this.outerScope = null;
this.functionNestingCount = 0; this.functionNestingCount = 0;
} }
/** Construct name scope within another scope*/ /** Construct name scope within another scope */
public NameScope(NameScope outerScope) { public NameScope(NameScope outerScope) {
this.outerScope = outerScope; this.outerScope = outerScope;
this.functionNestingCount = outerScope!=null? outerScope.functionNestingCount: 0; this.functionNestingCount = outerScope != null? outerScope.functionNestingCount: 0;
} }
/** Look up a name. If it is a global name, then throw IllegalArgumentException. */ /**
public Variable find( String name ) throws IllegalArgumentException { * Look up a name. If it is a global name, then throw
* IllegalArgumentException.
*/
public Variable find(String name) throws IllegalArgumentException {
validateIsNotKeyword(name); validateIsNotKeyword(name);
for ( NameScope n = this; n!=null; n=n.outerScope ) for (NameScope n = this; n != null; n = n.outerScope)
if ( n.namedVariables.containsKey(name) ) if (n.namedVariables.containsKey(name))
return (Variable)n.namedVariables.get(name); return (Variable) n.namedVariables.get(name);
Variable value = new Variable(name); Variable value = new Variable(name);
this.namedVariables.put(name, value); this.namedVariables.put(name, value);
return value; return value;
} }
/** Define a name in this scope. If it is a global name, then throw IllegalArgumentException. */ /**
public Variable define( String name ) throws IllegalStateException, IllegalArgumentException { * Define a name in this scope. If it is a global name, then throw
* IllegalArgumentException.
*/
public Variable define(String name) throws IllegalStateException, IllegalArgumentException {
validateIsNotKeyword(name); validateIsNotKeyword(name);
Variable value = new Variable(name, this); Variable value = new Variable(name, this);
this.namedVariables.put(name, value); this.namedVariables.put(name, value);
return value; return value;
} }
private void validateIsNotKeyword(String name) { private void validateIsNotKeyword(String name) {
if ( LUA_KEYWORDS.contains(name) ) if (LUA_KEYWORDS.contains(name))
throw new IllegalArgumentException("name is a keyword: '"+name+"'"); throw new IllegalArgumentException("name is a keyword: '" + name + "'");
} }
} }

View File

@@ -26,10 +26,10 @@ import java.util.List;
public class ParList extends SyntaxElement { public class ParList extends SyntaxElement {
public static final List<Name> EMPTY_NAMELIST = new ArrayList<Name>(); public static final List<Name> EMPTY_NAMELIST = new ArrayList<Name>();
public static final ParList EMPTY_PARLIST = new ParList(EMPTY_NAMELIST,false); public static final ParList EMPTY_PARLIST = new ParList(EMPTY_NAMELIST, false);
public final List<Name> names; public final List<Name> names;
public final boolean isvararg; public final boolean isvararg;
public ParList(List<Name> names, boolean isvararg) { public ParList(List<Name> names, boolean isvararg) {
this.names = names; this.names = names;

View File

@@ -25,8 +25,7 @@ import java.util.List;
import org.luaj.vm2.ast.Exp.VarExp; import org.luaj.vm2.ast.Exp.VarExp;
abstract abstract public class Stat extends SyntaxElement {
public class Stat extends SyntaxElement {
public abstract void accept(Visitor visitor); public abstract void accept(Visitor visitor);
public static Stat block(Block block) { public static Stat block(Block block) {
@@ -39,8 +38,8 @@ public class Stat extends SyntaxElement {
public static Stat repeatuntil(Block block, Exp exp) { public static Stat repeatuntil(Block block, Exp exp) {
return new RepeatUntil(block, exp); return new RepeatUntil(block, exp);
} }
public static Stat breakstat() { public static Stat breakstat() {
return new Break(); return new Break();
} }
@@ -50,7 +49,7 @@ public class Stat extends SyntaxElement {
} }
public static Stat assignment(List<VarExp> vars, List<Exp> exps) { public static Stat assignment(List<VarExp> vars, List<Exp> exps) {
return new Assign(vars,exps); return new Assign(vars, exps);
} }
public static Stat functioncall(Exp.FuncCall funccall) { public static Stat functioncall(Exp.FuncCall funccall) {
@@ -66,18 +65,19 @@ public class Stat extends SyntaxElement {
} }
public static Stat functiondef(FuncName funcname, FuncBody funcbody) { public static Stat functiondef(FuncName funcname, FuncBody funcbody) {
return new FuncDef( funcname, funcbody ); return new FuncDef(funcname, funcbody);
} }
public static Stat forgeneric(List<Name> names, List<Exp> exps, Block block) { public static Stat forgeneric(List<Name> names, List<Exp> exps, Block block) {
return new GenericFor(names, exps, block); return new GenericFor(names, exps, block);
} }
public static Stat localassignment(List<Name> names, List<Exp> values) { public static Stat localassignment(List<Name> names, List<Exp> values) {
return new LocalAssign(names, values); return new LocalAssign(names, values);
} }
public static Stat ifthenelse(Exp ifexp, Block ifblock, List<Exp> elseifexps, List<Block> elseifblocks, Block elseblock) { public static Stat ifthenelse(Exp ifexp, Block ifblock, List<Exp> elseifexps, List<Block> elseifblocks,
Block elseblock) {
return new IfThenElse(ifexp, ifblock, elseifexps, elseifblocks, elseblock); return new IfThenElse(ifexp, ifblock, elseifexps, elseifblocks, elseblock);
} }
@@ -91,9 +91,11 @@ public class Stat extends SyntaxElement {
public static class Goto extends Stat { public static class Goto extends Stat {
public final String name; public final String name;
public Goto(String name) { public Goto(String name) {
this.name = name; this.name = name;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -101,9 +103,11 @@ public class Stat extends SyntaxElement {
public static class Label extends Stat { public static class Label extends Stat {
public final String name; public final String name;
public Label(String name) { public Label(String name) {
this.name = name; this.name = name;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -111,8 +115,8 @@ public class Stat extends SyntaxElement {
public static class Assign extends Stat { public static class Assign extends Stat {
public final List<VarExp> vars; public final List<VarExp> vars;
public final List<Exp> exps; public final List<Exp> exps;
public Assign(List<VarExp> vars, List<Exp> exps) { public Assign(List<VarExp> vars, List<Exp> exps) {
this.vars = vars; this.vars = vars;
this.exps = exps; this.exps = exps;
@@ -125,95 +129,104 @@ public class Stat extends SyntaxElement {
} }
public static class WhileDo extends Stat { public static class WhileDo extends Stat {
public final Exp exp; public final Exp exp;
public final Block block; public final Block block;
public WhileDo( Exp exp, Block block ) {
public WhileDo(Exp exp, Block block) {
this.exp = exp; this.exp = exp;
this.block = block; this.block = block;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class RepeatUntil extends Stat { public static class RepeatUntil extends Stat {
public final Block block; public final Block block;
public final Exp exp; public final Exp exp;
public RepeatUntil( Block block, Exp exp ) {
public RepeatUntil(Block block, Exp exp) {
this.block = block; this.block = block;
this.exp = exp; this.exp = exp;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class Break extends Stat { public static class Break extends Stat {
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class Return extends Stat { public static class Return extends Stat {
public final List<Exp> values; public final List<Exp> values;
public Return(List<Exp> values) { public Return(List<Exp> values) {
this.values = values; this.values = values;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
public int nreturns() { public int nreturns() {
int n = values!=null? values.size(): 0; int n = values != null? values.size(): 0;
if ( n>0 && ((Exp)values.get(n-1)).isvarargexp() ) if (n > 0 && ((Exp) values.get(n-1)).isvarargexp())
n = -1; n = -1;
return n; return n;
} }
} }
public static class FuncCallStat extends Stat { public static class FuncCallStat extends Stat {
public final Exp.FuncCall funccall; public final Exp.FuncCall funccall;
public FuncCallStat(Exp.FuncCall funccall) { public FuncCallStat(Exp.FuncCall funccall) {
this.funccall = funccall; this.funccall = funccall;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class LocalFuncDef extends Stat { public static class LocalFuncDef extends Stat {
public final Name name; public final Name name;
public final FuncBody body; public final FuncBody body;
public LocalFuncDef(String name, FuncBody body) { public LocalFuncDef(String name, FuncBody body) {
this.name = new Name(name); this.name = new Name(name);
this.body = body; this.body = body;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class FuncDef extends Stat { public static class FuncDef extends Stat {
public final FuncName name; public final FuncName name;
public final FuncBody body; public final FuncBody body;
public FuncDef(FuncName name, FuncBody body) { public FuncDef(FuncName name, FuncBody body) {
this.name = name; this.name = name;
this.body = body; this.body = body;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class GenericFor extends Stat { public static class GenericFor extends Stat {
public List<Name> names; public List<Name> names;
public List<Exp> exps; public List<Exp> exps;
public Block block; public Block block;
public NameScope scope; public NameScope scope;
public GenericFor(List<Name> names, List<Exp> exps, Block block) { public GenericFor(List<Name> names, List<Exp> exps, Block block) {
this.names = names; this.names = names;
this.exps = exps; this.exps = exps;
@@ -221,15 +234,16 @@ public class Stat extends SyntaxElement {
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class NumericFor extends Stat { public static class NumericFor extends Stat {
public final Name name; public final Name name;
public final Exp initial,limit,step; public final Exp initial, limit, step;
public final Block block; public final Block block;
public NameScope scope; public NameScope scope;
public NumericFor(String name, Exp initial, Exp limit, Exp step, Block block) { public NumericFor(String name, Exp initial, Exp limit, Exp step, Block block) {
this.name = new Name(name); this.name = new Name(name);
this.initial = initial; this.initial = initial;
@@ -239,31 +253,32 @@ public class Stat extends SyntaxElement {
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class LocalAssign extends Stat { public static class LocalAssign extends Stat {
public final List<Name> names; public final List<Name> names;
public final List<Exp> values; public final List<Exp> values;
public LocalAssign(List<Name> names, List<Exp> values) { public LocalAssign(List<Name> names, List<Exp> values) {
this.names = names; this.names = names;
this.values = values; this.values = values;
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
public static class IfThenElse extends Stat { public static class IfThenElse extends Stat {
public final Exp ifexp; public final Exp ifexp;
public final Block ifblock; public final Block ifblock;
public final List<Exp> elseifexps; public final List<Exp> elseifexps;
public final List<Block> elseifblocks; public final List<Block> elseifblocks;
public final Block elseblock; public final Block elseblock;
public IfThenElse(Exp ifexp, Block ifblock, List<Exp> elseifexps,
List<Block> elseifblocks, Block elseblock) { public IfThenElse(Exp ifexp, Block ifblock, List<Exp> elseifexps, List<Block> elseifblocks, Block elseblock) {
this.ifexp = ifexp; this.ifexp = ifexp;
this.ifblock = ifblock; this.ifblock = ifblock;
this.elseifexps = elseifexps; this.elseifexps = elseifexps;
@@ -272,7 +287,7 @@ public class Stat extends SyntaxElement {
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit( this ); visitor.visit(this);
} }
} }
} }

View File

@@ -27,65 +27,95 @@ import java.io.UnsupportedEncodingException;
import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaString;
public class Str { public class Str {
private Str() {} private Str() {}
public static LuaString quoteString(String image) { public static LuaString quoteString(String image) {
String s = image.substring(1, image.length()-1); String s = image.substring(1, image.length()-1);
byte[] bytes = unquote(s); byte[] bytes = unquote(s);
return LuaString.valueUsing(bytes); return LuaString.valueUsing(bytes);
} }
public static LuaString charString(String image) { public static LuaString charString(String image) {
String s = image.substring(1, image.length()-1); String s = image.substring(1, image.length()-1);
byte[] bytes = unquote(s); byte[] bytes = unquote(s);
return LuaString.valueUsing(bytes); return LuaString.valueUsing(bytes);
} }
public static LuaString longString(String image) { public static LuaString longString(String image) {
int i = image.indexOf('[', image.indexOf('[')+1)+1; int i = image.indexOf('[', image.indexOf('[')+1)+1;
String s = image.substring(i,image.length()-i); String s = image.substring(i, image.length()-i);
byte[] b = iso88591bytes(s); byte[] b = iso88591bytes(s);
return LuaString.valueUsing(b); return LuaString.valueUsing(b);
} }
public static byte[] iso88591bytes( String s ) { public static byte[] iso88591bytes(String s) {
try { try {
return s.getBytes("ISO8859-1"); return s.getBytes("ISO8859-1");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new IllegalStateException("ISO8859-1 not supported"); throw new IllegalStateException("ISO8859-1 not supported");
} }
} }
public static byte[] unquote(String s) { public static byte[] unquote(String s) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
char[] c = s.toCharArray(); char[] c = s.toCharArray();
int n = c.length; int n = c.length;
for ( int i=0; i<n; i++ ) { for (int i = 0; i < n; i++) {
if ( c[i] == '\\' && i<n ) { if (c[i] == '\\' && i < n) {
switch ( c[++i] ) { switch (c[++i]) {
case '0': case '1': case '2': case '3': case '4': case '0':
case '5': case '6': case '7': case '8': case '9': case '1':
int d=(int) (c[i++]-'0'); case '2':
for ( int j=0; i<n && j<2 && c[i]>='0' && c[i]<='9'; i++, j++ ) case '3':
d = d * 10 + (int) (c[i]-'0'); case '4':
baos.write( (byte) d ); case '5':
case '6':
case '7':
case '8':
case '9':
int d = (int) (c[i++]-'0');
for (int j = 0; i < n && j < 2 && c[i] >= '0' && c[i] <= '9'; i++, j++)
d = d*10+(int) (c[i]-'0');
baos.write((byte) d);
--i; --i;
continue; continue;
case 'a': baos.write( (byte) 7 ); continue; case 'a':
case 'b': baos.write( (byte) '\b' ); continue; baos.write((byte) 7);
case 'f': baos.write( (byte) '\f' ); continue; continue;
case 'n': baos.write( (byte) '\n' ); continue; case 'b':
case 'r': baos.write( (byte) '\r' ); continue; baos.write((byte) '\b');
case 't': baos.write( (byte) '\t' ); continue; continue;
case 'v': baos.write( (byte) 11 ); continue; case 'f':
case '"': baos.write( (byte) '"' ); continue; baos.write((byte) '\f');
case '\'': baos.write( (byte) '\'' ); continue; continue;
case '\\': baos.write( (byte) '\\' ); continue; case 'n':
default: baos.write( (byte) c[i] ); break; baos.write((byte) '\n');
continue;
case 'r':
baos.write((byte) '\r');
continue;
case 't':
baos.write((byte) '\t');
continue;
case 'v':
baos.write((byte) 11);
continue;
case '"':
baos.write((byte) '"');
continue;
case '\'':
baos.write((byte) '\'');
continue;
case '\\':
baos.write((byte) '\\');
continue;
default:
baos.write((byte) c[i]);
break;
} }
} else { } else {
baos.write( (byte) c[i] ); baos.write((byte) c[i]);
} }
} }
return baos.toByteArray(); return baos.toByteArray();

View File

@@ -21,17 +21,18 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2.ast; package org.luaj.vm2.ast;
/** Base class for syntax elements of the parse tree that appear in source files. /**
* The LuaParser class will fill these values out during parsing for use in * Base class for syntax elements of the parse tree that appear in source files.
* The LuaParser class will fill these values out during parsing for use in
* syntax highlighting, for example. * syntax highlighting, for example.
*/ */
public class SyntaxElement { public class SyntaxElement {
/** The line number on which the element begins. */ /** The line number on which the element begins. */
public int beginLine; public int beginLine;
/** The column at which the element begins. */ /** The column at which the element begins. */
public short beginColumn; public short beginColumn;
/** The line number on which the element ends. */ /** The line number on which the element ends. */
public int endLine; public int endLine;

View File

@@ -23,16 +23,16 @@ package org.luaj.vm2.ast;
public class TableField extends SyntaxElement { public class TableField extends SyntaxElement {
public final Exp index; public final Exp index;
public final String name; public final String name;
public final Exp rhs; public final Exp rhs;
public TableField(Exp index, String name, Exp rhs) { public TableField(Exp index, String name, Exp rhs) {
this.index = index; this.index = index;
this.name = name; this.name = name;
this.rhs = rhs; this.rhs = rhs;
} }
public static TableField keyedField(Exp index, Exp rhs) { public static TableField keyedField(Exp index, Exp rhs) {
return new TableField(index, null, rhs); return new TableField(index, null, rhs);
} }

View File

@@ -23,40 +23,43 @@ package org.luaj.vm2.ast;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
/** Variable is created lua name scopes, and is a named, lua variable that /**
* either refers to a lua local, global, or upvalue storage location. * Variable is created lua name scopes, and is a named, lua variable that either
* refers to a lua local, global, or upvalue storage location.
*/ */
public class Variable { public class Variable {
/** The name as it appears in lua source code */ /** The name as it appears in lua source code */
public final String name; public final String name;
/** The lua scope in which this variable is defined. */ /** The lua scope in which this variable is defined. */
public final NameScope definingScope; public final NameScope definingScope;
/** true if this variable is an upvalue */ /** true if this variable is an upvalue */
public boolean isupvalue; public boolean isupvalue;
/** true if there are assignments made to this variable */ /** true if there are assignments made to this variable */
public boolean hasassignments; public boolean hasassignments;
/** When hasassignments == false, and the initial value is a constant, this is the initial value */ /**
* When hasassignments == false, and the initial value is a constant, this
* is the initial value
*/
public LuaValue initialValue; public LuaValue initialValue;
/** Global is named variable not associated with a defining scope */ /** Global is named variable not associated with a defining scope */
public Variable(String name) { public Variable(String name) {
this.name = name; this.name = name;
this.definingScope = null; this.definingScope = null;
} }
public Variable(String name, NameScope definingScope) { public Variable(String name, NameScope definingScope) {
/** Local variable is defined in a particular scope. */ /** Local variable is defined in a particular scope. */
this.name = name; this.name = name;
this.definingScope = definingScope; this.definingScope = definingScope;
} }
public boolean isLocal() {
return this.definingScope != null; public boolean isLocal() { return this.definingScope != null; }
}
public boolean isConstant() { public boolean isConstant() { return !hasassignments && initialValue != null; }
return ! hasassignments && initialValue != null; }
}
}

View File

@@ -26,155 +26,192 @@ import java.util.List;
import org.luaj.vm2.ast.Exp.VarExp; import org.luaj.vm2.ast.Exp.VarExp;
abstract public class Visitor { abstract public class Visitor {
public void visit(Chunk chunk) { public void visit(Chunk chunk) {
chunk.block.accept(this); chunk.block.accept(this);
}; };
public void visit(Block block) { public void visit(Block block) {
visit(block.scope); visit(block.scope);
if ( block.stats != null ) if (block.stats != null)
for ( int i=0, n=block.stats.size(); i<n; i++ ) for (int i = 0, n = block.stats.size(); i < n; i++)
((Stat)block.stats.get(i)).accept(this); ((Stat) block.stats.get(i)).accept(this);
}; };
public void visit(Stat.Assign stat) { public void visit(Stat.Assign stat) {
visitVars(stat.vars); visitVars(stat.vars);
visitExps(stat.exps); visitExps(stat.exps);
} }
public void visit(Stat.Break breakstat) { public void visit(Stat.Break breakstat) {
} }
public void visit(Stat.FuncCallStat stat) { public void visit(Stat.FuncCallStat stat) {
stat.funccall.accept(this); stat.funccall.accept(this);
} }
public void visit(Stat.FuncDef stat) { public void visit(Stat.FuncDef stat) {
stat.body.accept(this); stat.body.accept(this);
} }
public void visit(Stat.GenericFor stat) { public void visit(Stat.GenericFor stat) {
visit(stat.scope); visit(stat.scope);
visitNames(stat.names); visitNames(stat.names);
visitExps(stat.exps); visitExps(stat.exps);
stat.block.accept(this); stat.block.accept(this);
} }
public void visit(Stat.IfThenElse stat) { public void visit(Stat.IfThenElse stat) {
stat.ifexp.accept(this); stat.ifexp.accept(this);
stat.ifblock.accept(this); stat.ifblock.accept(this);
if ( stat.elseifblocks != null ) if (stat.elseifblocks != null)
for ( int i=0, n=stat.elseifblocks.size(); i<n; i++ ) { for (int i = 0, n = stat.elseifblocks.size(); i < n; i++) {
((Exp)stat.elseifexps.get(i)).accept(this); ((Exp) stat.elseifexps.get(i)).accept(this);
((Block)stat.elseifblocks.get(i)).accept(this); ((Block) stat.elseifblocks.get(i)).accept(this);
} }
if ( stat.elseblock != null ) if (stat.elseblock != null)
visit( stat.elseblock ); visit(stat.elseblock);
} }
public void visit(Stat.LocalAssign stat) { public void visit(Stat.LocalAssign stat) {
visitNames(stat.names); visitNames(stat.names);
visitExps(stat.values); visitExps(stat.values);
} }
public void visit(Stat.LocalFuncDef stat) { public void visit(Stat.LocalFuncDef stat) {
visit(stat.name); visit(stat.name);
stat.body.accept(this); stat.body.accept(this);
} }
public void visit(Stat.NumericFor stat) { public void visit(Stat.NumericFor stat) {
visit(stat.scope); visit(stat.scope);
visit(stat.name); visit(stat.name);
stat.initial.accept(this); stat.initial.accept(this);
stat.limit.accept(this); stat.limit.accept(this);
if ( stat.step != null ) if (stat.step != null)
stat.step.accept(this); stat.step.accept(this);
stat.block.accept(this); stat.block.accept(this);
} }
public void visit(Stat.RepeatUntil stat) { public void visit(Stat.RepeatUntil stat) {
stat.block.accept(this); stat.block.accept(this);
stat.exp.accept(this); stat.exp.accept(this);
} }
public void visit(Stat.Return stat) { public void visit(Stat.Return stat) {
visitExps(stat.values); visitExps(stat.values);
} }
public void visit(Stat.WhileDo stat) { public void visit(Stat.WhileDo stat) {
stat.exp.accept(this); stat.exp.accept(this);
stat.block.accept(this); stat.block.accept(this);
} }
public void visit(FuncBody body) { public void visit(FuncBody body) {
visit(body.scope); visit(body.scope);
body.parlist.accept(this); body.parlist.accept(this);
body.block.accept(this); body.block.accept(this);
} }
public void visit(FuncArgs args) { public void visit(FuncArgs args) {
visitExps(args.exps); visitExps(args.exps);
} }
public void visit(TableField field) { public void visit(TableField field) {
if ( field.name != null ) if (field.name != null)
visit( field.name ); visit(field.name);
if ( field.index != null ) if (field.index != null)
field.index.accept(this); field.index.accept(this);
field.rhs.accept(this); field.rhs.accept(this);
} }
public void visit(Exp.AnonFuncDef exp) { public void visit(Exp.AnonFuncDef exp) {
exp.body.accept(this); exp.body.accept(this);
} }
public void visit(Exp.BinopExp exp) { public void visit(Exp.BinopExp exp) {
exp.lhs.accept(this); exp.lhs.accept(this);
exp.rhs.accept(this); exp.rhs.accept(this);
} }
public void visit(Exp.Constant exp) { public void visit(Exp.Constant exp) {
} }
public void visit(Exp.FieldExp exp) { public void visit(Exp.FieldExp exp) {
exp.lhs.accept(this); exp.lhs.accept(this);
visit(exp.name); visit(exp.name);
} }
public void visit(Exp.FuncCall exp) { public void visit(Exp.FuncCall exp) {
exp.lhs.accept(this); exp.lhs.accept(this);
exp.args.accept(this); exp.args.accept(this);
} }
public void visit(Exp.IndexExp exp) { public void visit(Exp.IndexExp exp) {
exp.lhs.accept(this); exp.lhs.accept(this);
exp.exp.accept(this); exp.exp.accept(this);
} }
public void visit(Exp.MethodCall exp) { public void visit(Exp.MethodCall exp) {
exp.lhs.accept(this); exp.lhs.accept(this);
visit(exp.name); visit(exp.name);
exp.args.accept(this); exp.args.accept(this);
} }
public void visit(Exp.NameExp exp) { public void visit(Exp.NameExp exp) {
visit(exp.name); visit(exp.name);
} }
public void visit(Exp.ParensExp exp) { public void visit(Exp.ParensExp exp) {
exp.exp.accept(this); exp.exp.accept(this);
} }
public void visit(Exp.UnopExp exp) { public void visit(Exp.UnopExp exp) {
exp.rhs.accept(this); exp.rhs.accept(this);
} }
public void visit(Exp.VarargsExp exp) { public void visit(Exp.VarargsExp exp) {
} }
public void visit(ParList pars) { public void visit(ParList pars) {
visitNames(pars.names); visitNames(pars.names);
} }
public void visit(TableConstructor table) { public void visit(TableConstructor table) {
if( table.fields != null) if (table.fields != null)
for ( int i=0, n=table.fields.size(); i<n; i++ ) for (int i = 0, n = table.fields.size(); i < n; i++)
((TableField)table.fields.get(i)).accept(this); ((TableField) table.fields.get(i)).accept(this);
} }
public void visitVars(List<VarExp> vars) { public void visitVars(List<VarExp> vars) {
if ( vars != null ) if (vars != null)
for ( int i=0, n=vars.size(); i<n; i++ ) for (int i = 0, n = vars.size(); i < n; i++)
((Exp.VarExp)vars.get(i)).accept(this); ((Exp.VarExp) vars.get(i)).accept(this);
} }
public void visitExps(List<Exp> exps) { public void visitExps(List<Exp> exps) {
if ( exps != null ) if (exps != null)
for ( int i=0, n=exps.size(); i<n; i++ ) for (int i = 0, n = exps.size(); i < n; i++)
((Exp)exps.get(i)).accept(this); ((Exp) exps.get(i)).accept(this);
} }
public void visitNames(List<Name> names) { public void visitNames(List<Name> names) {
if ( names != null ) if (names != null)
for ( int i=0, n=names.size(); i<n; i++ ) for (int i = 0, n = names.size(); i < n; i++)
visit((Name) names.get(i)); visit((Name) names.get(i));
} }
public void visit(Name name) { public void visit(Name name) {
} }
public void visit(String name) { public void visit(String name) {
} }
public void visit(NameScope scope) { public void visit(NameScope scope) {
} }
public void visit(Stat.Goto gotostat) { public void visit(Stat.Goto gotostat) {
} }
public void visit(Stat.Label label) { public void visit(Stat.Label label) {
} }
} }

View File

@@ -32,13 +32,13 @@ import org.luaj.vm2.LuaUserdata;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
/** /**
* Helper class to coerce values from Java to lua within the luajava library. * Helper class to coerce values from Java to lua within the luajava library.
* <p> * <p>
* This class is primarily used by the {@link org.luaj.vm2.lib.jse.LuajavaLib}, * This class is primarily used by the {@link org.luaj.vm2.lib.jse.LuajavaLib},
* but can also be used directly when working with Java/lua bindings. * but can also be used directly when working with Java/lua bindings.
* <p> * <p>
* To coerce scalar types, the various, generally the {@code valueOf(type)} methods * To coerce scalar types, the various, generally the {@code valueOf(type)}
* on {@link LuaValue} may be used: * methods on {@link LuaValue} may be used:
* <ul> * <ul>
* <li>{@link LuaValue#valueOf(boolean)}</li> * <li>{@link LuaValue#valueOf(boolean)}</li>
* <li>{@link LuaValue#valueOf(byte[])}</li> * <li>{@link LuaValue#valueOf(byte[])}</li>
@@ -47,69 +47,69 @@ import org.luaj.vm2.LuaValue;
* <li>{@link LuaValue#valueOf(String)}</li> * <li>{@link LuaValue#valueOf(String)}</li>
* </ul> * </ul>
* <p> * <p>
* To coerce arrays of objects and lists, the {@code listOf(..)} and {@code tableOf(...)} methods * To coerce arrays of objects and lists, the {@code listOf(..)} and
* on {@link LuaValue} may be used: * {@code tableOf(...)} methods on {@link LuaValue} may be used:
* <ul> * <ul>
* <li>{@link LuaValue#listOf(LuaValue[])}</li> * <li>{@link LuaValue#listOf(LuaValue[])}</li>
* <li>{@link LuaValue#listOf(LuaValue[], org.luaj.vm2.Varargs)}</li> * <li>{@link LuaValue#listOf(LuaValue[], org.luaj.vm2.Varargs)}</li>
* <li>{@link LuaValue#tableOf(LuaValue[])}</li> * <li>{@link LuaValue#tableOf(LuaValue[])}</li>
* <li>{@link LuaValue#tableOf(LuaValue[], LuaValue[], org.luaj.vm2.Varargs)}</li> * <li>{@link LuaValue#tableOf(LuaValue[], LuaValue[], org.luaj.vm2.Varargs)}</li>
* </ul> * </ul>
* The method {@link CoerceJavaToLua#coerce(Object)} looks as the type and dimesioning * The method {@link CoerceJavaToLua#coerce(Object)} looks as the type and
* of the argument and tries to guess the best fit for corrsponding lua scalar, * dimesioning of the argument and tries to guess the best fit for corrsponding
* table, or table of tables. * lua scalar, table, or table of tables.
* *
* @see CoerceJavaToLua#coerce(Object) * @see CoerceJavaToLua#coerce(Object)
* @see org.luaj.vm2.lib.jse.LuajavaLib * @see org.luaj.vm2.lib.jse.LuajavaLib
*/ */
public class CoerceJavaToLua { public class CoerceJavaToLua {
static interface Coercion { static interface Coercion {
public LuaValue coerce( Object javaValue ); public LuaValue coerce(Object javaValue);
}; };
private static final class BoolCoercion implements Coercion { private static final class BoolCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
Boolean b = (Boolean) javaValue; Boolean b = (Boolean) javaValue;
return b.booleanValue()? LuaValue.TRUE: LuaValue.FALSE; return b.booleanValue()? LuaValue.TRUE: LuaValue.FALSE;
} }
} }
private static final class IntCoercion implements Coercion { private static final class IntCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
Number n = (Number) javaValue; Number n = (Number) javaValue;
return LuaInteger.valueOf( n.intValue() ); return LuaInteger.valueOf(n.intValue());
} }
} }
private static final class CharCoercion implements Coercion { private static final class CharCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
Character c = (Character) javaValue; Character c = (Character) javaValue;
return LuaInteger.valueOf( c.charValue() ); return LuaInteger.valueOf(c.charValue());
} }
} }
private static final class DoubleCoercion implements Coercion { private static final class DoubleCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
Number n = (Number) javaValue; Number n = (Number) javaValue;
return LuaDouble.valueOf( n.doubleValue() ); return LuaDouble.valueOf(n.doubleValue());
} }
} }
private static final class StringCoercion implements Coercion { private static final class StringCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
return LuaString.valueOf( javaValue.toString() ); return LuaString.valueOf(javaValue.toString());
} }
} }
private static final class BytesCoercion implements Coercion { private static final class BytesCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
return LuaValue.valueOf((byte[]) javaValue); return LuaValue.valueOf((byte[]) javaValue);
} }
} }
private static final class ClassCoercion implements Coercion { private static final class ClassCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
return JavaClass.forClass((Class) javaValue); return JavaClass.forClass((Class) javaValue);
} }
} }
@@ -128,46 +128,46 @@ public class CoerceJavaToLua {
} }
private static final class LuaCoercion implements Coercion { private static final class LuaCoercion implements Coercion {
public LuaValue coerce( Object javaValue ) { public LuaValue coerce(Object javaValue) {
return (LuaValue) javaValue; return (LuaValue) javaValue;
} }
} }
static final Map COERCIONS = Collections.synchronizedMap(new HashMap()); static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
static { static {
Coercion boolCoercion = new BoolCoercion() ; Coercion boolCoercion = new BoolCoercion();
Coercion intCoercion = new IntCoercion() ; Coercion intCoercion = new IntCoercion();
Coercion charCoercion = new CharCoercion() ; Coercion charCoercion = new CharCoercion();
Coercion doubleCoercion = new DoubleCoercion() ; Coercion doubleCoercion = new DoubleCoercion();
Coercion stringCoercion = new StringCoercion() ; Coercion stringCoercion = new StringCoercion();
Coercion bytesCoercion = new BytesCoercion() ; Coercion bytesCoercion = new BytesCoercion();
Coercion classCoercion = new ClassCoercion() ; Coercion classCoercion = new ClassCoercion();
COERCIONS.put( Boolean.class, boolCoercion ); COERCIONS.put(Boolean.class, boolCoercion);
COERCIONS.put( Byte.class, intCoercion ); COERCIONS.put(Byte.class, intCoercion);
COERCIONS.put( Character.class, charCoercion ); COERCIONS.put(Character.class, charCoercion);
COERCIONS.put( Short.class, intCoercion ); COERCIONS.put(Short.class, intCoercion);
COERCIONS.put( Integer.class, intCoercion ); COERCIONS.put(Integer.class, intCoercion);
COERCIONS.put( Long.class, doubleCoercion ); COERCIONS.put(Long.class, doubleCoercion);
COERCIONS.put( Float.class, doubleCoercion ); COERCIONS.put(Float.class, doubleCoercion);
COERCIONS.put( Double.class, doubleCoercion ); COERCIONS.put(Double.class, doubleCoercion);
COERCIONS.put( String.class, stringCoercion ); COERCIONS.put(String.class, stringCoercion);
COERCIONS.put( byte[].class, bytesCoercion ); COERCIONS.put(byte[].class, bytesCoercion);
COERCIONS.put( Class.class, classCoercion ); COERCIONS.put(Class.class, classCoercion);
} }
/** /**
* Coerse a Java object to a corresponding lua value. * Coerse a Java object to a corresponding lua value.
* <p> * <p>
* Integral types {@code boolean}, {@code byte}, {@code char}, and {@code int} * Integral types {@code boolean}, {@code byte}, {@code char}, and
* will become {@link LuaInteger}; * {@code int} will become {@link LuaInteger}; {@code long}, {@code float},
* {@code long}, {@code float}, and {@code double} will become {@link LuaDouble}; * and {@code double} will become {@link LuaDouble}; {@code String} and
* {@code String} and {@code byte[]} will become {@link LuaString}; * {@code byte[]} will become {@link LuaString}; types inheriting from
* types inheriting from {@link LuaValue} will be returned without coercion; * {@link LuaValue} will be returned without coercion; other types will
* other types will become {@link LuaUserdata}. * become {@link LuaUserdata}.
*
* @param o Java object needing conversion * @param o Java object needing conversion
* @return {@link LuaValue} corresponding to the supplied Java value. * @return {@link LuaValue} corresponding to the supplied Java value.
* @see LuaValue * @see LuaValue
* @see LuaInteger * @see LuaInteger
* @see LuaDouble * @see LuaDouble
@@ -175,22 +175,20 @@ public class CoerceJavaToLua {
* @see LuaUserdata * @see LuaUserdata
*/ */
public static LuaValue coerce(Object o) { public static LuaValue coerce(Object o) {
if ( o == null ) if (o == null)
return LuaValue.NIL; return LuaValue.NIL;
Class clazz = o.getClass(); Class clazz = o.getClass();
Coercion c = (Coercion) COERCIONS.get( clazz ); Coercion c = (Coercion) COERCIONS.get(clazz);
if ( c == null ) { if (c == null) {
c = clazz.isArray()? arrayCoercion: c = clazz.isArray()? arrayCoercion: o instanceof LuaValue? luaCoercion: instanceCoercion;
o instanceof LuaValue ? luaCoercion: COERCIONS.put(clazz, c);
instanceCoercion;
COERCIONS.put( clazz, c );
} }
return c.coerce(o); return c.coerce(o);
} }
static final Coercion instanceCoercion = new InstanceCoercion(); static final Coercion instanceCoercion = new InstanceCoercion();
static final Coercion arrayCoercion = new ArrayCoercion();
static final Coercion luaCoercion = new LuaCoercion() ; static final Coercion arrayCoercion = new ArrayCoercion();
static final Coercion luaCoercion = new LuaCoercion();
} }

View File

@@ -31,13 +31,13 @@ import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
/** /**
* Helper class to coerce values from lua to Java within the luajava library. * Helper class to coerce values from lua to Java within the luajava library.
* <p> * <p>
* This class is primarily used by the {@link org.luaj.vm2.lib.jse.LuajavaLib}, * This class is primarily used by the {@link org.luaj.vm2.lib.jse.LuajavaLib},
* but can also be used directly when working with Java/lua bindings. * but can also be used directly when working with Java/lua bindings.
* <p> * <p>
* To coerce to specific Java values, generally the {@code toType()} methods * To coerce to specific Java values, generally the {@code toType()} methods on
* on {@link LuaValue} may be used: * {@link LuaValue} may be used:
* <ul> * <ul>
* <li>{@link LuaValue#toboolean()}</li> * <li>{@link LuaValue#toboolean()}</li>
* <li>{@link LuaValue#tobyte()}</li> * <li>{@link LuaValue#tobyte()}</li>
@@ -51,41 +51,45 @@ import org.luaj.vm2.LuaValue;
* <li>{@link LuaValue#touserdata(Class)}</li> * <li>{@link LuaValue#touserdata(Class)}</li>
* </ul> * </ul>
* <p> * <p>
* For data in lua tables, the various methods on {@link LuaTable} can be used directly * For data in lua tables, the various methods on {@link LuaTable} can be used
* to convert data to something more useful. * directly to convert data to something more useful.
* *
* @see org.luaj.vm2.lib.jse.LuajavaLib * @see org.luaj.vm2.lib.jse.LuajavaLib
* @see CoerceJavaToLua * @see CoerceJavaToLua
*/ */
public class CoerceLuaToJava { public class CoerceLuaToJava {
static int SCORE_NULL_VALUE = 0x10; static int SCORE_NULL_VALUE = 0x10;
static int SCORE_WRONG_TYPE = 0x100; static int SCORE_WRONG_TYPE = 0x100;
static int SCORE_UNCOERCIBLE = 0x10000; static int SCORE_UNCOERCIBLE = 0x10000;
static interface Coercion { static interface Coercion {
public int score( LuaValue value ); public int score(LuaValue value);
public Object coerce( LuaValue value );
public Object coerce(LuaValue value);
}; };
/** /**
* Coerce a LuaValue value to a specified java class * Coerce a LuaValue value to a specified java class
*
* @param value LuaValue to coerce * @param value LuaValue to coerce
* @param clazz Class to coerce into * @param clazz Class to coerce into
* @return Object of type clazz (or a subclass) with the corresponding value. * @return Object of type clazz (or a subclass) with the corresponding
* value.
*/ */
public static Object coerce(LuaValue value, Class clazz) { public static Object coerce(LuaValue value, Class clazz) {
return getCoercion(clazz).coerce(value); return getCoercion(clazz).coerce(value);
} }
static final Map COERCIONS = Collections.synchronizedMap(new HashMap()); static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
static final class BoolCoercion implements Coercion { static final class BoolCoercion implements Coercion {
public String toString() { public String toString() {
return "BoolCoercion()"; return "BoolCoercion()";
} }
public int score( LuaValue value ) {
switch ( value.type() ) { public int score(LuaValue value) {
switch (value.type()) {
case LuaValue.TBOOLEAN: case LuaValue.TBOOLEAN:
return 0; return 0;
} }
@@ -98,74 +102,84 @@ public class CoerceLuaToJava {
} }
static final class NumericCoercion implements Coercion { static final class NumericCoercion implements Coercion {
static final int TARGET_TYPE_BYTE = 0; static final int TARGET_TYPE_BYTE = 0;
static final int TARGET_TYPE_CHAR = 1; static final int TARGET_TYPE_CHAR = 1;
static final int TARGET_TYPE_SHORT = 2; static final int TARGET_TYPE_SHORT = 2;
static final int TARGET_TYPE_INT = 3; static final int TARGET_TYPE_INT = 3;
static final int TARGET_TYPE_LONG = 4; static final int TARGET_TYPE_LONG = 4;
static final int TARGET_TYPE_FLOAT = 5; static final int TARGET_TYPE_FLOAT = 5;
static final int TARGET_TYPE_DOUBLE = 6; static final int TARGET_TYPE_DOUBLE = 6;
static final String[] TYPE_NAMES = { "byte", "char", "short", "int", "long", "float", "double" }; static final String[] TYPE_NAMES = { "byte", "char", "short", "int", "long", "float", "double" };
final int targetType; final int targetType;
public String toString() { public String toString() {
return "NumericCoercion("+TYPE_NAMES[targetType]+")"; return "NumericCoercion(" + TYPE_NAMES[targetType] + ")";
} }
NumericCoercion(int targetType) { NumericCoercion(int targetType) {
this.targetType = targetType; this.targetType = targetType;
} }
public int score( LuaValue value ) {
public int score(LuaValue value) {
int fromStringPenalty = 0; int fromStringPenalty = 0;
if ( value.type() == LuaValue.TSTRING ) { if (value.type() == LuaValue.TSTRING) {
value = value.tonumber(); value = value.tonumber();
if ( value.isnil() ) { if (value.isnil()) {
return SCORE_UNCOERCIBLE; return SCORE_UNCOERCIBLE;
} }
fromStringPenalty = 4; fromStringPenalty = 4;
} }
if ( value.isint() ) { if (value.isint()) {
switch ( targetType ) { switch (targetType) {
case TARGET_TYPE_BYTE: { case TARGET_TYPE_BYTE: {
int i = value.toint(); int i = value.toint();
return fromStringPenalty + ((i==(byte)i)? 0: SCORE_WRONG_TYPE); return fromStringPenalty+((i == (byte) i)? 0: SCORE_WRONG_TYPE);
} }
case TARGET_TYPE_CHAR: { case TARGET_TYPE_CHAR: {
int i = value.toint(); int i = value.toint();
return fromStringPenalty + ((i==(byte)i)? 1: (i==(char)i)? 0: SCORE_WRONG_TYPE); return fromStringPenalty+((i == (byte) i)? 1: (i == (char) i)? 0: SCORE_WRONG_TYPE);
} }
case TARGET_TYPE_SHORT: { case TARGET_TYPE_SHORT: {
int i = value.toint(); int i = value.toint();
return fromStringPenalty + return fromStringPenalty+((i == (byte) i)? 1: (i == (short) i)? 0: SCORE_WRONG_TYPE);
((i==(byte)i)? 1: (i==(short)i)? 0: SCORE_WRONG_TYPE);
} }
case TARGET_TYPE_INT: { case TARGET_TYPE_INT: {
int i = value.toint(); int i = value.toint();
return fromStringPenalty + return fromStringPenalty+((i == (byte) i)? 2: ((i == (char) i) || (i == (short) i))? 1: 0);
((i==(byte)i)? 2: ((i==(char)i) || (i==(short)i))? 1: 0);
} }
case TARGET_TYPE_FLOAT: return fromStringPenalty + 1; case TARGET_TYPE_FLOAT:
case TARGET_TYPE_LONG: return fromStringPenalty + 1; return fromStringPenalty+1;
case TARGET_TYPE_DOUBLE: return fromStringPenalty + 2; case TARGET_TYPE_LONG:
default: return SCORE_WRONG_TYPE; return fromStringPenalty+1;
case TARGET_TYPE_DOUBLE:
return fromStringPenalty+2;
default:
return SCORE_WRONG_TYPE;
} }
} else if ( value.isnumber() ) { } else if (value.isnumber()) {
switch ( targetType ) { switch (targetType) {
case TARGET_TYPE_BYTE: return SCORE_WRONG_TYPE; case TARGET_TYPE_BYTE:
case TARGET_TYPE_CHAR: return SCORE_WRONG_TYPE; return SCORE_WRONG_TYPE;
case TARGET_TYPE_SHORT: return SCORE_WRONG_TYPE; case TARGET_TYPE_CHAR:
case TARGET_TYPE_INT: return SCORE_WRONG_TYPE; return SCORE_WRONG_TYPE;
case TARGET_TYPE_SHORT:
return SCORE_WRONG_TYPE;
case TARGET_TYPE_INT:
return SCORE_WRONG_TYPE;
case TARGET_TYPE_LONG: { case TARGET_TYPE_LONG: {
double d = value.todouble(); double d = value.todouble();
return fromStringPenalty + ((d==(long)d)? 0: SCORE_WRONG_TYPE); return fromStringPenalty+((d == (long) d)? 0: SCORE_WRONG_TYPE);
} }
case TARGET_TYPE_FLOAT: { case TARGET_TYPE_FLOAT: {
double d = value.todouble(); double d = value.todouble();
return fromStringPenalty + ((d==(float)d)? 0: SCORE_WRONG_TYPE); return fromStringPenalty+((d == (float) d)? 0: SCORE_WRONG_TYPE);
} }
case TARGET_TYPE_DOUBLE: { case TARGET_TYPE_DOUBLE: {
double d = value.todouble(); double d = value.todouble();
return fromStringPenalty + (((d==(long)d) || (d==(float)d))? 1: 0); return fromStringPenalty+(((d == (long) d) || (d == (float) d))? 1: 0);
} }
default: return SCORE_WRONG_TYPE; default:
return SCORE_WRONG_TYPE;
} }
} else { } else {
return SCORE_UNCOERCIBLE; return SCORE_UNCOERCIBLE;
@@ -173,45 +187,56 @@ public class CoerceLuaToJava {
} }
public Object coerce(LuaValue value) { public Object coerce(LuaValue value) {
switch ( targetType ) { switch (targetType) {
case TARGET_TYPE_BYTE: return new Byte( (byte) value.toint() ); case TARGET_TYPE_BYTE:
case TARGET_TYPE_CHAR: return new Character( (char) value.toint() ); return new Byte((byte) value.toint());
case TARGET_TYPE_SHORT: return new Short( (short) value.toint() ); case TARGET_TYPE_CHAR:
case TARGET_TYPE_INT: return new Integer( (int) value.toint() ); return new Character((char) value.toint());
case TARGET_TYPE_LONG: return new Long( (long) value.todouble() ); case TARGET_TYPE_SHORT:
case TARGET_TYPE_FLOAT: return new Float( (float) value.todouble() ); return new Short((short) value.toint());
case TARGET_TYPE_DOUBLE: return new Double( (double) value.todouble() ); case TARGET_TYPE_INT:
default: return null; return new Integer((int) value.toint());
case TARGET_TYPE_LONG:
return new Long((long) value.todouble());
case TARGET_TYPE_FLOAT:
return new Float((float) value.todouble());
case TARGET_TYPE_DOUBLE:
return new Double((double) value.todouble());
default:
return null;
} }
} }
} }
static final class StringCoercion implements Coercion { static final class StringCoercion implements Coercion {
public static final int TARGET_TYPE_STRING = 0; public static final int TARGET_TYPE_STRING = 0;
public static final int TARGET_TYPE_BYTES = 1; public static final int TARGET_TYPE_BYTES = 1;
final int targetType; final int targetType;
public StringCoercion(int targetType) { public StringCoercion(int targetType) {
this.targetType = targetType; this.targetType = targetType;
} }
public String toString() { public String toString() {
return "StringCoercion("+(targetType==TARGET_TYPE_STRING? "String": "byte[]")+")"; return "StringCoercion(" + (targetType == TARGET_TYPE_STRING? "String": "byte[]") + ")";
} }
public int score(LuaValue value) { public int score(LuaValue value) {
switch ( value.type() ) { switch (value.type()) {
case LuaValue.TSTRING: case LuaValue.TSTRING:
return value.checkstring().isValidUtf8()? return value.checkstring().isValidUtf8()? (targetType == TARGET_TYPE_STRING? 0: 1)
(targetType==TARGET_TYPE_STRING? 0: 1): : (targetType == TARGET_TYPE_BYTES? 0: SCORE_WRONG_TYPE);
(targetType==TARGET_TYPE_BYTES? 0: SCORE_WRONG_TYPE);
case LuaValue.TNIL: case LuaValue.TNIL:
return SCORE_NULL_VALUE; return SCORE_NULL_VALUE;
default: default:
return targetType == TARGET_TYPE_STRING? SCORE_WRONG_TYPE: SCORE_UNCOERCIBLE; return targetType == TARGET_TYPE_STRING? SCORE_WRONG_TYPE: SCORE_UNCOERCIBLE;
} }
} }
public Object coerce(LuaValue value) { public Object coerce(LuaValue value) {
if ( value.isnil() ) if (value.isnil())
return null; return null;
if ( targetType == TARGET_TYPE_STRING ) if (targetType == TARGET_TYPE_STRING)
return value.tojstring(); return value.tojstring();
LuaString s = value.checkstring(); LuaString s = value.checkstring();
byte[] b = new byte[s.m_length]; byte[] b = new byte[s.m_length];
@@ -221,33 +246,37 @@ public class CoerceLuaToJava {
} }
static final class ArrayCoercion implements Coercion { static final class ArrayCoercion implements Coercion {
final Class componentType; final Class componentType;
final Coercion componentCoercion; final Coercion componentCoercion;
public ArrayCoercion(Class componentType) { public ArrayCoercion(Class componentType) {
this.componentType = componentType; this.componentType = componentType;
this.componentCoercion = getCoercion(componentType); this.componentCoercion = getCoercion(componentType);
} }
public String toString() { public String toString() {
return "ArrayCoercion("+componentType.getName()+")"; return "ArrayCoercion(" + componentType.getName() + ")";
} }
public int score(LuaValue value) { public int score(LuaValue value) {
switch ( value.type() ) { switch (value.type()) {
case LuaValue.TTABLE: case LuaValue.TTABLE:
return value.length()==0? 0: componentCoercion.score( value.get(1) ); return value.length() == 0? 0: componentCoercion.score(value.get(1));
case LuaValue.TUSERDATA: case LuaValue.TUSERDATA:
return inheritanceLevels( componentType, value.touserdata().getClass().getComponentType() ); return inheritanceLevels(componentType, value.touserdata().getClass().getComponentType());
case LuaValue.TNIL: case LuaValue.TNIL:
return SCORE_NULL_VALUE; return SCORE_NULL_VALUE;
default: default:
return SCORE_UNCOERCIBLE; return SCORE_UNCOERCIBLE;
} }
} }
public Object coerce(LuaValue value) { public Object coerce(LuaValue value) {
switch ( value.type() ) { switch (value.type()) {
case LuaValue.TTABLE: { case LuaValue.TTABLE: {
int n = value.length(); int n = value.length();
Object a = Array.newInstance(componentType, n); Object a = Array.newInstance(componentType, n);
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
Array.set(a, i, componentCoercion.coerce(value.get(i+1))); Array.set(a, i, componentCoercion.coerce(value.get(i+1)));
return a; return a;
} }
@@ -255,60 +284,65 @@ public class CoerceLuaToJava {
return value.touserdata(); return value.touserdata();
case LuaValue.TNIL: case LuaValue.TNIL:
return null; return null;
default: default:
return null; return null;
} }
} }
} }
/** /**
* Determine levels of inheritance between a base class and a subclass * Determine levels of inheritance between a base class and a subclass
*
* @param baseclass base class to look for * @param baseclass base class to look for
* @param subclass class from which to start looking * @param subclass class from which to start looking
* @return number of inheritance levels between subclass and baseclass, * @return number of inheritance levels between subclass and baseclass, or
* or SCORE_UNCOERCIBLE if not a subclass * SCORE_UNCOERCIBLE if not a subclass
*/ */
static final int inheritanceLevels( Class baseclass, Class subclass ) { static final int inheritanceLevels(Class baseclass, Class subclass) {
if ( subclass == null ) if (subclass == null)
return SCORE_UNCOERCIBLE; return SCORE_UNCOERCIBLE;
if ( baseclass == subclass ) if (baseclass == subclass)
return 0; return 0;
int min = Math.min( SCORE_UNCOERCIBLE, inheritanceLevels( baseclass, subclass.getSuperclass() ) + 1 ); int min = Math.min(SCORE_UNCOERCIBLE, inheritanceLevels(baseclass, subclass.getSuperclass())+1);
Class[] ifaces = subclass.getInterfaces(); Class[] ifaces = subclass.getInterfaces();
for ( int i=0; i<ifaces.length; i++ ) for (int i = 0; i < ifaces.length; i++)
min = Math.min(min, inheritanceLevels(baseclass, ifaces[i]) + 1 ); min = Math.min(min, inheritanceLevels(baseclass, ifaces[i])+1);
return min; return min;
} }
static final class ObjectCoercion implements Coercion { static final class ObjectCoercion implements Coercion {
final Class targetType; final Class targetType;
ObjectCoercion(Class targetType) { ObjectCoercion(Class targetType) {
this.targetType = targetType; this.targetType = targetType;
} }
public String toString() { public String toString() {
return "ObjectCoercion("+targetType.getName()+")"; return "ObjectCoercion(" + targetType.getName() + ")";
} }
public int score(LuaValue value) { public int score(LuaValue value) {
switch ( value.type() ) { switch (value.type()) {
case LuaValue.TNUMBER: case LuaValue.TNUMBER:
return inheritanceLevels( targetType, value.isint()? Integer.class: Double.class ); return inheritanceLevels(targetType, value.isint()? Integer.class: Double.class);
case LuaValue.TBOOLEAN: case LuaValue.TBOOLEAN:
return inheritanceLevels( targetType, Boolean.class ); return inheritanceLevels(targetType, Boolean.class);
case LuaValue.TSTRING: case LuaValue.TSTRING:
return inheritanceLevels( targetType, String.class ); return inheritanceLevels(targetType, String.class);
case LuaValue.TUSERDATA: case LuaValue.TUSERDATA:
return inheritanceLevels( targetType, value.touserdata().getClass() ); return inheritanceLevels(targetType, value.touserdata().getClass());
case LuaValue.TNIL: case LuaValue.TNIL:
return SCORE_NULL_VALUE; return SCORE_NULL_VALUE;
default: default:
return inheritanceLevels( targetType, value.getClass() ); return inheritanceLevels(targetType, value.getClass());
} }
} }
public Object coerce(LuaValue value) { public Object coerce(LuaValue value) {
switch ( value.type() ) { switch (value.type()) {
case LuaValue.TNUMBER: case LuaValue.TNUMBER:
return value.isint()? (Object)new Integer(value.toint()): (Object)new Double(value.todouble()); return value.isint()? (Object) new Integer(value.toint()): (Object) new Double(value.todouble());
case LuaValue.TBOOLEAN: case LuaValue.TBOOLEAN:
return value.toboolean()? Boolean.TRUE: Boolean.FALSE; return value.toboolean()? Boolean.TRUE: Boolean.FALSE;
case LuaValue.TSTRING: case LuaValue.TSTRING:
@@ -324,49 +358,49 @@ public class CoerceLuaToJava {
} }
static { static {
Coercion boolCoercion = new BoolCoercion(); Coercion boolCoercion = new BoolCoercion();
Coercion byteCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_BYTE); Coercion byteCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_BYTE);
Coercion charCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_CHAR); Coercion charCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_CHAR);
Coercion shortCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_SHORT); Coercion shortCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_SHORT);
Coercion intCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_INT); Coercion intCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_INT);
Coercion longCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_LONG); Coercion longCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_LONG);
Coercion floatCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_FLOAT); Coercion floatCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_FLOAT);
Coercion doubleCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_DOUBLE); Coercion doubleCoercion = new NumericCoercion(NumericCoercion.TARGET_TYPE_DOUBLE);
Coercion stringCoercion = new StringCoercion(StringCoercion.TARGET_TYPE_STRING); Coercion stringCoercion = new StringCoercion(StringCoercion.TARGET_TYPE_STRING);
Coercion bytesCoercion = new StringCoercion(StringCoercion.TARGET_TYPE_BYTES); Coercion bytesCoercion = new StringCoercion(StringCoercion.TARGET_TYPE_BYTES);
COERCIONS.put( Boolean.TYPE, boolCoercion ); COERCIONS.put(Boolean.TYPE, boolCoercion);
COERCIONS.put( Boolean.class, boolCoercion ); COERCIONS.put(Boolean.class, boolCoercion);
COERCIONS.put( Byte.TYPE, byteCoercion ); COERCIONS.put(Byte.TYPE, byteCoercion);
COERCIONS.put( Byte.class, byteCoercion ); COERCIONS.put(Byte.class, byteCoercion);
COERCIONS.put( Character.TYPE, charCoercion ); COERCIONS.put(Character.TYPE, charCoercion);
COERCIONS.put( Character.class, charCoercion ); COERCIONS.put(Character.class, charCoercion);
COERCIONS.put( Short.TYPE, shortCoercion ); COERCIONS.put(Short.TYPE, shortCoercion);
COERCIONS.put( Short.class, shortCoercion ); COERCIONS.put(Short.class, shortCoercion);
COERCIONS.put( Integer.TYPE, intCoercion ); COERCIONS.put(Integer.TYPE, intCoercion);
COERCIONS.put( Integer.class, intCoercion ); COERCIONS.put(Integer.class, intCoercion);
COERCIONS.put( Long.TYPE, longCoercion ); COERCIONS.put(Long.TYPE, longCoercion);
COERCIONS.put( Long.class, longCoercion ); COERCIONS.put(Long.class, longCoercion);
COERCIONS.put( Float.TYPE, floatCoercion ); COERCIONS.put(Float.TYPE, floatCoercion);
COERCIONS.put( Float.class, floatCoercion ); COERCIONS.put(Float.class, floatCoercion);
COERCIONS.put( Double.TYPE, doubleCoercion ); COERCIONS.put(Double.TYPE, doubleCoercion);
COERCIONS.put( Double.class, doubleCoercion ); COERCIONS.put(Double.class, doubleCoercion);
COERCIONS.put( String.class, stringCoercion ); COERCIONS.put(String.class, stringCoercion);
COERCIONS.put( byte[].class, bytesCoercion ); COERCIONS.put(byte[].class, bytesCoercion);
} }
static Coercion getCoercion(Class c) { static Coercion getCoercion(Class c) {
Coercion co = (Coercion) COERCIONS.get( c ); Coercion co = (Coercion) COERCIONS.get(c);
if ( co != null ) { if (co != null) {
return co; return co;
} }
if ( c.isArray() ) { if (c.isArray()) {
Class typ = c.getComponentType(); Class typ = c.getComponentType();
co = new ArrayCoercion(c.getComponentType()); co = new ArrayCoercion(c.getComponentType());
} else { } else {
co = new ObjectCoercion(c); co = new ObjectCoercion(c);
} }
COERCIONS.put( c, co ); COERCIONS.put(c, co);
return co; return co;
} }
} }

View File

@@ -33,9 +33,9 @@ import org.luaj.vm2.lib.OneArgFunction;
* <p> * <p>
* Can get elements by their integer key index, as well as the length. * Can get elements by their integer key index, as well as the length.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to
* It is returned by calls to {@link CoerceJavaToLua#coerce(Object)} * {@link CoerceJavaToLua#coerce(Object)} when an array is supplied.
* when an array is supplied. *
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
*/ */
@@ -43,44 +43,43 @@ class JavaArray extends LuaUserdata {
private static final class LenFunction extends OneArgFunction { private static final class LenFunction extends OneArgFunction {
public LuaValue call(LuaValue u) { public LuaValue call(LuaValue u) {
return LuaValue.valueOf(Array.getLength(((LuaUserdata)u).m_instance)); return LuaValue.valueOf(Array.getLength(((LuaUserdata) u).m_instance));
} }
} }
static final LuaValue LENGTH = valueOf("length"); static final LuaValue LENGTH = valueOf("length");
static final LuaTable array_metatable; static final LuaTable array_metatable;
static { static {
array_metatable = new LuaTable(); array_metatable = new LuaTable();
array_metatable.rawset(LuaValue.LEN, new LenFunction()); array_metatable.rawset(LuaValue.LEN, new LenFunction());
} }
JavaArray(Object instance) { JavaArray(Object instance) {
super(instance); super(instance);
setmetatable(array_metatable); setmetatable(array_metatable);
} }
public LuaValue get(LuaValue key) { public LuaValue get(LuaValue key) {
if ( key.equals(LENGTH) ) if (key.equals(LENGTH))
return valueOf(Array.getLength(m_instance)); return valueOf(Array.getLength(m_instance));
if ( key.isint() ) { if (key.isint()) {
int i = key.toint() - 1; int i = key.toint()-1;
return i>=0 && i<Array.getLength(m_instance)? return i >= 0 && i < Array.getLength(m_instance)
CoerceJavaToLua.coerce(Array.get(m_instance,key.toint()-1)): ? CoerceJavaToLua.coerce(Array.get(m_instance, key.toint()-1))
NIL; : NIL;
} }
return super.get(key); return super.get(key);
} }
public void set(LuaValue key, LuaValue value) { public void set(LuaValue key, LuaValue value) {
if ( key.isint() ) { if (key.isint()) {
int i = key.toint() - 1; int i = key.toint()-1;
if ( i>=0 && i<Array.getLength(m_instance) ) if (i >= 0 && i < Array.getLength(m_instance))
Array.set(m_instance,i,CoerceLuaToJava.coerce(value, m_instance.getClass().getComponentType())); Array.set(m_instance, i, CoerceLuaToJava.coerce(value, m_instance.getClass().getComponentType()));
else if ( m_metatable==null || ! settable(this,key,value) ) else if (m_metatable == null || !settable(this, key, value))
error("array index out of bounds"); error("array index out of bounds");
} } else
else
super.set(key, value); super.set(key, value);
} }
} }

View File

@@ -38,11 +38,11 @@ import org.luaj.vm2.LuaValue;
/** /**
* LuaValue that represents a Java class. * LuaValue that represents a Java class.
* <p> * <p>
* Will respond to get() and set() by returning field values, or java methods. * Will respond to get() and set() by returning field values, or java methods.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to
* It is returned by calls to {@link CoerceJavaToLua#coerce(Object)} * {@link CoerceJavaToLua#coerce(Object)} when a Class is supplied.
* when a Class is supplied. *
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
*/ */
@@ -51,18 +51,18 @@ class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion {
static final Map classes = Collections.synchronizedMap(new HashMap()); static final Map classes = Collections.synchronizedMap(new HashMap());
static final LuaValue NEW = valueOf("new"); static final LuaValue NEW = valueOf("new");
Map fields; Map fields;
Map methods; Map methods;
Map innerclasses; Map innerclasses;
static JavaClass forClass(Class c) { static JavaClass forClass(Class c) {
JavaClass j = (JavaClass) classes.get(c); JavaClass j = (JavaClass) classes.get(c);
if ( j == null ) if (j == null)
classes.put( c, j = new JavaClass(c) ); classes.put(c, j = new JavaClass(c));
return j; return j;
} }
JavaClass(Class c) { JavaClass(Class c) {
super(c); super(c);
this.jclass = this; this.jclass = this;
@@ -71,14 +71,14 @@ class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion {
public LuaValue coerce(Object javaValue) { public LuaValue coerce(Object javaValue) {
return this; return this;
} }
Field getField(LuaValue key) { Field getField(LuaValue key) {
if ( fields == null ) { if (fields == null) {
Map m = new HashMap(); Map m = new HashMap();
Field[] f = ((Class)m_instance).getFields(); Field[] f = ((Class) m_instance).getFields();
for ( int i=0; i<f.length; i++ ) { for (int i = 0; i < f.length; i++) {
Field fi = f[i]; Field fi = f[i];
if ( Modifier.isPublic(fi.getModifiers()) ) { if (Modifier.isPublic(fi.getModifiers())) {
m.put(LuaValue.valueOf(fi.getName()), fi); m.put(LuaValue.valueOf(fi.getName()), fi);
try { try {
if (!fi.isAccessible()) if (!fi.isAccessible())
@@ -91,52 +91,56 @@ class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion {
} }
return (Field) fields.get(key); return (Field) fields.get(key);
} }
LuaValue getMethod(LuaValue key) { LuaValue getMethod(LuaValue key) {
if ( methods == null ) { if (methods == null) {
Map namedlists = new HashMap(); Map namedlists = new HashMap();
Method[] m = ((Class)m_instance).getMethods(); Method[] m = ((Class) m_instance).getMethods();
for ( int i=0; i<m.length; i++ ) { for (int i = 0; i < m.length; i++) {
Method mi = m[i]; Method mi = m[i];
if ( Modifier.isPublic( mi.getModifiers()) ) { if (Modifier.isPublic(mi.getModifiers())) {
String name = mi.getName(); String name = mi.getName();
List list = (List) namedlists.get(name); List list = (List) namedlists.get(name);
if ( list == null ) if (list == null)
namedlists.put(name, list = new ArrayList()); namedlists.put(name, list = new ArrayList());
list.add( JavaMethod.forMethod(mi) ); list.add(JavaMethod.forMethod(mi));
} }
} }
Map map = new HashMap(); Map map = new HashMap();
Constructor[] c = ((Class)m_instance).getConstructors(); Constructor[] c = ((Class) m_instance).getConstructors();
List list = new ArrayList(); List list = new ArrayList();
for ( int i=0; i<c.length; i++ ) for (int i = 0; i < c.length; i++)
if ( Modifier.isPublic(c[i].getModifiers()) ) if (Modifier.isPublic(c[i].getModifiers()))
list.add( JavaConstructor.forConstructor(c[i]) ); list.add(JavaConstructor.forConstructor(c[i]));
switch ( list.size() ) { switch (list.size()) {
case 0: break; case 0:
case 1: map.put(NEW, list.get(0)); break; break;
default: map.put(NEW, JavaConstructor.forConstructors( (JavaConstructor[])list.toArray(new JavaConstructor[list.size()]) ) ); break; case 1:
map.put(NEW, list.get(0));
break;
default:
map.put(NEW, JavaConstructor
.forConstructors((JavaConstructor[]) list.toArray(new JavaConstructor[list.size()])));
break;
} }
for ( Iterator it=namedlists.entrySet().iterator(); it.hasNext(); ) { for (Iterator it = namedlists.entrySet().iterator(); it.hasNext();) {
Entry e = (Entry) it.next(); Entry e = (Entry) it.next();
String name = (String) e.getKey(); String name = (String) e.getKey();
List methods = (List) e.getValue(); List methods = (List) e.getValue();
map.put( LuaValue.valueOf(name), map.put(LuaValue.valueOf(name), methods.size() == 1? methods.get(0)
methods.size()==1? : JavaMethod.forMethods((JavaMethod[]) methods.toArray(new JavaMethod[methods.size()])));
methods.get(0):
JavaMethod.forMethods( (JavaMethod[])methods.toArray(new JavaMethod[methods.size()])) );
} }
methods = map; methods = map;
} }
return (LuaValue) methods.get(key); return (LuaValue) methods.get(key);
} }
Class getInnerClass(LuaValue key) { Class getInnerClass(LuaValue key) {
if ( innerclasses == null ) { if (innerclasses == null) {
Map m = new HashMap(); Map m = new HashMap();
Class[] c = ((Class)m_instance).getClasses(); Class[] c = ((Class) m_instance).getClasses();
for ( int i=0; i<c.length; i++ ) { for (int i = 0; i < c.length; i++) {
Class ci = c[i]; Class ci = c[i];
String name = ci.getName(); String name = ci.getName();
String stub = name.substring(Math.max(name.lastIndexOf('$'), name.lastIndexOf('.'))+1); String stub = name.substring(Math.max(name.lastIndexOf('$'), name.lastIndexOf('.'))+1);
@@ -147,7 +151,5 @@ class JavaClass extends JavaInstance implements CoerceJavaToLua.Coercion {
return (Class) innerclasses.get(key); return (Class) innerclasses.get(key);
} }
public LuaValue getConstructor() { public LuaValue getConstructor() { return getMethod(NEW); }
return getMethod(NEW);
}
} }

View File

@@ -35,45 +35,45 @@ import org.luaj.vm2.lib.VarArgFunction;
/** /**
* LuaValue that represents a particular public Java constructor. * LuaValue that represents a particular public Java constructor.
* <p> * <p>
* May be called with arguments to return a JavaInstance * May be called with arguments to return a JavaInstance created by calling the
* created by calling the constructor. * constructor.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to
* It is returned by calls to {@link JavaClass#new(LuaValue key)} * {@link JavaClass#new(LuaValue key)} when the value of key is "new".
* when the value of key is "new". *
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
*/ */
class JavaConstructor extends JavaMember { class JavaConstructor extends JavaMember {
static final Map constructors = Collections.synchronizedMap(new HashMap()); static final Map constructors = Collections.synchronizedMap(new HashMap());
static JavaConstructor forConstructor(Constructor c) { static JavaConstructor forConstructor(Constructor c) {
JavaConstructor j = (JavaConstructor) constructors.get(c); JavaConstructor j = (JavaConstructor) constructors.get(c);
if ( j == null ) if (j == null)
constructors.put( c, j = new JavaConstructor(c) ); constructors.put(c, j = new JavaConstructor(c));
return j; return j;
} }
public static LuaValue forConstructors(JavaConstructor[] array) { public static LuaValue forConstructors(JavaConstructor[] array) {
return new Overload(array); return new Overload(array);
} }
final Constructor constructor; final Constructor constructor;
private JavaConstructor(Constructor c) { private JavaConstructor(Constructor c) {
super( c.getParameterTypes(), c.getModifiers() ); super(c.getParameterTypes(), c.getModifiers());
this.constructor = c; this.constructor = c;
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
Object[] a = convertArgs(args); Object[] a = convertArgs(args);
try { try {
return CoerceJavaToLua.coerce( constructor.newInstance(a) ); return CoerceJavaToLua.coerce(constructor.newInstance(a));
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw new LuaError(e.getTargetException()); throw new LuaError(e.getTargetException());
} catch (Exception e) { } catch (Exception e) {
return LuaValue.error("coercion error "+e); return LuaValue.error("coercion error " + e);
} }
} }
@@ -82,12 +82,13 @@ class JavaConstructor extends JavaMember {
* <p> * <p>
* On invocation, will pick the best method from the list, and invoke it. * On invocation, will pick the best method from the list, and invoke it.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to calls to
* It is returned by calls to calls to {@link JavaClass#get(LuaValue key)} * {@link JavaClass#get(LuaValue key)} when key is "new" and there is more
* when key is "new" and there is more than one public constructor. * than one public constructor.
*/ */
static class Overload extends VarArgFunction { static class Overload extends VarArgFunction {
final JavaConstructor[] constructors; final JavaConstructor[] constructors;
public Overload(JavaConstructor[] c) { public Overload(JavaConstructor[] c) {
this.constructors = c; this.constructors = c;
} }
@@ -95,20 +96,20 @@ class JavaConstructor extends JavaMember {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
JavaConstructor best = null; JavaConstructor best = null;
int score = CoerceLuaToJava.SCORE_UNCOERCIBLE; int score = CoerceLuaToJava.SCORE_UNCOERCIBLE;
for ( int i=0; i<constructors.length; i++ ) { for (int i = 0; i < constructors.length; i++) {
int s = constructors[i].score(args); int s = constructors[i].score(args);
if ( s < score ) { if (s < score) {
score = s; score = s;
best = constructors[i]; best = constructors[i];
if ( score == 0 ) if (score == 0)
break; break;
} }
} }
// any match? // any match?
if ( best == null ) if (best == null)
LuaValue.error("no coercible public method"); LuaValue.error("no coercible public method");
// invoke it // invoke it
return best.invoke(args); return best.invoke(args);
} }

View File

@@ -30,46 +30,46 @@ import org.luaj.vm2.LuaValue;
/** /**
* LuaValue that represents a Java instance. * LuaValue that represents a Java instance.
* <p> * <p>
* Will respond to get() and set() by returning field values or methods. * Will respond to get() and set() by returning field values or methods.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to
* It is returned by calls to {@link CoerceJavaToLua#coerce(Object)} * {@link CoerceJavaToLua#coerce(Object)} when a subclass of Object is supplied.
* when a subclass of Object is supplied. *
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
*/ */
class JavaInstance extends LuaUserdata { class JavaInstance extends LuaUserdata {
JavaClass jclass; JavaClass jclass;
JavaInstance(Object instance) { JavaInstance(Object instance) {
super(instance); super(instance);
} }
public LuaValue get(LuaValue key) { public LuaValue get(LuaValue key) {
if ( jclass == null ) if (jclass == null)
jclass = JavaClass.forClass(m_instance.getClass()); jclass = JavaClass.forClass(m_instance.getClass());
Field f = jclass.getField(key); Field f = jclass.getField(key);
if ( f != null ) if (f != null)
try { try {
return CoerceJavaToLua.coerce(f.get(m_instance)); return CoerceJavaToLua.coerce(f.get(m_instance));
} catch (Exception e) { } catch (Exception e) {
throw new LuaError(e); throw new LuaError(e);
} }
LuaValue m = jclass.getMethod(key); LuaValue m = jclass.getMethod(key);
if ( m != null ) if (m != null)
return m; return m;
Class c = jclass.getInnerClass(key); Class c = jclass.getInnerClass(key);
if ( c != null ) if (c != null)
return JavaClass.forClass(c); return JavaClass.forClass(c);
return super.get(key); return super.get(key);
} }
public void set(LuaValue key, LuaValue value) { public void set(LuaValue key, LuaValue value) {
if ( jclass == null ) if (jclass == null)
jclass = JavaClass.forClass(m_instance.getClass()); jclass = JavaClass.forClass(m_instance.getClass());
Field f = jclass.getField(key); Field f = jclass.getField(key);
if ( f != null ) if (f != null)
try { try {
f.set(m_instance, CoerceLuaToJava.coerce(value, f.getType())); f.set(m_instance, CoerceLuaToJava.coerce(value, f.getType()));
return; return;
@@ -77,6 +77,6 @@ class JavaInstance extends LuaUserdata {
throw new LuaError(e); throw new LuaError(e);
} }
super.set(key, value); super.set(key, value);
} }
} }

View File

@@ -28,56 +28,56 @@ import org.luaj.vm2.lib.jse.CoerceLuaToJava.Coercion;
/** /**
* Java method or constructor. * Java method or constructor.
* <p> * <p>
* Primarily handles argument coercion for parameter lists including scoring of compatibility and * Primarily handles argument coercion for parameter lists including scoring of
* java varargs handling. * compatibility and java varargs handling.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is an abstract base class for
* It is an abstract base class for {@link JavaConstructor} and {@link JavaMethod}. * {@link JavaConstructor} and {@link JavaMethod}.
*
* @see JavaConstructor * @see JavaConstructor
* @see JavaMethod * @see JavaMethod
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
*/ */
abstract abstract class JavaMember extends VarArgFunction {
class JavaMember extends VarArgFunction {
static final int METHOD_MODIFIERS_VARARGS = 0x80; static final int METHOD_MODIFIERS_VARARGS = 0x80;
final Coercion[] fixedargs; final Coercion[] fixedargs;
final Coercion varargs; final Coercion varargs;
protected JavaMember(Class[] params, int modifiers) { protected JavaMember(Class[] params, int modifiers) {
boolean isvarargs = ((modifiers & METHOD_MODIFIERS_VARARGS) != 0); boolean isvarargs = ((modifiers & METHOD_MODIFIERS_VARARGS) != 0);
fixedargs = new CoerceLuaToJava.Coercion[isvarargs? params.length-1: params.length]; fixedargs = new CoerceLuaToJava.Coercion[isvarargs? params.length-1: params.length];
for ( int i=0; i<fixedargs.length; i++ ) for (int i = 0; i < fixedargs.length; i++)
fixedargs[i] = CoerceLuaToJava.getCoercion( params[i] ); fixedargs[i] = CoerceLuaToJava.getCoercion(params[i]);
varargs = isvarargs? CoerceLuaToJava.getCoercion( params[params.length-1] ): null; varargs = isvarargs? CoerceLuaToJava.getCoercion(params[params.length-1]): null;
} }
int score(Varargs args) { int score(Varargs args) {
int n = args.narg(); int n = args.narg();
int s = n>fixedargs.length? CoerceLuaToJava.SCORE_WRONG_TYPE * (n-fixedargs.length): 0; int s = n > fixedargs.length? CoerceLuaToJava.SCORE_WRONG_TYPE*(n-fixedargs.length): 0;
for ( int j=0; j<fixedargs.length; j++ ) for (int j = 0; j < fixedargs.length; j++)
s += fixedargs[j].score( args.arg(j+1) ); s += fixedargs[j].score(args.arg(j+1));
if ( varargs != null ) if (varargs != null)
for ( int k=fixedargs.length; k<n; k++ ) for (int k = fixedargs.length; k < n; k++)
s += varargs.score( args.arg(k+1) ); s += varargs.score(args.arg(k+1));
return s; return s;
} }
protected Object[] convertArgs(Varargs args) { protected Object[] convertArgs(Varargs args) {
Object[] a; Object[] a;
if ( varargs == null ) { if (varargs == null) {
a = new Object[fixedargs.length]; a = new Object[fixedargs.length];
for ( int i=0; i<a.length; i++ ) for (int i = 0; i < a.length; i++)
a[i] = fixedargs[i].coerce( args.arg(i+1) ); a[i] = fixedargs[i].coerce(args.arg(i+1));
} else { } else {
int n = Math.max(fixedargs.length,args.narg()); int n = Math.max(fixedargs.length, args.narg());
a = new Object[n]; a = new Object[n];
for ( int i=0; i<fixedargs.length; i++ ) for (int i = 0; i < fixedargs.length; i++)
a[i] = fixedargs[i].coerce( args.arg(i+1) ); a[i] = fixedargs[i].coerce(args.arg(i+1));
for ( int i=fixedargs.length; i<n; i++ ) for (int i = fixedargs.length; i < n; i++)
a[i] = varargs.coerce( args.arg(i+1) ); a[i] = varargs.coerce(args.arg(i+1));
} }
return a; return a;
} }

View File

@@ -35,33 +35,33 @@ import org.luaj.vm2.Varargs;
/** /**
* LuaValue that represents a Java method. * LuaValue that represents a Java method.
* <p> * <p>
* Can be invoked via call(LuaValue...) and related methods. * Can be invoked via call(LuaValue...) and related methods.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to calls to
* It is returned by calls to calls to {@link JavaInstance#get(LuaValue key)} * {@link JavaInstance#get(LuaValue key)} when a method is named.
* when a method is named. *
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
*/ */
class JavaMethod extends JavaMember { class JavaMethod extends JavaMember {
static final Map methods = Collections.synchronizedMap(new HashMap()); static final Map methods = Collections.synchronizedMap(new HashMap());
static JavaMethod forMethod(Method m) { static JavaMethod forMethod(Method m) {
JavaMethod j = (JavaMethod) methods.get(m); JavaMethod j = (JavaMethod) methods.get(m);
if ( j == null ) if (j == null)
methods.put( m, j = new JavaMethod(m) ); methods.put(m, j = new JavaMethod(m));
return j; return j;
} }
static LuaFunction forMethods(JavaMethod[] m) { static LuaFunction forMethods(JavaMethod[] m) {
return new Overload(m); return new Overload(m);
} }
final Method method; final Method method;
private JavaMethod(Method m) { private JavaMethod(Method m) {
super( m.getParameterTypes(), m.getModifiers() ); super(m.getParameterTypes(), m.getModifiers());
this.method = m; this.method = m;
try { try {
if (!m.isAccessible()) if (!m.isAccessible())
@@ -81,39 +81,39 @@ class JavaMethod extends JavaMember {
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return invokeMethod(arg1.checkuserdata(), arg2); return invokeMethod(arg1.checkuserdata(), arg2);
} }
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return invokeMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3)); return invokeMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3));
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return invokeMethod(args.checkuserdata(1), args.subargs(2)); return invokeMethod(args.checkuserdata(1), args.subargs(2));
} }
LuaValue invokeMethod(Object instance, Varargs args) { LuaValue invokeMethod(Object instance, Varargs args) {
Object[] a = convertArgs(args); Object[] a = convertArgs(args);
try { try {
return CoerceJavaToLua.coerce( method.invoke(instance, a) ); return CoerceJavaToLua.coerce(method.invoke(instance, a));
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw new LuaError(e.getTargetException()); throw new LuaError(e.getTargetException());
} catch (Exception e) { } catch (Exception e) {
return LuaValue.error("coercion error "+e); return LuaValue.error("coercion error " + e);
} }
} }
/** /**
* LuaValue that represents an overloaded Java method. * LuaValue that represents an overloaded Java method.
* <p> * <p>
* On invocation, will pick the best method from the list, and invoke it. * On invocation, will pick the best method from the list, and invoke it.
* <p> * <p>
* This class is not used directly. * This class is not used directly. It is returned by calls to calls to
* It is returned by calls to calls to {@link JavaInstance#get(LuaValue key)} * {@link JavaInstance#get(LuaValue key)} when an overloaded method is
* when an overloaded method is named. * named.
*/ */
static class Overload extends LuaFunction { static class Overload extends LuaFunction {
final JavaMethod[] methods; final JavaMethod[] methods;
Overload(JavaMethod[] methods) { Overload(JavaMethod[] methods) {
this.methods = methods; this.methods = methods;
} }
@@ -129,11 +129,11 @@ class JavaMethod extends JavaMember {
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return invokeBestMethod(arg1.checkuserdata(), arg2); return invokeBestMethod(arg1.checkuserdata(), arg2);
} }
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return invokeBestMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3)); return invokeBestMethod(arg1.checkuserdata(), LuaValue.varargsOf(arg2, arg3));
} }
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return invokeBestMethod(args.checkuserdata(1), args.subargs(2)); return invokeBestMethod(args.checkuserdata(1), args.subargs(2));
} }
@@ -141,20 +141,20 @@ class JavaMethod extends JavaMember {
private LuaValue invokeBestMethod(Object instance, Varargs args) { private LuaValue invokeBestMethod(Object instance, Varargs args) {
JavaMethod best = null; JavaMethod best = null;
int score = CoerceLuaToJava.SCORE_UNCOERCIBLE; int score = CoerceLuaToJava.SCORE_UNCOERCIBLE;
for ( int i=0; i<methods.length; i++ ) { for (int i = 0; i < methods.length; i++) {
int s = methods[i].score(args); int s = methods[i].score(args);
if ( s < score ) { if (s < score) {
score = s; score = s;
best = methods[i]; best = methods[i];
if ( score == 0 ) if (score == 0)
break; break;
} }
} }
// any match? // any match?
if ( best == null ) if (best == null)
LuaValue.error("no coercible public method"); LuaValue.error("no coercible public method");
// invoke it // invoke it
return best.invokeMethod(instance, args); return best.invokeMethod(instance, args);
} }

View File

@@ -33,34 +33,47 @@ import org.luaj.vm2.lib.BaseLib;
import org.luaj.vm2.lib.LibFunction; import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.ResourceFinder; import org.luaj.vm2.lib.ResourceFinder;
/** /**
* Subclass of {@link BaseLib} and {@link LibFunction} which implements the lua basic library functions * Subclass of {@link BaseLib} and {@link LibFunction} which implements the lua
* and provides a directory based {@link ResourceFinder} as the {@link Globals#finder}. * basic library functions and provides a directory based {@link ResourceFinder}
* as the {@link Globals#finder}.
* <p> * <p>
* Since JME has no file system by default, {@link BaseLib} implements * Since JME has no file system by default, {@link BaseLib} implements
* {@link ResourceFinder} using {@link Class#getResource(String)}. * {@link ResourceFinder} using {@link Class#getResource(String)}. The
* The {@link org.luaj.vm2.lib.jse.JseBaseLib} implements {@link Globals#finder} by scanning the current directory * {@link org.luaj.vm2.lib.jse.JseBaseLib} implements {@link Globals#finder} by
* first, then falling back to {@link Class#getResource(String)} if that fails. * scanning the current directory first, then falling back to
* Otherwise, the behavior is the same as that of {@link BaseLib}. * {@link Class#getResource(String)} if that fails. Otherwise, the behavior is
* <p> * the same as that of {@link BaseLib}.
* Typically, this library is included as part of a call to * <p>
* Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* globals.get("print").call(LuaValue.valueOf("hello, world")); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired, a minimal
* a minimal set of libraries could be loaded * set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* directly via {@link Globals#load(LuaValue)} using code such as: * using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.get("print").call(LuaValue.valueOf("hello, world")); * &#64;code
* } </pre> * Globals globals = new Globals();
* <p>However, other libraries such as <em>PackageLib</em> are not loaded in this case. * globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p>
* However, other libraries such as <em>PackageLib</em> are not loaded in this
* case.
* <p> * <p>
* This is a direct port of the corresponding library in C. * This is a direct port of the corresponding library in C.
*
* @see Globals * @see Globals
* @see BaseLib * @see BaseLib
* @see ResourceFinder * @see ResourceFinder
@@ -68,18 +81,24 @@ import org.luaj.vm2.lib.ResourceFinder;
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.1">Lua 5.2 Base Lib
* Reference</a>
*/ */
public class JseBaseLib extends org.luaj.vm2.lib.BaseLib { public class JseBaseLib extends org.luaj.vm2.lib.BaseLib {
/**
/** Perform one-time initialization on the library by creating a table * Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment, * containing the library functions, adding that table to the supplied
* adding the table to package.loaded, and returning table as the return value. * environment, adding the table to package.loaded, and returning table as
* <P>Specifically, extend the library loading to set the default value for {@link Globals#STDIN} * the return value.
* <P>
* Specifically, extend the library loading to set the default value for
* {@link Globals#STDIN}
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance. * @param env the environment to load into, which must be a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
super.call(modname, env); super.call(modname, env);
@@ -87,30 +106,28 @@ public class JseBaseLib extends org.luaj.vm2.lib.BaseLib {
return env; return env;
} }
/**
/** * Try to open a file in the current working directory, or fall back to base
* Try to open a file in the current working directory, * opener if not found.
* or fall back to base opener if not found.
* *
* This implementation attempts to open the file using new File(filename). * This implementation attempts to open the file using new File(filename).
* It falls back to the base implementation that looks it up as a resource * It falls back to the base implementation that looks it up as a resource
* in the class path if not found as a plain file. * in the class path if not found as a plain file.
* *
* @see org.luaj.vm2.lib.BaseLib * @see org.luaj.vm2.lib.BaseLib
* @see org.luaj.vm2.lib.ResourceFinder * @see org.luaj.vm2.lib.ResourceFinder
* *
* @param filename * @param filename
* @return InputStream, or null if not found. * @return InputStream, or null if not found.
*/ */
public InputStream findResource(String filename) { public InputStream findResource(String filename) {
File f = new File(filename); File f = new File(filename);
if ( ! f.exists() ) if (!f.exists())
return super.findResource(filename); return super.findResource(filename);
try { try {
return new BufferedInputStream(new FileInputStream(f)); return new BufferedInputStream(new FileInputStream(f));
} catch ( IOException ioe ) { } catch (IOException ioe) {
return null; return null;
} }
} }
} }

View File

@@ -37,135 +37,157 @@ import org.luaj.vm2.lib.IoLib;
import org.luaj.vm2.lib.LibFunction; import org.luaj.vm2.lib.LibFunction;
/** /**
* Subclass of {@link IoLib} and therefore {@link LibFunction} which implements the lua standard {@code io} * Subclass of {@link IoLib} and therefore {@link LibFunction} which implements
* library for the JSE platform. * the lua standard {@code io} library for the JSE platform.
* <p> * <p>
* It uses RandomAccessFile to implement seek on files. * It uses RandomAccessFile to implement seek on files.
* <p> * <p>
* Typically, this library is included as part of a call to * Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired, a minimal
* a minimal set of libraries could be loaded * set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* directly via {@link Globals#load(LuaValue)} using code such as: * using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new JseIoLib()); * Globals globals = new Globals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* <p>However, other libraries such as <em>MathLib</em> are not loaded in this case. * globals.load(new JseIoLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* }
* </pre>
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * However, other libraries such as <em>MathLib</em> are not loaded in this
* case.
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see IoLib * @see IoLib
* @see org.luaj.vm2.lib.jme.JmeIoLib * @see org.luaj.vm2.lib.jme.JmeIoLib
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.8">Lua 5.2 I/O Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.8">Lua 5.2 I/O Lib
* Reference</a>
*/ */
public class JseIoLib extends IoLib { public class JseIoLib extends IoLib {
protected File wrapStdin() throws IOException { protected File wrapStdin() throws IOException {
return new StdinFile(); return new StdinFile();
} }
protected File wrapStdout() throws IOException { protected File wrapStdout() throws IOException {
return new StdoutFile(FTYPE_STDOUT); return new StdoutFile(FTYPE_STDOUT);
} }
protected File wrapStderr() throws IOException { protected File wrapStderr() throws IOException {
return new StdoutFile(FTYPE_STDERR); return new StdoutFile(FTYPE_STDERR);
} }
protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException { protected File openFile(String filename, boolean readMode, boolean appendMode, boolean updateMode,
RandomAccessFile f = new RandomAccessFile(filename,readMode? "r": "rw"); boolean binaryMode) throws IOException {
if ( appendMode ) { RandomAccessFile f = new RandomAccessFile(filename, readMode? "r": "rw");
if (appendMode) {
f.seek(f.length()); f.seek(f.length());
} else { } else {
if ( ! readMode ) if (!readMode)
f.setLength(0); f.setLength(0);
} }
return new FileImpl( f ); return new FileImpl(f);
} }
protected File openProgram(String prog, String mode) throws IOException { protected File openProgram(String prog, String mode) throws IOException {
final Process p = Runtime.getRuntime().exec(prog); final Process p = Runtime.getRuntime().exec(prog);
return "w".equals(mode)? return "w".equals(mode)? new FileImpl(p.getOutputStream()): new FileImpl(p.getInputStream());
new FileImpl( p.getOutputStream() ):
new FileImpl( p.getInputStream() );
} }
protected File tmpFile() throws IOException { protected File tmpFile() throws IOException {
java.io.File f = java.io.File.createTempFile(".luaj","bin"); java.io.File f = java.io.File.createTempFile(".luaj", "bin");
f.deleteOnExit(); f.deleteOnExit();
return new FileImpl( new RandomAccessFile(f,"rw") ); return new FileImpl(new RandomAccessFile(f, "rw"));
} }
private static void notimplemented() { private static void notimplemented() {
throw new LuaError("not implemented"); throw new LuaError("not implemented");
} }
private final class FileImpl extends File { private final class FileImpl extends File {
private final RandomAccessFile file; private final RandomAccessFile file;
private final InputStream is; private final InputStream is;
private final OutputStream os; private final OutputStream os;
private boolean closed = false; private boolean closed = false;
private boolean nobuffer = false; private boolean nobuffer = false;
private FileImpl( RandomAccessFile file, InputStream is, OutputStream os ) {
private FileImpl(RandomAccessFile file, InputStream is, OutputStream os) {
this.file = file; this.file = file;
this.is = is!=null? is.markSupported()? is: new BufferedInputStream(is): null; this.is = is != null? is.markSupported()? is: new BufferedInputStream(is): null;
this.os = os; this.os = os;
} }
private FileImpl( RandomAccessFile f ) {
this( f, null, null ); private FileImpl(RandomAccessFile f) {
this(f, null, null);
} }
private FileImpl( InputStream i ) {
this( null, i, null ); private FileImpl(InputStream i) {
this(null, i, null);
} }
private FileImpl( OutputStream o ) {
this( null, null, o ); private FileImpl(OutputStream o) {
this(null, null, o);
} }
public String tojstring() { public String tojstring() {
return "file (" + (this.closed ? "closed" : String.valueOf(this.hashCode())) + ")"; return "file (" + (this.closed? "closed": String.valueOf(this.hashCode())) + ")";
} }
public boolean isstdfile() { public boolean isstdfile() {
return file == null; return file == null;
} }
public void close() throws IOException {
public void close() throws IOException {
closed = true; closed = true;
if ( file != null ) { if (file != null) {
file.close(); file.close();
} }
} }
public void flush() throws IOException { public void flush() throws IOException {
if ( os != null ) if (os != null)
os.flush(); os.flush();
} }
public void write(LuaString s) throws IOException { public void write(LuaString s) throws IOException {
if ( os != null ) if (os != null)
os.write( s.m_bytes, s.m_offset, s.m_length ); os.write(s.m_bytes, s.m_offset, s.m_length);
else if ( file != null ) else if (file != null)
file.write( s.m_bytes, s.m_offset, s.m_length ); file.write(s.m_bytes, s.m_offset, s.m_length);
else else
notimplemented(); notimplemented();
if ( nobuffer ) if (nobuffer)
flush(); flush();
} }
public boolean isclosed() { public boolean isclosed() {
return closed; return closed;
} }
public int seek(String option, int pos) throws IOException { public int seek(String option, int pos) throws IOException {
if ( file != null ) { if (file != null) {
if ( "set".equals(option) ) { if ("set".equals(option)) {
file.seek(pos); file.seek(pos);
} else if ( "end".equals(option) ) { } else if ("end".equals(option)) {
file.seek(file.length()+pos); file.seek(file.length()+pos);
} else { } else {
file.seek(file.getFilePointer()+pos); file.seek(file.getFilePointer()+pos);
@@ -175,23 +197,24 @@ public class JseIoLib extends IoLib {
notimplemented(); notimplemented();
return 0; return 0;
} }
public void setvbuf(String mode, int size) { public void setvbuf(String mode, int size) {
nobuffer = "no".equals(mode); nobuffer = "no".equals(mode);
} }
// get length remaining to read // get length remaining to read
public int remaining() throws IOException { public int remaining() throws IOException {
return file!=null? (int) (file.length()-file.getFilePointer()): -1; return file != null? (int) (file.length()-file.getFilePointer()): -1;
} }
// peek ahead one character // peek ahead one character
public int peek() throws IOException { public int peek() throws IOException {
if ( is != null ) { if (is != null) {
is.mark(1); is.mark(1);
int c = is.read(); int c = is.read();
is.reset(); is.reset();
return c; return c;
} else if ( file != null ) { } else if (file != null) {
long fp = file.getFilePointer(); long fp = file.getFilePointer();
int c = file.read(); int c = file.read();
file.seek(fp); file.seek(fp);
@@ -200,12 +223,12 @@ public class JseIoLib extends IoLib {
notimplemented(); notimplemented();
return 0; return 0;
} }
// return char if read, -1 if eof, throw IOException on other exception // return char if read, -1 if eof, throw IOException on other exception
public int read() throws IOException { public int read() throws IOException {
if ( is != null ) if (is != null)
return is.read(); return is.read();
else if ( file != null ) { else if (file != null) {
return file.read(); return file.read();
} }
notimplemented(); notimplemented();
@@ -214,9 +237,9 @@ public class JseIoLib extends IoLib {
// return number of bytes read if positive, -1 if eof, throws IOException // return number of bytes read if positive, -1 if eof, throws IOException
public int read(byte[] bytes, int offset, int length) throws IOException { public int read(byte[] bytes, int offset, int length) throws IOException {
if (file!=null) { if (file != null) {
return file.read(bytes, offset, length); return file.read(bytes, offset, length);
} else if (is!=null) { } else if (is != null) {
return is.read(bytes, offset, length); return is.read(bytes, offset, length);
} else { } else {
notimplemented(); notimplemented();
@@ -233,14 +256,10 @@ public class JseIoLib extends IoLib {
} }
public String tojstring() { public String tojstring() {
return "file ("+this.hashCode()+")"; return "file (" + this.hashCode() + ")";
} }
private final PrintStream getPrintStream() { private final PrintStream getPrintStream() { return file_type == FTYPE_STDERR? globals.STDERR: globals.STDOUT; }
return file_type == FTYPE_STDERR?
globals.STDERR:
globals.STDOUT;
}
public void write(LuaString string) throws IOException { public void write(LuaString string) throws IOException {
getPrintStream().write(string.m_bytes, string.m_offset, string.m_length); getPrintStream().write(string.m_bytes, string.m_offset, string.m_length);
@@ -281,8 +300,7 @@ public class JseIoLib extends IoLib {
return 0; return 0;
} }
public int read(byte[] bytes, int offset, int length) public int read(byte[] bytes, int offset, int length) throws IOException {
throws IOException {
return 0; return 0;
} }
} }
@@ -292,7 +310,7 @@ public class JseIoLib extends IoLib {
} }
public String tojstring() { public String tojstring() {
return "file ("+this.hashCode()+")"; return "file (" + this.hashCode() + ")";
} }
public void write(LuaString string) throws IOException { public void write(LuaString string) throws IOException {
@@ -335,8 +353,7 @@ public class JseIoLib extends IoLib {
return globals.STDIN.read(); return globals.STDIN.read();
} }
public int read(byte[] bytes, int offset, int length) public int read(byte[] bytes, int offset, int length) throws IOException {
throws IOException {
return globals.STDIN.read(bytes, offset, length); return globals.STDIN.read(bytes, offset, length);
} }
} }

View File

@@ -26,58 +26,76 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.LibFunction; import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.TwoArgFunction; import org.luaj.vm2.lib.TwoArgFunction;
/** /**
* Subclass of {@link LibFunction} which implements the lua standard {@code math} * Subclass of {@link LibFunction} which implements the lua standard
* library. * {@code math} library.
* <p>
* It contains all lua math functions, including those not available on the JME platform.
* See {@link org.luaj.vm2.lib.MathLib} for the exception list.
* <p> * <p>
* Typically, this library is included as part of a call to * It contains all lua math functions, including those not available on the JME
* platform. See {@link org.luaj.vm2.lib.MathLib} for the exception list.
* <p>
* Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired, a minimal
* a minimal set of libraries could be loaded * set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* directly via {@link Globals#load(LuaValue)} using code such as: * using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new JseMathLib()); * Globals globals = new Globals();
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* <p>However, other libraries such as <em>CoroutineLib</em> are not loaded in this case. * globals.load(new JseMathLib());
* System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* }
* </pre>
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * However, other libraries such as <em>CoroutineLib</em> are not loaded in this
* case.
* <p>
* This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
*
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see org.luaj.vm2.lib.jse.JseMathLib * @see org.luaj.vm2.lib.jse.JseMathLib
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.6">Lua 5.2 Math Lib
* Reference</a>
*/ */
public class JseMathLib extends org.luaj.vm2.lib.MathLib { public class JseMathLib extends org.luaj.vm2.lib.MathLib {
public JseMathLib() {} public JseMathLib() {}
/**
/** Perform one-time initialization on the library by creating a table * Perform one-time initialization on the library by creating a table
* containing the library functions, adding that table to the supplied environment, * containing the library functions, adding that table to the supplied
* adding the table to package.loaded, and returning table as the return value. * environment, adding the table to package.loaded, and returning table as
* <P>Specifically, adds all library functions that can be implemented directly * the return value.
* in JSE but not JME: acos, asin, atan, atan2, cosh, exp, log, pow, sinh, and tanh. * <P>
* Specifically, adds all library functions that can be implemented directly
* in JSE but not JME: acos, asin, atan, atan2, cosh, exp, log, pow, sinh,
* and tanh.
*
* @param modname the module name supplied if this is loaded via 'require'. * @param modname the module name supplied if this is loaded via 'require'.
* @param env the environment to load into, which must be a Globals instance. * @param env the environment to load into, which must be a Globals
* instance.
*/ */
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
super.call(modname, env); super.call(modname, env);
LuaValue math = env.get("math"); LuaValue math = env.get("math");
math.set("acos", new acos()); math.set("acos", new acos());
math.set("asin", new asin()); math.set("asin", new asin());
LuaValue atan = new atan2(); LuaValue atan = new atan2();
math.set("atan", atan); math.set("atan", atan);
math.set("atan2", atan); math.set("atan2", atan);
math.set("cosh", new cosh()); math.set("cosh", new cosh());
@@ -89,32 +107,53 @@ public class JseMathLib extends org.luaj.vm2.lib.MathLib {
return math; return math;
} }
static final class acos extends UnaryOp { protected double call(double d) { return Math.acos(d); } } static final class acos extends UnaryOp {
static final class asin extends UnaryOp { protected double call(double d) { return Math.asin(d); } } protected double call(double d) { return Math.acos(d); }
static final class atan2 extends TwoArgFunction { }
static final class asin extends UnaryOp {
protected double call(double d) { return Math.asin(d); }
}
static final class atan2 extends TwoArgFunction {
public LuaValue call(LuaValue x, LuaValue y) { public LuaValue call(LuaValue x, LuaValue y) {
return valueOf(Math.atan2(x.checkdouble(), y.optdouble(1))); return valueOf(Math.atan2(x.checkdouble(), y.optdouble(1)));
} }
} }
static final class cosh extends UnaryOp { protected double call(double d) { return Math.cosh(d); } }
static final class exp extends UnaryOp { protected double call(double d) { return Math.exp(d); } } static final class cosh extends UnaryOp {
protected double call(double d) { return Math.cosh(d); }
}
static final class exp extends UnaryOp {
protected double call(double d) { return Math.exp(d); }
}
static final class log extends TwoArgFunction { static final class log extends TwoArgFunction {
public LuaValue call(LuaValue x, LuaValue base) { public LuaValue call(LuaValue x, LuaValue base) {
double nat = Math.log(x.checkdouble()); double nat = Math.log(x.checkdouble());
double b = base.optdouble(Math.E); double b = base.optdouble(Math.E);
if (b != Math.E) nat /= Math.log(b); if (b != Math.E)
nat /= Math.log(b);
return valueOf(nat); return valueOf(nat);
} }
} }
static final class pow extends BinaryOp { protected double call(double x, double y) { return Math.pow(x, y); } }
static final class sinh extends UnaryOp { protected double call(double d) { return Math.sinh(d); } } static final class pow extends BinaryOp {
static final class tanh extends UnaryOp { protected double call(double d) { return Math.tanh(d); } } protected double call(double x, double y) { return Math.pow(x, y); }
}
static final class sinh extends UnaryOp {
protected double call(double d) { return Math.sinh(d); }
}
static final class tanh extends UnaryOp {
protected double call(double d) { return Math.tanh(d); }
}
/** Faster, better version of pow() used by arithmetic operator ^ */ /** Faster, better version of pow() used by arithmetic operator ^ */
public double dpow_lib(double a, double b) { public double dpow_lib(double a, double b) {
return Math.pow(a, b); return Math.pow(a, b);
} }
}
}

View File

@@ -31,10 +31,11 @@ import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.OsLib; import org.luaj.vm2.lib.OsLib;
/** /**
* Subclass of {@link LibFunction} which implements the standard lua {@code os} library. * Subclass of {@link LibFunction} which implements the standard lua {@code os}
* library.
* <p> * <p>
* This contains more complete implementations of the following functions * This contains more complete implementations of the following functions using
* using features that are specific to JSE: * features that are specific to JSE:
* <ul> * <ul>
* <li>{@code execute()}</li> * <li>{@code execute()}</li>
* <li>{@code remove()}</li> * <li>{@code remove()}</li>
@@ -42,53 +43,65 @@ import org.luaj.vm2.lib.OsLib;
* <li>{@code tmpname()}</li> * <li>{@code tmpname()}</li>
* </ul> * </ul>
* <p> * <p>
* Because the nature of the {@code os} library is to encapsulate * Because the nature of the {@code os} library is to encapsulate os-specific
* os-specific features, the behavior of these functions varies considerably * features, the behavior of these functions varies considerably from their
* from their counterparts in the C platform. * counterparts in the C platform.
* <p> * <p>
* Typically, this library is included as part of a call to * Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* System.out.println( globals.get("os").get("time").call() ); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
* <p> * <p>
* For special cases where the smallest possible footprint is desired, * For special cases where the smallest possible footprint is desired, a minimal
* a minimal set of libraries could be loaded * set of libraries could be loaded directly via {@link Globals#load(LuaValue)}
* directly via {@link Globals#load(LuaValue)} using code such as: * using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new JseOsLib()); * Globals globals = new Globals();
* System.out.println( globals.get("os").get("time").call() ); * globals.load(new JseBaseLib());
* } </pre> * globals.load(new PackageLib());
* <p>However, other libraries such as <em>MathLib</em> are not loaded in this case. * globals.load(new JseOsLib());
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
* <p> * <p>
* However, other libraries such as <em>MathLib</em> are not loaded in this
* case.
* <p>
*
* @see LibFunction * @see LibFunction
* @see OsLib * @see OsLib
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
* @see <a href="http://www.lua.org/manual/5.2/manual.html#6.9">Lua 5.2 OS Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.9">Lua 5.2 OS Lib
* Reference</a>
*/ */
public class JseOsLib extends org.luaj.vm2.lib.OsLib { public class JseOsLib extends org.luaj.vm2.lib.OsLib {
/** return code indicating the execute() threw an I/O exception */ /** return code indicating the execute() threw an I/O exception */
public static final int EXEC_IOEXCEPTION = 1; public static final int EXEC_IOEXCEPTION = 1;
/** return code indicating the execute() was interrupted */ /** return code indicating the execute() was interrupted */
public static final int EXEC_INTERRUPTED = -2; public static final int EXEC_INTERRUPTED = -2;
/** return code indicating the execute() threw an unknown exception */ /** return code indicating the execute() threw an unknown exception */
public static final int EXEC_ERROR = -3; public static final int EXEC_ERROR = -3;
/** public constructor */ /** public constructor */
public JseOsLib() { public JseOsLib() {
} }
protected String getenv(String varname) { protected String getenv(String varname) {
String s = System.getenv(varname); String s = System.getenv(varname);
return s != null? s : System.getProperty(varname); return s != null? s: System.getProperty(varname);
} }
protected Varargs execute(String command) { protected Varargs execute(String command) {
@@ -109,27 +122,27 @@ public class JseOsLib extends org.luaj.vm2.lib.OsLib {
protected void remove(String filename) throws IOException { protected void remove(String filename) throws IOException {
File f = new File(filename); File f = new File(filename);
if ( ! f.exists() ) if (!f.exists())
throw new IOException("No such file or directory"); throw new IOException("No such file or directory");
if ( ! f.delete() ) if (!f.delete())
throw new IOException("Failed to delete"); throw new IOException("Failed to delete");
} }
protected void rename(String oldname, String newname) throws IOException { protected void rename(String oldname, String newname) throws IOException {
File f = new File(oldname); File f = new File(oldname);
if ( ! f.exists() ) if (!f.exists())
throw new IOException("No such file or directory"); throw new IOException("No such file or directory");
if ( ! f.renameTo(new File(newname)) ) if (!f.renameTo(new File(newname)))
throw new IOException("Failed to rename"); throw new IOException("Failed to rename");
} }
protected String tmpname() { protected String tmpname() {
try { try {
java.io.File f = java.io.File.createTempFile(TMP_PREFIX ,TMP_SUFFIX); java.io.File f = java.io.File.createTempFile(TMP_PREFIX, TMP_SUFFIX);
return f.getAbsolutePath(); return f.getAbsolutePath();
} catch ( IOException ioe ) { } catch (IOException ioe) {
return super.tmpname(); return super.tmpname();
} }
} }
} }

View File

@@ -34,31 +34,45 @@ import org.luaj.vm2.lib.ResourceFinder;
import org.luaj.vm2.lib.StringLib; import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.TableLib; import org.luaj.vm2.lib.TableLib;
/** The {@link org.luaj.vm2.lib.jse.JsePlatform} class is a convenience class to standardize /**
* how globals tables are initialized for the JSE platform. * The {@link org.luaj.vm2.lib.jse.JsePlatform} class is a convenience class to
* standardize how globals tables are initialized for the JSE platform.
* <p> * <p>
* It is used to allocate either a set of standard globals using * It is used to allocate either a set of standard globals using
* {@link #standardGlobals()} or debug globals using {@link #debugGlobals()} * {@link #standardGlobals()} or debug globals using {@link #debugGlobals()}
* <p> * <p>
* A simple example of initializing globals and using them from Java is: * A simple example of initializing globals and using them from Java is:
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* globals.get("print").call(LuaValue.valueOf("hello, world")); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world"));
* }
* </pre>
* <p> * <p>
* Once globals are created, a simple way to load and run a script is: * Once globals are created, a simple way to load and run a script is:
* <pre> {@code *
* <pre>
* {@code
* globals.load( new FileInputStream("main.lua"), "main.lua" ).call(); * globals.load( new FileInputStream("main.lua"), "main.lua" ).call();
* } </pre> * }
* </pre>
* <p> * <p>
* although {@code require} could also be used: * although {@code require} could also be used:
* <pre> {@code *
* <pre>
* {@code
* globals.get("require").call(LuaValue.valueOf("main")); * globals.get("require").call(LuaValue.valueOf("main"));
* } </pre> * }
* For this to succeed, the file "main.lua" must be in the current directory or a resource. * </pre>
* See {@link org.luaj.vm2.lib.jse.JseBaseLib} for details on finding scripts using {@link ResourceFinder}. *
* For this to succeed, the file "main.lua" must be in the current directory or
* a resource. See {@link org.luaj.vm2.lib.jse.JseBaseLib} for details on
* finding scripts using {@link ResourceFinder}.
* <p> * <p>
* The standard globals will contain all standard libraries plus {@code luajava}: * The standard globals will contain all standard libraries plus
* {@code luajava}:
* <ul> * <ul>
* <li>{@link Globals}</li> * <li>{@link Globals}</li>
* <li>{@link org.luaj.vm2.lib.jse.JseBaseLib}</li> * <li>{@link org.luaj.vm2.lib.jse.JseBaseLib}</li>
@@ -72,9 +86,11 @@ import org.luaj.vm2.lib.TableLib;
* <li>{@link org.luaj.vm2.lib.jse.JseOsLib}</li> * <li>{@link org.luaj.vm2.lib.jse.JseOsLib}</li>
* <li>{@link org.luaj.vm2.lib.jse.LuajavaLib}</li> * <li>{@link org.luaj.vm2.lib.jse.LuajavaLib}</li>
* </ul> * </ul>
* In addition, the {@link LuaC} compiler is installed so lua files may be loaded in their source form. * In addition, the {@link LuaC} compiler is installed so lua files may be
* loaded in their source form.
* <p> * <p>
* The debug globals are simply the standard globals plus the {@code debug} library {@link DebugLib}. * The debug globals are simply the standard globals plus the {@code debug}
* library {@link DebugLib}.
* <p> * <p>
* The class ensures that initialization is done in the correct order. * The class ensures that initialization is done in the correct order.
* *
@@ -108,9 +124,11 @@ public class JsePlatform {
return globals; return globals;
} }
/** Create standard globals including the {@link DebugLib} library. /**
* Create standard globals including the {@link DebugLib} library.
* *
* @return Table of globals initialized with the standard JSE and debug libraries * @return Table of globals initialized with the standard JSE and debug
* libraries
* @see #standardGlobals() * @see #standardGlobals()
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
* @see org.luaj.vm2.lib.jme.JmePlatform * @see org.luaj.vm2.lib.jme.JmePlatform
@@ -122,10 +140,11 @@ public class JsePlatform {
return globals; return globals;
} }
/**
/** Simple wrapper for invoking a lua function with command line arguments. * Simple wrapper for invoking a lua function with command line arguments.
* The supplied function is first given a new Globals object as its environment * The supplied function is first given a new Globals object as its
* then the program is run with arguments. * environment then the program is run with arguments.
*
* @return {@link Varargs} containing any values returned by mainChunk. * @return {@link Varargs} containing any values returned by mainChunk.
*/ */
public static Varargs luaMain(LuaValue mainChunk, String[] args) { public static Varargs luaMain(LuaValue mainChunk, String[] args) {

View File

@@ -25,37 +25,48 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
/** Analog of Process that pipes input and output to client-specified streams. /**
* Analog of Process that pipes input and output to client-specified streams.
*/ */
public class JseProcess { public class JseProcess {
final Process process; final Process process;
final Thread input,output,error; final Thread input, output, error;
/** Construct a process around a command, with specified streams to redirect input and output to. /**
* Construct a process around a command, with specified streams to redirect
* input and output to.
* *
* @param cmd The command to execute, including arguments, if any * @param cmd The command to execute, including arguments, if any
* @param stdin Optional InputStream to read from as process input, or null if input is not needed. * @param stdin Optional InputStream to read from as process input, or null
* @param stdout Optional OutputStream to copy process output to, or null if output is ignored. * if input is not needed.
* @param stderr Optinoal OutputStream to copy process stderr output to, or null if output is ignored. * @param stdout Optional OutputStream to copy process output to, or null if
* output is ignored.
* @param stderr Optinoal OutputStream to copy process stderr output to, or
* null if output is ignored.
* @throws IOException If the system process could not be created. * @throws IOException If the system process could not be created.
* @see Process * @see Process
*/ */
public JseProcess(String[] cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException { public JseProcess(String[] cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException {
this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr); this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr);
} }
/** Construct a process around a command, with specified streams to redirect input and output to. /**
* Construct a process around a command, with specified streams to redirect
* input and output to.
* *
* @param cmd The command to execute, including arguments, if any * @param cmd The command to execute, including arguments, if any
* @param stdin Optional InputStream to read from as process input, or null if input is not needed. * @param stdin Optional InputStream to read from as process input, or null
* @param stdout Optional OutputStream to copy process output to, or null if output is ignored. * if input is not needed.
* @param stderr Optinoal OutputStream to copy process stderr output to, or null if output is ignored. * @param stdout Optional OutputStream to copy process output to, or null if
* output is ignored.
* @param stderr Optinoal OutputStream to copy process stderr output to, or
* null if output is ignored.
* @throws IOException If the system process could not be created. * @throws IOException If the system process could not be created.
* @see Process * @see Process
*/ */
public JseProcess(String cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException { public JseProcess(String cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException {
this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr); this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr);
} }
private JseProcess(Process process, InputStream stdin, OutputStream stdout, OutputStream stderr) { private JseProcess(Process process, InputStream stdin, OutputStream stdout, OutputStream stderr) {
@@ -70,7 +81,9 @@ public class JseProcess {
return process.exitValue(); return process.exitValue();
} }
/** Wait for the process to complete, and all pending output to finish. /**
* Wait for the process to complete, and all pending output to finish.
*
* @return The exit status. * @return The exit status.
* @throws InterruptedException * @throws InterruptedException
*/ */
@@ -87,9 +100,8 @@ public class JseProcess {
} }
/** Create a thread to copy bytes from input to output. */ /** Create a thread to copy bytes from input to output. */
private Thread copyBytes(final InputStream input, private Thread copyBytes(final InputStream input, final OutputStream output, final InputStream ownedInput,
final OutputStream output, final InputStream ownedInput, final OutputStream ownedOutput) {
final OutputStream ownedOutput) {
Thread t = (new CopyThread(output, ownedOutput, ownedInput, input)); Thread t = (new CopyThread(output, ownedOutput, ownedInput, input));
t.start(); t.start();
return t; return t;
@@ -98,11 +110,10 @@ public class JseProcess {
private static final class CopyThread extends Thread { private static final class CopyThread extends Thread {
private final OutputStream output; private final OutputStream output;
private final OutputStream ownedOutput; private final OutputStream ownedOutput;
private final InputStream ownedInput; private final InputStream ownedInput;
private final InputStream input; private final InputStream input;
private CopyThread(OutputStream output, OutputStream ownedOutput, private CopyThread(OutputStream output, OutputStream ownedOutput, InputStream ownedInput, InputStream input) {
InputStream ownedInput, InputStream input) {
this.output = output; this.output = output;
this.ownedOutput = ownedOutput; this.ownedOutput = ownedOutput;
this.ownedInput = ownedInput; this.ownedInput = ownedInput;
@@ -114,7 +125,7 @@ public class JseProcess {
byte[] buf = new byte[1024]; byte[] buf = new byte[1024];
int r; int r;
try { try {
while ((r = input.read(buf)) >= 0) { while ( (r = input.read(buf)) >= 0 ) {
output.write(buf, 0, r); output.write(buf, 0, r);
} }
} finally { } finally {

View File

@@ -22,7 +22,7 @@
package org.luaj.vm2.lib.jse; package org.luaj.vm2.lib.jse;
public class JseStringLib extends org.luaj.vm2.lib.StringLib { public class JseStringLib extends org.luaj.vm2.lib.StringLib {
/** public constructor */ /** public constructor */
public JseStringLib() { public JseStringLib() {
} }
@@ -30,7 +30,7 @@ public class JseStringLib extends org.luaj.vm2.lib.StringLib {
protected String format(String src, double x) { protected String format(String src, double x) {
String out; String out;
try { try {
out = String.format(src, new Object[] {Double.valueOf(x)}); out = String.format(src, new Object[] { Double.valueOf(x) });
} catch (Throwable e) { } catch (Throwable e) {
out = super.format(src, x); out = super.format(src, x);
} }

View File

@@ -21,7 +21,6 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2.lib.jse; package org.luaj.vm2.lib.jse;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -38,40 +37,51 @@ import org.luaj.vm2.lib.LibFunction;
import org.luaj.vm2.lib.VarArgFunction; import org.luaj.vm2.lib.VarArgFunction;
/** /**
* Subclass of {@link LibFunction} which implements the features of the luajava package. * Subclass of {@link LibFunction} which implements the features of the luajava
* package.
* <p> * <p>
* Luajava is an approach to mixing lua and java using simple functions that bind * Luajava is an approach to mixing lua and java using simple functions that
* java classes and methods to lua dynamically. The API is documented on the * bind java classes and methods to lua dynamically. The API is documented on
* <a href="http://www.keplerproject.org/luajava/">luajava</a> documentation pages. * the <a href="http://www.keplerproject.org/luajava/">luajava</a> documentation
* pages.
* *
* <p> * <p>
* Typically, this library is included as part of a call to * Typically, this library is included as part of a call to
* {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()}
* <pre> {@code *
* Globals globals = JsePlatform.standardGlobals(); * <pre>
* System.out.println( globals.get("luajava").get("bindClass").call( LuaValue.valueOf("java.lang.System") ).invokeMethod("currentTimeMillis") ); * {
* } </pre> * &#64;code
* Globals globals = JsePlatform.standardGlobals();
* System.out.println(globals.get("luajava").get("bindClass").call(LuaValue.valueOf("java.lang.System"))
* .invokeMethod("currentTimeMillis"));
* }
* </pre>
* <p> * <p>
* To instantiate and use it directly, * To instantiate and use it directly, link it into your globals table via
* link it into your globals table via {@link Globals#load} using code such as: * {@link Globals#load} using code such as:
* <pre> {@code *
* Globals globals = new Globals(); * <pre>
* globals.load(new JseBaseLib()); * {
* globals.load(new PackageLib()); * &#64;code
* globals.load(new LuajavaLib()); * Globals globals = new Globals();
* globals.load( * globals.load(new JseBaseLib());
* "sys = luajava.bindClass('java.lang.System')\n"+ * globals.load(new PackageLib());
* "print ( sys:currentTimeMillis() )\n", "main.lua" ).call(); * globals.load(new LuajavaLib());
* } </pre> * globals.load("sys = luajava.bindClass('java.lang.System')\n" + "print ( sys:currentTimeMillis() )\n", "main.lua")
* .call();
* }
* </pre>
* <p> * <p>
* *
* The {@code luajava} library is available * The {@code luajava} library is available on all JSE platforms via the call to
* on all JSE platforms via the call to {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jse.JsePlatform#standardGlobals()} and the luajava
* and the luajava api's are simply invoked from lua. * api's are simply invoked from lua. Because it makes extensive use of Java's
* Because it makes extensive use of Java's reflection API, it is not available * reflection API, it is not available on JME, but can be used in Android
* on JME, but can be used in Android applications. * applications.
* <p> * <p>
* This has been implemented to match as closely as possible the behavior in the corresponding library in C. * This has been implemented to match as closely as possible the behavior in the
* corresponding library in C.
* *
* @see LibFunction * @see LibFunction
* @see org.luaj.vm2.lib.jse.JsePlatform * @see org.luaj.vm2.lib.jse.JsePlatform
@@ -79,25 +89,20 @@ import org.luaj.vm2.lib.VarArgFunction;
* @see LuaC * @see LuaC
* @see CoerceJavaToLua * @see CoerceJavaToLua
* @see CoerceLuaToJava * @see CoerceLuaToJava
* @see <a href="http://www.keplerproject.org/luajava/manual.html#luareference">http://www.keplerproject.org/luajava/manual.html#luareference</a> * @see <a href=
* "http://www.keplerproject.org/luajava/manual.html#luareference">http://www.keplerproject.org/luajava/manual.html#luareference</a>
*/ */
public class LuajavaLib extends VarArgFunction { public class LuajavaLib extends VarArgFunction {
static final int INIT = 0; static final int INIT = 0;
static final int BINDCLASS = 1; static final int BINDCLASS = 1;
static final int NEWINSTANCE = 2; static final int NEWINSTANCE = 2;
static final int NEW = 3; static final int NEW = 3;
static final int CREATEPROXY = 4; static final int CREATEPROXY = 4;
static final int LOADLIB = 5; static final int LOADLIB = 5;
static final String[] NAMES = { "bindClass", "newInstance", "new", "createProxy", "loadLib", };
static final String[] NAMES = {
"bindClass",
"newInstance",
"new",
"createProxy",
"loadLib",
};
static final int METHOD_MODIFIERS_VARARGS = 0x80; static final int METHOD_MODIFIERS_VARARGS = 0x80;
public LuajavaLib() { public LuajavaLib() {
@@ -105,14 +110,15 @@ public class LuajavaLib extends VarArgFunction {
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
try { try {
switch ( opcode ) { switch (opcode) {
case INIT: { case INIT: {
// LuaValue modname = args.arg1(); // LuaValue modname = args.arg1();
LuaValue env = args.arg(2); LuaValue env = args.arg(2);
LuaTable t = new LuaTable(); LuaTable t = new LuaTable();
bind( t, this.getClass(), NAMES, BINDCLASS ); bind(t, this.getClass(), NAMES, BINDCLASS);
env.set("luajava", t); env.set("luajava", t);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("luajava", t); if (!env.get("package").isnil())
env.get("package").get("loaded").set("luajava", t);
return t; return t;
} }
case BINDCLASS: { case BINDCLASS: {
@@ -123,30 +129,31 @@ public class LuajavaLib extends VarArgFunction {
case NEW: { case NEW: {
// get constructor // get constructor
final LuaValue c = args.checkvalue(1); final LuaValue c = args.checkvalue(1);
final Class clazz = (opcode==NEWINSTANCE? classForName(c.tojstring()): (Class) c.checkuserdata(Class.class)); final Class clazz = (opcode == NEWINSTANCE? classForName(c.tojstring())
: (Class) c.checkuserdata(Class.class));
final Varargs consargs = args.subargs(2); final Varargs consargs = args.subargs(2);
return JavaClass.forClass(clazz).getConstructor().invoke(consargs); return JavaClass.forClass(clazz).getConstructor().invoke(consargs);
} }
case CREATEPROXY: { case CREATEPROXY: {
final int niface = args.narg()-1; final int niface = args.narg()-1;
if ( niface <= 0 ) if (niface <= 0)
throw new LuaError("no interfaces"); throw new LuaError("no interfaces");
final LuaValue lobj = args.checktable(niface+1); final LuaValue lobj = args.checktable(niface+1);
// get the interfaces // get the interfaces
final Class[] ifaces = new Class[niface]; final Class[] ifaces = new Class[niface];
for ( int i=0; i<niface; i++ ) for (int i = 0; i < niface; i++)
ifaces[i] = classForName(args.checkjstring(i+1)); ifaces[i] = classForName(args.checkjstring(i+1));
// create the invocation handler // create the invocation handler
InvocationHandler handler = new ProxyInvocationHandler(lobj); InvocationHandler handler = new ProxyInvocationHandler(lobj);
// create the proxy object // create the proxy object
Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler); Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler);
// return the proxy // return the proxy
return LuaValue.userdataOf( proxy ); return LuaValue.userdataOf(proxy);
} }
case LOADLIB: { case LOADLIB: {
// get constructor // get constructor
@@ -155,14 +162,14 @@ public class LuajavaLib extends VarArgFunction {
Class clazz = classForName(classname); Class clazz = classForName(classname);
Method method = clazz.getMethod(methodname, new Class[] {}); Method method = clazz.getMethod(methodname, new Class[] {});
Object result = method.invoke(clazz, new Object[] {}); Object result = method.invoke(clazz, new Object[] {});
if ( result instanceof LuaValue ) { if (result instanceof LuaValue) {
return (LuaValue) result; return (LuaValue) result;
} else { } else {
return NIL; return NIL;
} }
} }
default: default:
throw new LuaError("not yet supported: "+this); throw new LuaError("not yet supported: " + this);
} }
} catch (LuaError e) { } catch (LuaError e) {
throw e; throw e;
@@ -177,7 +184,7 @@ public class LuajavaLib extends VarArgFunction {
protected Class classForName(String name) throws ClassNotFoundException { protected Class classForName(String name) throws ClassNotFoundException {
return Class.forName(name, true, ClassLoader.getSystemClassLoader()); return Class.forName(name, true, ClassLoader.getSystemClassLoader());
} }
private static final class ProxyInvocationHandler implements InvocationHandler { private static final class ProxyInvocationHandler implements InvocationHandler {
private final LuaValue lobj; private final LuaValue lobj;
@@ -188,27 +195,27 @@ public class LuajavaLib extends VarArgFunction {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName(); String name = method.getName();
LuaValue func = lobj.get(name); LuaValue func = lobj.get(name);
if ( func.isnil() ) if (func.isnil())
return null; return null;
boolean isvarargs = ((method.getModifiers() & METHOD_MODIFIERS_VARARGS) != 0); boolean isvarargs = ((method.getModifiers() & METHOD_MODIFIERS_VARARGS) != 0);
int n = args!=null? args.length: 0; int n = args != null? args.length: 0;
LuaValue[] v; LuaValue[] v;
if ( isvarargs ) { if (isvarargs) {
Object o = args[--n]; Object o = args[--n];
int m = Array.getLength( o ); int m = Array.getLength(o);
v = new LuaValue[n+m]; v = new LuaValue[n+m];
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
v[i] = CoerceJavaToLua.coerce(args[i]); v[i] = CoerceJavaToLua.coerce(args[i]);
for ( int i=0; i<m; i++ ) for (int i = 0; i < m; i++)
v[i+n] = CoerceJavaToLua.coerce(Array.get(o,i)); v[i+n] = CoerceJavaToLua.coerce(Array.get(o, i));
} else { } else {
v = new LuaValue[n]; v = new LuaValue[n];
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
v[i] = CoerceJavaToLua.coerce(args[i]); v[i] = CoerceJavaToLua.coerce(args[i]);
} }
LuaValue result = func.invoke(v).arg1(); LuaValue result = func.invoke(v).arg1();
return CoerceLuaToJava.coerce(result, method.getReturnType()); return CoerceLuaToJava.coerce(result, method.getReturnType());
} }
} }
} }

View File

@@ -9,40 +9,38 @@ import org.luaj.vm2.Lua;
import org.luaj.vm2.Prototype; import org.luaj.vm2.Prototype;
public class BasicBlock { public class BasicBlock {
int pc0,pc1; // range of program counter values for the block int pc0, pc1; // range of program counter values for the block
BasicBlock[] prev; // previous basic blocks (0-n of these) BasicBlock[] prev; // previous basic blocks (0-n of these)
BasicBlock[] next; // next basic blocks (0, 1, or 2 of these) BasicBlock[] next; // next basic blocks (0, 1, or 2 of these)
boolean islive; // true if this block is used boolean islive; // true if this block is used
public BasicBlock(Prototype p, int pc0) { public BasicBlock(Prototype p, int pc0) {
this.pc0 = this.pc1 = pc0; this.pc0 = this.pc1 = pc0;
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append( (pc0+1)+"-"+(pc1+1) sb.append((pc0+1) + "-" + (pc1+1) + (prev != null? " prv: " + str(prev, 1): "")
+(prev!=null? " prv: "+str(prev,1): "") + (next != null? " nxt: " + str(next, 0): "") + "\n");
+(next!=null? " nxt: "+str(next,0): "")
+"\n" );
return sb.toString(); return sb.toString();
} }
private String str(BasicBlock[] b, int p) { private String str(BasicBlock[] b, int p) {
if ( b == null ) if (b == null)
return ""; return "";
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append("("); sb.append("(");
for ( int i=0, n=b.length; i<n; i++ ) { for (int i = 0, n = b.length; i < n; i++) {
if ( i > 0 ) if (i > 0)
sb.append( "," ); sb.append(",");
sb.append( String.valueOf( p==1? b[i].pc1+1: b[i].pc0+1 ) ); sb.append(String.valueOf(p == 1? b[i].pc1+1: b[i].pc0+1));
} }
sb.append(")"); sb.append(")");
return sb.toString(); return sb.toString();
} }
public static BasicBlock[] findBasicBlocks(Prototype p) { public static BasicBlock[] findBasicBlocks(Prototype p) {
// mark beginnings, endings // mark beginnings, endings
final int n = p.code.length; final int n = p.code.length;
final boolean[] isbeg = new boolean[n]; final boolean[] isbeg = new boolean[n];
@@ -51,34 +49,33 @@ public class BasicBlock {
BranchVisitor bv = new MarkAndMergeVisitor(isbeg, isend); BranchVisitor bv = new MarkAndMergeVisitor(isbeg, isend);
visitBranches(p, bv); // 1st time to mark branches visitBranches(p, bv); // 1st time to mark branches
visitBranches(p, bv); // 2nd time to catch merges visitBranches(p, bv); // 2nd time to catch merges
// create basic blocks // create basic blocks
final BasicBlock[] blocks = new BasicBlock[n]; final BasicBlock[] blocks = new BasicBlock[n];
for ( int i=0; i<n; i++ ) { for (int i = 0; i < n; i++) {
isbeg[i] = true; isbeg[i] = true;
BasicBlock b = new BasicBlock(p,i); BasicBlock b = new BasicBlock(p, i);
blocks[i] = b; blocks[i] = b;
while ( !isend[i] && i+1<n && !isbeg[i+1] ) while ( !isend[i] && i+1 < n && !isbeg[i+1] )
blocks[b.pc1=++i] = b; blocks[b.pc1 = ++i] = b;
} }
// count previous, next // count previous, next
final int[] nnext = new int[n]; final int[] nnext = new int[n];
final int[] nprev = new int[n]; final int[] nprev = new int[n];
visitBranches(p, new CountPrevNextVistor(isbeg, nnext, nprev)); visitBranches(p, new CountPrevNextVistor(isbeg, nnext, nprev));
// allocate and cross-reference // allocate and cross-reference
visitBranches( p, new AllocAndXRefVisitor(isbeg, nnext, nprev, blocks)); visitBranches(p, new AllocAndXRefVisitor(isbeg, nnext, nprev, blocks));
return blocks; return blocks;
} }
private static final class AllocAndXRefVisitor extends BranchVisitor { private static final class AllocAndXRefVisitor extends BranchVisitor {
private final int[] nnext; private final int[] nnext;
private final int[] nprev; private final int[] nprev;
private final BasicBlock[] blocks; private final BasicBlock[] blocks;
private AllocAndXRefVisitor(boolean[] isbeg, int[] nnext, int[] nprev, private AllocAndXRefVisitor(boolean[] isbeg, int[] nnext, int[] nprev, BasicBlock[] blocks) {
BasicBlock[] blocks) {
super(isbeg); super(isbeg);
this.nnext = nnext; this.nnext = nnext;
this.nprev = nprev; this.nprev = nprev;
@@ -86,8 +83,10 @@ public class BasicBlock {
} }
public void visitBranch(int pc0, int pc1) { public void visitBranch(int pc0, int pc1) {
if ( blocks[pc0].next == null ) blocks[pc0].next = new BasicBlock[nnext[pc0]]; if (blocks[pc0].next == null)
if ( blocks[pc1].prev == null ) blocks[pc1].prev = new BasicBlock[nprev[pc1]]; blocks[pc0].next = new BasicBlock[nnext[pc0]];
if (blocks[pc1].prev == null)
blocks[pc1].prev = new BasicBlock[nprev[pc1]];
blocks[pc0].next[--nnext[pc0]] = blocks[pc1]; blocks[pc0].next[--nnext[pc0]] = blocks[pc1];
blocks[pc1].prev[--nprev[pc1]] = blocks[pc0]; blocks[pc1].prev[--nprev[pc1]] = blocks[pc0];
} }
@@ -129,87 +128,90 @@ public class BasicBlock {
abstract public static class BranchVisitor { abstract public static class BranchVisitor {
final boolean[] isbeg; final boolean[] isbeg;
public BranchVisitor(boolean[] isbeg) { public BranchVisitor(boolean[] isbeg) {
this.isbeg = isbeg; this.isbeg = isbeg;
} }
public void visitBranch( int frompc, int topc ) {}
public void visitReturn( int atpc ) {} public void visitBranch(int frompc, int topc) {}
public void visitReturn(int atpc) {}
} }
public static void visitBranches( Prototype p, BranchVisitor visitor ) { public static void visitBranches(Prototype p, BranchVisitor visitor) {
int sbx,j,c; int sbx, j, c;
int[] code = p.code; int[] code = p.code;
int n = code.length; int n = code.length;
for ( int i=0; i<n; i++ ) { for (int i = 0; i < n; i++) {
int ins = code[i]; int ins = code[i];
switch ( Lua.GET_OPCODE( ins ) ) { switch (Lua.GET_OPCODE(ins)) {
case Lua.OP_LOADBOOL: case Lua.OP_LOADBOOL:
if ( 0 == Lua.GETARG_C(ins) ) if (0 == Lua.GETARG_C(ins))
break; break;
if ( Lua.GET_OPCODE(code[i+1]) == Lua.OP_JMP ) if (Lua.GET_OPCODE(code[i+1]) == Lua.OP_JMP)
throw new IllegalArgumentException("OP_LOADBOOL followed by jump at "+i); throw new IllegalArgumentException("OP_LOADBOOL followed by jump at " + i);
visitor.visitBranch( i, i+2 ); visitor.visitBranch(i, i+2);
continue; continue;
case Lua.OP_EQ: case Lua.OP_EQ:
case Lua.OP_LT: case Lua.OP_LT:
case Lua.OP_LE: case Lua.OP_LE:
case Lua.OP_TEST: case Lua.OP_TEST:
case Lua.OP_TESTSET: case Lua.OP_TESTSET:
if ( Lua.GET_OPCODE(code[i+1]) != Lua.OP_JMP ) if (Lua.GET_OPCODE(code[i+1]) != Lua.OP_JMP)
throw new IllegalArgumentException("test not followed by jump at "+i); throw new IllegalArgumentException("test not followed by jump at " + i);
sbx = Lua.GETARG_sBx(code[i+1]); sbx = Lua.GETARG_sBx(code[i+1]);
++i; ++i;
j = i + sbx + 1; j = i+sbx+1;
visitor.visitBranch( i, j ); visitor.visitBranch(i, j);
visitor.visitBranch( i, i+1 ); visitor.visitBranch(i, i+1);
continue; continue;
case Lua.OP_TFORLOOP: case Lua.OP_TFORLOOP:
case Lua.OP_FORLOOP: case Lua.OP_FORLOOP:
sbx = Lua.GETARG_sBx(ins); sbx = Lua.GETARG_sBx(ins);
j = i + sbx + 1; j = i+sbx+1;
visitor.visitBranch( i, j ); visitor.visitBranch(i, j);
visitor.visitBranch( i, i+1 ); visitor.visitBranch(i, i+1);
continue; continue;
case Lua.OP_JMP: case Lua.OP_JMP:
case Lua.OP_FORPREP: case Lua.OP_FORPREP:
sbx = Lua.GETARG_sBx(ins); sbx = Lua.GETARG_sBx(ins);
j = i + sbx + 1; j = i+sbx+1;
visitor.visitBranch( i, j ); visitor.visitBranch(i, j);
continue; continue;
case Lua.OP_TAILCALL: case Lua.OP_TAILCALL:
case Lua.OP_RETURN: case Lua.OP_RETURN:
visitor.visitReturn( i ); visitor.visitReturn(i);
continue; continue;
} }
if ( i+1<n && visitor.isbeg[i+1] ) if (i+1 < n && visitor.isbeg[i+1])
visitor.visitBranch( i, i+1 ); visitor.visitBranch(i, i+1);
} }
} }
public static BasicBlock[] findLiveBlocks(BasicBlock[] blocks) { public static BasicBlock[] findLiveBlocks(BasicBlock[] blocks) {
// add reachable blocks // add reachable blocks
Vector next = new Vector (); Vector next = new Vector();
next.addElement( blocks[0] ); next.addElement(blocks[0]);
while ( ! next.isEmpty() ) { while ( !next.isEmpty() ) {
BasicBlock b = (BasicBlock) next.elementAt(0); BasicBlock b = (BasicBlock) next.elementAt(0);
next.removeElementAt(0); next.removeElementAt(0);
if ( ! b.islive ) { if (!b.islive) {
b.islive = true; b.islive = true;
for ( int i=0, n=b.next!=null? b.next.length: 0; i<n; i++ ) for (int i = 0, n = b.next != null? b.next.length: 0; i < n; i++)
if ( ! b.next[i].islive ) if (!b.next[i].islive)
next.addElement( b.next[i] ); next.addElement(b.next[i]);
} }
} }
// create list in natural order // create list in natural order
Vector list = new Vector(); Vector list = new Vector();
for ( int i=0; i<blocks.length; i=blocks[i].pc1+1 ) for (int i = 0; i < blocks.length; i = blocks[i].pc1+1)
if ( blocks[i].islive ) if (blocks[i].islive)
list.addElement(blocks[i]); list.addElement(blocks[i]);
// convert to array // convert to array
BasicBlock[] array = new BasicBlock[list.size()]; BasicBlock[] array = new BasicBlock[list.size()];
list.copyInto(array); list.copyInto(array);
return array; return array;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -27,23 +27,21 @@ import org.luaj.vm2.Prototype;
import org.luaj.vm2.Upvaldesc; import org.luaj.vm2.Upvaldesc;
/** /**
* TODO: * TODO: propogate constants loader can find inner classes
* propogate constants
* loader can find inner classes
*/ */
public class JavaGen { public class JavaGen {
public final String classname; public final String classname;
public final byte[] bytecode; public final byte[] bytecode;
public final JavaGen[] inners; public final JavaGen[] inners;
public JavaGen( Prototype p, String classname, String filename, boolean genmain ) { public JavaGen(Prototype p, String classname, String filename, boolean genmain) {
this( new ProtoInfo(p,classname), classname, filename, genmain ); this(new ProtoInfo(p, classname), classname, filename, genmain);
} }
private JavaGen( ProtoInfo pi, String classname, String filename, boolean genmain ) { private JavaGen(ProtoInfo pi, String classname, String filename, boolean genmain) {
this.classname = classname; this.classname = classname;
// build this class // build this class
JavaBuilder builder = new JavaBuilder(pi, classname, filename); JavaBuilder builder = new JavaBuilder(pi, classname, filename);
scanInstructions(pi, classname, builder); scanInstructions(pi, classname, builder);
@@ -52,12 +50,12 @@ public class JavaGen {
builder.setVarStartEnd(i, l.startpc, l.endpc, l.varname.tojstring()); builder.setVarStartEnd(i, l.startpc, l.endpc, l.varname.tojstring());
} }
this.bytecode = builder.completeClass(genmain); this.bytecode = builder.completeClass(genmain);
// build sub-prototypes // build sub-prototypes
if ( pi.subprotos != null ) { if (pi.subprotos != null) {
int n = pi.subprotos.length; int n = pi.subprotos.length;
inners = new JavaGen[n]; inners = new JavaGen[n];
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
inners[i] = new JavaGen(pi.subprotos[i], pi.subprotos[i].name, filename, false); inners[i] = new JavaGen(pi.subprotos[i], pi.subprotos[i].name, filename, false);
} else { } else {
inners = null; inners = null;
@@ -67,20 +65,20 @@ public class JavaGen {
private void scanInstructions(ProtoInfo pi, String classname, JavaBuilder builder) { private void scanInstructions(ProtoInfo pi, String classname, JavaBuilder builder) {
Prototype p = pi.prototype; Prototype p = pi.prototype;
int vresultbase = -1; int vresultbase = -1;
for ( int bi=0; bi<pi.blocklist.length; bi++ ) { for (int bi = 0; bi < pi.blocklist.length; bi++) {
BasicBlock b0 = pi.blocklist[bi]; BasicBlock b0 = pi.blocklist[bi];
// convert upvalues that are phi-variables // convert upvalues that are phi-variables
for ( int slot=0; slot<p.maxstacksize; slot++ ) { for (int slot = 0; slot < p.maxstacksize; slot++) {
int pc = b0.pc0; int pc = b0.pc0;
boolean c = pi.isUpvalueCreate(pc, slot); boolean c = pi.isUpvalueCreate(pc, slot);
if ( c && pi.vars[slot][pc].isPhiVar() ) if (c && pi.vars[slot][pc].isPhiVar())
builder.convertToUpvalue(pc, slot); builder.convertToUpvalue(pc, slot);
} }
for ( int pc=b0.pc0; pc<=b0.pc1; pc++ ) { for (int pc = b0.pc0; pc <= b0.pc1; pc++) {
final int pc0 = pc; // closure changes pc final int pc0 = pc; // closure changes pc
final int ins = p.code[pc]; final int ins = p.code[pc];
final int line = pc < p.lineinfo.length? p.lineinfo[pc]: -1; final int line = pc < p.lineinfo.length? p.lineinfo[pc]: -1;
@@ -90,104 +88,104 @@ public class JavaGen {
int bx = Lua.GETARG_Bx(ins); int bx = Lua.GETARG_Bx(ins);
int sbx = Lua.GETARG_sBx(ins); int sbx = Lua.GETARG_sBx(ins);
int c = Lua.GETARG_C(ins); int c = Lua.GETARG_C(ins);
switch ( o ) { switch (o) {
case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */ case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */
builder.loadUpvalue( b ); builder.loadUpvalue(b);
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
builder.storeUpvalue( pc, b, a );
break;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
builder.newTable( b, c );
builder.storeLocal( pc, a );
break;
case Lua.OP_MOVE:/* A B R(A):= R(B) */
builder.loadLocal( pc, b );
builder.storeLocal( pc, a );
break; break;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
builder.storeUpvalue(pc, b, a);
break;
case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */
builder.newTable(b, c);
builder.storeLocal(pc, a);
break;
case Lua.OP_MOVE:/* A B R(A):= R(B) */
builder.loadLocal(pc, b);
builder.storeLocal(pc, a);
break;
case Lua.OP_UNM: /* A B R(A):= -R(B) */ case Lua.OP_UNM: /* A B R(A):= -R(B) */
case Lua.OP_NOT: /* A B R(A):= not R(B) */ case Lua.OP_NOT: /* A B R(A):= not R(B) */
case Lua.OP_LEN: /* A B R(A):= length of R(B) */ case Lua.OP_LEN: /* A B R(A):= length of R(B) */
builder.loadLocal( pc, b ); builder.loadLocal(pc, b);
builder.unaryop( o ); builder.unaryop(o);
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break; break;
case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */ case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */
builder.loadConstant( p.k[bx] ); builder.loadConstant(p.k[bx]);
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break; break;
case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */ case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(A+B):= nil */
builder.loadNil(); builder.loadNil();
for ( ; b>=0; a++, b-- ) { for (; b >= 0; a++, b--) {
if ( b > 0 ) if (b > 0)
builder.dup(); builder.dup();
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
} }
break; break;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */ case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
builder.loadUpvalue( b ); builder.loadUpvalue(b);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.getTable(); builder.getTable();
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break; break;
case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */ case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */
builder.loadLocal( pc, b ); builder.loadLocal(pc, b);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.getTable(); builder.getTable();
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break; break;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */ case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
builder.loadUpvalue( a ); builder.loadUpvalue(a);
loadLocalOrConstant( p, builder, pc, b ); loadLocalOrConstant(p, builder, pc, b);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.setTable(); builder.setTable();
break; break;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */ case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
builder.loadLocal( pc, a ); builder.loadLocal(pc, a);
loadLocalOrConstant( p, builder, pc, b ); loadLocalOrConstant(p, builder, pc, b);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.setTable(); builder.setTable();
break; break;
case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */ case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */
case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */ case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */
case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */ case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */
case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */ case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */
case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */ case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */
case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */ case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */
loadLocalOrConstant( p, builder, pc, b ); loadLocalOrConstant(p, builder, pc, b);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.binaryop( o ); builder.binaryop(o);
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break; break;
case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */ case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */
builder.loadLocal(pc,b); builder.loadLocal(pc, b);
builder.dup(); builder.dup();
builder.storeLocal(pc, a+1); builder.storeLocal(pc, a+1);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.getTable(); builder.getTable();
builder.storeLocal(pc, a); builder.storeLocal(pc, a);
break; break;
case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */ case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */
for ( int k=b; k<=c; k++ ) for (int k = b; k <= c; k++)
builder.loadLocal(pc, k); builder.loadLocal(pc, k);
if ( c > b+1 ) { if (c > b+1) {
builder.tobuffer(); builder.tobuffer();
for ( int k=c; --k>=b; ) for (int k = c; --k >= b;)
builder.concatbuffer(); builder.concatbuffer();
builder.tovalue(); builder.tovalue();
} else { } else {
@@ -195,14 +193,14 @@ public class JavaGen {
} }
builder.storeLocal(pc, a); builder.storeLocal(pc, a);
break; break;
case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */ case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */
builder.loadBoolean( b!=0 ); builder.loadBoolean(b != 0);
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
if ( c!=0 ) if (c != 0)
builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+2); builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+2);
break; break;
case Lua.OP_JMP: /* sBx pc+=sBx */ case Lua.OP_JMP: /* sBx pc+=sBx */
if (a > 0) { if (a > 0) {
for (int i = a-1; i < pi.openups.length; ++i) { for (int i = a-1; i < pi.openups.length; ++i) {
@@ -211,74 +209,77 @@ public class JavaGen {
} }
builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+1+sbx); builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+1+sbx);
break; break;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
loadLocalOrConstant( p, builder, pc, b ); loadLocalOrConstant(p, builder, pc, b);
loadLocalOrConstant( p, builder, pc, c ); loadLocalOrConstant(p, builder, pc, c);
builder.compareop(o); builder.compareop(o);
builder.addBranch(pc, (a!=0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2); builder.addBranch(pc, (a != 0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2);
break; break;
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */ case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
builder.loadLocal( pc, a ); builder.loadLocal(pc, a);
builder.toBoolean(); builder.toBoolean();
builder.addBranch(pc, (c!=0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2); builder.addBranch(pc, (c != 0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2);
break; break;
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */ case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */
builder.loadLocal( pc, b ); builder.loadLocal(pc, b);
builder.toBoolean(); builder.toBoolean();
builder.addBranch(pc, (c!=0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2); builder.addBranch(pc, (c != 0? JavaBuilder.BRANCH_IFEQ: JavaBuilder.BRANCH_IFNE), pc+2);
builder.loadLocal( pc, b ); builder.loadLocal(pc, b);
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
break; break;
case Lua.OP_CALL: { /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */ case Lua.OP_CALL: { /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */
// load function // load function
builder.loadLocal(pc, a); builder.loadLocal(pc, a);
// load args // load args
int narg = b - 1; int narg = b-1;
switch ( narg ) { switch (narg) {
case 0: case 1: case 2: case 3: case 0:
for ( int i=1; i<b; i++ ) case 1:
case 2:
case 3:
for (int i = 1; i < b; i++)
builder.loadLocal(pc, a+i); builder.loadLocal(pc, a+i);
break; break;
default: // fixed arg count > 3 default: // fixed arg count > 3
builder.newVarargs( pc, a+1, b-1 ); builder.newVarargs(pc, a+1, b-1);
narg = -1; narg = -1;
break; break;
case -1: // prev vararg result case -1: // prev vararg result
loadVarargResults( builder, pc, a+1, vresultbase ); loadVarargResults(builder, pc, a+1, vresultbase);
narg = -1; narg = -1;
break; break;
} }
// call or invoke // call or invoke
boolean useinvoke = narg<0 || c<1 || c>2; boolean useinvoke = narg < 0 || c < 1 || c > 2;
if ( useinvoke ) if (useinvoke)
builder.invoke(narg); builder.invoke(narg);
else else
builder.call(narg); builder.call(narg);
// handle results // handle results
switch ( c ) { switch (c) {
case 1: case 1:
builder.pop(); builder.pop();
break; break;
case 2: case 2:
if ( useinvoke ) if (useinvoke)
builder.arg( 1 ); builder.arg(1);
builder.storeLocal(pc, a); builder.storeLocal(pc, a);
break; break;
default: // fixed result count - unpack args default: // fixed result count - unpack args
for ( int i=1; i<c; i++ ) { for (int i = 1; i < c; i++) {
if ( i+1 < c ) if (i+1 < c)
builder.dup(); builder.dup();
builder.arg( i ); builder.arg(i);
builder.storeLocal(pc, a+i-1); builder.storeLocal(pc, a+i-1);
} }
break; break;
@@ -287,59 +288,67 @@ public class JavaGen {
builder.storeVarresult(); builder.storeVarresult();
break; break;
} }
} }
break; break;
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
// load function // load function
builder.loadLocal(pc, a); builder.loadLocal(pc, a);
// load args // load args
switch ( b ) { switch (b) {
case 1: case 1:
builder.loadNone(); builder.loadNone();
break; break;
case 2: case 2:
builder.loadLocal(pc, a+1); builder.loadLocal(pc, a+1);
break; break;
default: // fixed arg count > 1 default: // fixed arg count > 1
builder.newVarargs( pc, a+1, b-1 ); builder.newVarargs(pc, a+1, b-1);
break; break;
case 0: // prev vararg result case 0: // prev vararg result
loadVarargResults( builder, pc, a+1, vresultbase ); loadVarargResults(builder, pc, a+1, vresultbase);
break; break;
} }
builder.newTailcallVarargs(); builder.newTailcallVarargs();
builder.areturn(); builder.areturn();
break; break;
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */ case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
if ( c == 1 ) { if (c == 1) {
builder.loadNone(); builder.loadNone();
} else { } else {
switch ( b ) { switch (b) {
case 0: loadVarargResults( builder, pc, a, vresultbase ); break; case 0:
case 1: builder.loadNone(); break; loadVarargResults(builder, pc, a, vresultbase);
case 2: builder.loadLocal(pc, a); break; break;
default: builder.newVarargs(pc, a, b-1); break; case 1:
builder.loadNone();
break;
case 2:
builder.loadLocal(pc, a);
break;
default:
builder.newVarargs(pc, a, b-1);
break;
} }
} }
builder.areturn(); builder.areturn();
break; break;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */ case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */
builder.loadLocal(pc, a); builder.loadLocal(pc, a);
builder.loadLocal(pc, a+2); builder.loadLocal(pc, a+2);
builder.binaryop( Lua.OP_SUB ); builder.binaryop(Lua.OP_SUB);
builder.storeLocal(pc, a); builder.storeLocal(pc, a);
builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+1+sbx); builder.addBranch(pc, JavaBuilder.BRANCH_GOTO, pc+1+sbx);
break; break;
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/ case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) <?= R(A+1) then { pc+=sBx: R(A+3)=R(A) }*/
builder.loadLocal(pc, a); builder.loadLocal(pc, a);
builder.loadLocal(pc, a+2); builder.loadLocal(pc, a+2);
builder.binaryop( Lua.OP_ADD ); builder.binaryop(Lua.OP_ADD);
builder.dup(); builder.dup();
builder.dup(); builder.dup();
builder.storeLocal(pc, a); builder.storeLocal(pc, a);
@@ -349,20 +358,20 @@ public class JavaGen {
builder.testForLoop(); builder.testForLoop();
builder.addBranch(pc, JavaBuilder.BRANCH_IFNE, pc+1+sbx); builder.addBranch(pc, JavaBuilder.BRANCH_IFNE, pc+1+sbx);
break; break;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
builder.loadLocal(pc, a); builder.loadLocal(pc, a);
builder.loadLocal(pc, a+1); builder.loadLocal(pc, a+1);
builder.loadLocal(pc, a+2); builder.loadLocal(pc, a+2);
builder.invoke(2); builder.invoke(2);
for ( int i=1; i<=c; i++ ) { for (int i = 1; i <= c; i++) {
if ( i < c ) if (i < c)
builder.dup(); builder.dup();
builder.arg( i ); builder.arg(i);
builder.storeLocal(pc, a+2+i); builder.storeLocal(pc, a+2+i);
} }
break; break;
case Lua.OP_TFORLOOP:/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */ case Lua.OP_TFORLOOP:/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx } */
builder.loadLocal(pc, a+1); builder.loadLocal(pc, a+1);
builder.dup(); builder.dup();
@@ -370,68 +379,68 @@ public class JavaGen {
builder.isNil(); builder.isNil();
builder.addBranch(pc, JavaBuilder.BRANCH_IFEQ, pc+1+sbx); builder.addBranch(pc, JavaBuilder.BRANCH_IFEQ, pc+1+sbx);
break; break;
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */ case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
int index0 = (c-1)*Lua.LFIELDS_PER_FLUSH + 1; int index0 = (c-1)*Lua.LFIELDS_PER_FLUSH+1;
builder.loadLocal( pc, a ); builder.loadLocal(pc, a);
if ( b == 0 ) { if (b == 0) {
int nstack = vresultbase - (a+1); int nstack = vresultbase-(a+1);
if ( nstack > 0 ) { if (nstack > 0) {
builder.setlistStack( pc, a+1, index0, nstack ); builder.setlistStack(pc, a+1, index0, nstack);
index0 += nstack; index0 += nstack;
} }
builder.setlistVarargs( index0, vresultbase ); builder.setlistVarargs(index0, vresultbase);
} else { } else {
builder.setlistStack( pc, a+1, index0, b ); builder.setlistStack(pc, a+1, index0, b);
builder.pop(); builder.pop();
} }
break; break;
case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
{ {
Prototype newp = p.p[bx]; Prototype newp = p.p[bx];
int nup = newp.upvalues.length; int nup = newp.upvalues.length;
String protoname = pi.subprotos[bx].name; String protoname = pi.subprotos[bx].name;
builder.closureCreate( protoname ); builder.closureCreate(protoname);
if ( nup > 0 ) if (nup > 0)
builder.dup(); builder.dup();
builder.storeLocal( pc, a ); builder.storeLocal(pc, a);
for ( int up=0; up<nup; ++up ) { for (int up = 0; up < nup; ++up) {
if ( up+1 < nup ) if (up+1 < nup)
builder.dup(); builder.dup();
Upvaldesc u = newp.upvalues[up]; Upvaldesc u = newp.upvalues[up];
if (u.instack) if (u.instack)
builder.closureInitUpvalueFromLocal( protoname, up, pc, u.idx ); builder.closureInitUpvalueFromLocal(protoname, up, pc, u.idx);
else else
builder.closureInitUpvalueFromUpvalue( protoname, up, u.idx ); builder.closureInitUpvalueFromUpvalue(protoname, up, u.idx);
} }
break; break;
} }
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
if ( b == 0 ) { if (b == 0) {
builder.loadVarargs(); builder.loadVarargs();
builder.storeVarresult(); builder.storeVarresult();
vresultbase = a; vresultbase = a;
} else { } else {
for ( int i=1; i<b; ++a, ++i ) { for (int i = 1; i < b; ++a, ++i) {
builder.loadVarargs( i ); builder.loadVarargs(i);
builder.storeLocal(pc, a); builder.storeLocal(pc, a);
} }
} }
break; break;
} }
// let builder process branch instructions // let builder process branch instructions
builder.onEndOfLuaInstruction( pc0, line ); builder.onEndOfLuaInstruction(pc0, line);
} }
} }
} }
private void loadVarargResults(JavaBuilder builder, int pc, int a, int vresultbase) { private void loadVarargResults(JavaBuilder builder, int pc, int a, int vresultbase) {
if ( vresultbase <= a ) { if (vresultbase <= a) {
builder.loadVarresult(); builder.loadVarresult();
builder.subargs( a+1-vresultbase ); builder.subargs(a+1-vresultbase);
} else if ( vresultbase == a ) { } else if (vresultbase == a) {
builder.loadVarresult(); builder.loadVarresult();
} else { } else {
builder.newVarargsVarresult(pc, a, vresultbase-a); builder.newVarargsVarresult(pc, a, vresultbase-a);
@@ -439,9 +448,9 @@ public class JavaGen {
} }
private void loadLocalOrConstant(Prototype p, JavaBuilder builder, int pc, int borc) { private void loadLocalOrConstant(Prototype p, JavaBuilder builder, int pc, int borc) {
if ( borc<=0xff ) if (borc <= 0xff)
builder.loadLocal( pc, borc ); builder.loadLocal(pc, borc);
else else
builder.loadConstant( p.k[borc&0xff] ); builder.loadConstant(p.k[borc & 0xff]);
} }
} }

View File

@@ -8,64 +8,64 @@ import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Prototype; import org.luaj.vm2.Prototype;
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2010 Luaj.org. All rights reserved. * Copyright (c) 2010 Luaj.org. All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* THE SOFTWARE. * SOFTWARE.
******************************************************************************/ ******************************************************************************/
public class JavaLoader extends ClassLoader { public class JavaLoader extends ClassLoader {
private Map<String,byte[]> unloaded = new HashMap<String,byte[]>(); private Map<String, byte[]> unloaded = new HashMap<String, byte[]>();
public JavaLoader() { public JavaLoader() {
} }
public LuaFunction load( Prototype p, String classname, String filename, LuaValue env ) { public LuaFunction load(Prototype p, String classname, String filename, LuaValue env) {
JavaGen jg = new JavaGen( p, classname, filename, false ); JavaGen jg = new JavaGen(p, classname, filename, false);
return load( jg, env ); return load(jg, env);
} }
public LuaFunction load( JavaGen jg, LuaValue env ) { public LuaFunction load(JavaGen jg, LuaValue env) {
include( jg ); include(jg);
return load( jg.classname, env ); return load(jg.classname, env);
} }
public LuaFunction load(String classname, LuaValue env) { public LuaFunction load(String classname, LuaValue env) {
try { try {
Class c = loadClass( classname ); Class c = loadClass(classname);
LuaFunction v = (LuaFunction) c.newInstance(); LuaFunction v = (LuaFunction) c.newInstance();
v.initupvalue1(env); v.initupvalue1(env);
return v; return v;
} catch ( Exception e ) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
throw new IllegalStateException("bad class gen: "+e); throw new IllegalStateException("bad class gen: " + e);
} }
} }
public void include( JavaGen jg ) { public void include(JavaGen jg) {
unloaded.put( jg.classname, jg.bytecode ); unloaded.put(jg.classname, jg.bytecode);
for ( int i=0, n=jg.inners!=null? jg.inners.length: 0; i<n; i++ ) for (int i = 0, n = jg.inners != null? jg.inners.length: 0; i < n; i++)
include( jg.inners[i] ); include(jg.inners[i]);
} }
public Class findClass(String classname) throws ClassNotFoundException { public Class findClass(String classname) throws ClassNotFoundException {
byte[] bytes = (byte[]) unloaded.get(classname); byte[] bytes = (byte[]) unloaded.get(classname);
if ( bytes != null ) if (bytes != null)
return defineClass(classname, bytes, 0, bytes.length); return defineClass(classname, bytes, 0, bytes.length);
return super.findClass(classname); return super.findClass(classname);
} }

View File

@@ -34,28 +34,30 @@ import org.luaj.vm2.Prototype;
import org.luaj.vm2.compiler.LuaC; import org.luaj.vm2.compiler.LuaC;
/** /**
* Implementation of {@link org.luaj.vm2.Globals.Compiler} which does direct * Implementation of {@link org.luaj.vm2.Globals.Compiler} which does direct
* lua-to-java-bytecode compiling. * lua-to-java-bytecode compiling.
* <p> * <p>
* By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or * By default, when using {@link org.luaj.vm2.lib.jse.JsePlatform} or
* {@link org.luaj.vm2.lib.jme.JmePlatform} * {@link org.luaj.vm2.lib.jme.JmePlatform} to construct globals, the plain
* to construct globals, the plain compiler {@link LuaC} is installed and lua code * compiler {@link LuaC} is installed and lua code will only be compiled into
* will only be compiled into lua bytecode and execute as {@link LuaClosure}. * lua bytecode and execute as {@link LuaClosure}.
* <p> * <p>
* To override the default compiling behavior with {@link LuaJC} * To override the default compiling behavior with {@link LuaJC} lua-to-java
* lua-to-java bytecode compiler, install it before undumping code, * bytecode compiler, install it before undumping code, for example:
* for example: *
* <pre> {@code * <pre>
* {@code
* LuaValue globals = JsePlatform.standardGlobals(); * LuaValue globals = JsePlatform.standardGlobals();
* LuaJC.install(globals); * LuaJC.install(globals);
* LuaValue chunk = globals.load( "print('hello, world'), "main.lua"); * LuaValue chunk = globals.load( "print('hello, world'), "main.lua");
* System.out.println(chunk.isclosure()); // Will be false when LuaJC is working. * System.out.println(chunk.isclosure()); // Will be false when LuaJC is working.
* chunk.call(); * chunk.call();
* } </pre> * }
* </pre>
* <p> * <p>
* This requires the bcel library to be on the class path to work as expected. * This requires the bcel library to be on the class path to work as expected.
* If the library is not found, the default {@link LuaC} lua-to-lua-bytecode * If the library is not found, the default {@link LuaC} lua-to-lua-bytecode
* compiler will be used. * compiler will be used.
* *
* @see Globals#compiler * @see Globals#compiler
* @see #install(Globals) * @see #install(Globals)
@@ -63,70 +65,75 @@ import org.luaj.vm2.compiler.LuaC;
* @see LuaValue * @see LuaValue
*/ */
public class LuaJC implements Globals.Loader { public class LuaJC implements Globals.Loader {
public static final LuaJC instance = new LuaJC(); public static final LuaJC instance = new LuaJC();
/** /**
* Install the compiler as the main Globals.Loader to use in a set of globals. * Install the compiler as the main Globals.Loader to use in a set of
* Will fall back to the LuaC prototype compiler. * globals. Will fall back to the LuaC prototype compiler.
*/ */
public static final void install(Globals G) { public static final void install(Globals G) {
G.loader = instance; G.loader = instance;
} }
protected LuaJC() {} protected LuaJC() {}
public Hashtable compileAll(InputStream script, String chunkname, String filename, Globals globals, boolean genmain) throws IOException { public Hashtable compileAll(InputStream script, String chunkname, String filename, Globals globals, boolean genmain)
final String classname = toStandardJavaClassName( chunkname ); throws IOException {
final String classname = toStandardJavaClassName(chunkname);
final Prototype p = globals.loadPrototype(script, classname, "bt"); final Prototype p = globals.loadPrototype(script, classname, "bt");
return compileProtoAndSubProtos(p, classname, filename, genmain); return compileProtoAndSubProtos(p, classname, filename, genmain);
} }
public Hashtable compileAll(Reader script, String chunkname, String filename, Globals globals, boolean genmain) throws IOException { public Hashtable compileAll(Reader script, String chunkname, String filename, Globals globals, boolean genmain)
final String classname = toStandardJavaClassName( chunkname ); throws IOException {
final String classname = toStandardJavaClassName(chunkname);
final Prototype p = globals.compilePrototype(script, classname); final Prototype p = globals.compilePrototype(script, classname);
return compileProtoAndSubProtos(p, classname, filename, genmain); return compileProtoAndSubProtos(p, classname, filename, genmain);
} }
private Hashtable compileProtoAndSubProtos(Prototype p, String classname, String filename, boolean genmain) throws IOException { private Hashtable compileProtoAndSubProtos(Prototype p, String classname, String filename, boolean genmain)
final String luaname = toStandardLuaFileName( filename ); throws IOException {
final String luaname = toStandardLuaFileName(filename);
final Hashtable h = new Hashtable(); final Hashtable h = new Hashtable();
final JavaGen gen = new JavaGen(p, classname, luaname, genmain); final JavaGen gen = new JavaGen(p, classname, luaname, genmain);
insert( h, gen ); insert(h, gen);
return h; return h;
} }
private void insert(Hashtable h, JavaGen gen) { private void insert(Hashtable h, JavaGen gen) {
h.put(gen.classname, gen.bytecode); h.put(gen.classname, gen.bytecode);
for ( int i=0, n=gen.inners!=null? gen.inners.length: 0; i<n; i++ ) for (int i = 0, n = gen.inners != null? gen.inners.length: 0; i < n; i++)
insert(h, gen.inners[i]); insert(h, gen.inners[i]);
} }
public LuaFunction load(Prototype p, String name, LuaValue globals) throws IOException { public LuaFunction load(Prototype p, String name, LuaValue globals) throws IOException {
String luaname = toStandardLuaFileName( name ); String luaname = toStandardLuaFileName(name);
String classname = toStandardJavaClassName( luaname ); String classname = toStandardJavaClassName(luaname);
JavaLoader loader = new JavaLoader(); JavaLoader loader = new JavaLoader();
return loader.load(p, classname, luaname, globals); return loader.load(p, classname, luaname, globals);
} }
private static String toStandardJavaClassName( String luachunkname ) { private static String toStandardJavaClassName(String luachunkname) {
String stub = toStub( luachunkname ); String stub = toStub(luachunkname);
StringBuffer classname = new StringBuffer(); StringBuffer classname = new StringBuffer();
for (int i = 0, n = stub.length(); i < n; ++i) { for (int i = 0, n = stub.length(); i < n; ++i) {
final char c = stub.charAt(i); final char c = stub.charAt(i);
classname.append((((i == 0) && Character.isJavaIdentifierStart(c)) || ((i > 0) && Character.isJavaIdentifierPart(c)))? c: '_'); classname.append(
(((i == 0) && Character.isJavaIdentifierStart(c)) || ((i > 0) && Character.isJavaIdentifierPart(c)))? c
: '_');
} }
return classname.toString(); return classname.toString();
} }
private static String toStandardLuaFileName( String luachunkname ) { private static String toStandardLuaFileName(String luachunkname) {
String stub = toStub( luachunkname ); String stub = toStub(luachunkname);
String filename = stub.replace('.','/')+".lua"; String filename = stub.replace('.', '/') + ".lua";
return filename.startsWith("@")? filename.substring(1): filename; return filename.startsWith("@")? filename.substring(1): filename;
} }
private static String toStub( String s ) { private static String toStub(String s) {
String stub = s.endsWith(".lua")? s.substring(0,s.length()-4): s; String stub = s.endsWith(".lua")? s.substring(0, s.length()-4): s;
return stub; return stub;
} }
} }

View File

@@ -15,39 +15,39 @@ import org.luaj.vm2.Upvaldesc;
*/ */
public class ProtoInfo { public class ProtoInfo {
public final String name; public final String name;
public final Prototype prototype; // the prototype that this info is about public final Prototype prototype; // the prototype that this info is about
public final ProtoInfo[] subprotos; // one per enclosed prototype, or null public final ProtoInfo[] subprotos; // one per enclosed prototype, or null
public final BasicBlock[] blocks; // basic block analysis of code branching public final BasicBlock[] blocks; // basic block analysis of code branching
public final BasicBlock[] blocklist; // blocks in breadth-first order public final BasicBlock[] blocklist; // blocks in breadth-first order
public final VarInfo[] params; // Parameters and initial values of stack variables public final VarInfo[] params; // Parameters and initial values of stack variables
public final VarInfo[][] vars; // Each variable public final VarInfo[][] vars; // Each variable
public final UpvalInfo[] upvals; // from outer scope public final UpvalInfo[] upvals; // from outer scope
public final UpvalInfo[][] openups; // per slot, upvalues allocated by this prototype public final UpvalInfo[][] openups; // per slot, upvalues allocated by this prototype
// A main chunk proto info. // A main chunk proto info.
public ProtoInfo(Prototype p, String name) { public ProtoInfo(Prototype p, String name) {
// For the outer chunk, we have one upvalue which is the environment. // For the outer chunk, we have one upvalue which is the environment.
this(p,name,null); this(p, name, null);
} }
private ProtoInfo(Prototype p, String name, UpvalInfo[] u) { private ProtoInfo(Prototype p, String name, UpvalInfo[] u) {
this.name = name; this.name = name;
this.prototype = p; this.prototype = p;
this.upvals = u != null? u: new UpvalInfo[] { new UpvalInfo(this) }; this.upvals = u != null? u: new UpvalInfo[] { new UpvalInfo(this) };
this.subprotos = p.p!=null&&p.p.length>0? new ProtoInfo[p.p.length]: null; this.subprotos = p.p != null && p.p.length > 0? new ProtoInfo[p.p.length]: null;
// find basic blocks // find basic blocks
this.blocks = BasicBlock.findBasicBlocks(p); this.blocks = BasicBlock.findBasicBlocks(p);
this.blocklist = BasicBlock.findLiveBlocks(blocks); this.blocklist = BasicBlock.findLiveBlocks(blocks);
// params are inputs to first block // params are inputs to first block
this.params = new VarInfo[p.maxstacksize]; this.params = new VarInfo[p.maxstacksize];
for ( int slot=0; slot<p.maxstacksize; slot++ ) { for (int slot = 0; slot < p.maxstacksize; slot++) {
VarInfo v = VarInfo.PARAM(slot); VarInfo v = VarInfo.PARAM(slot);
params[slot] = v; params[slot] = v;
} }
// find variables // find variables
this.vars = findVariables(); this.vars = findVariables();
replaceTrivialPhiVariables(); replaceTrivialPhiVariables();
@@ -59,36 +59,38 @@ public class ProtoInfo {
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
// prototpye name // prototpye name
sb.append( "proto '"+name+"'\n" ); sb.append("proto '" + name + "'\n");
// upvalues from outer scopes // upvalues from outer scopes
for ( int i=0, n=(upvals!=null? upvals.length: 0); i<n; i++ ) for (int i = 0, n = (upvals != null? upvals.length: 0); i < n; i++)
sb.append( " up["+i+"]: "+upvals[i]+"\n" ); sb.append(" up[" + i + "]: " + upvals[i] + "\n");
// basic blocks // basic blocks
for ( int i=0; i<blocklist.length; i++ ) { for (int i = 0; i < blocklist.length; i++) {
BasicBlock b = blocklist[i]; BasicBlock b = blocklist[i];
int pc0 = b.pc0; int pc0 = b.pc0;
sb.append( " block "+b.toString() ); sb.append(" block " + b.toString());
appendOpenUps( sb, -1 ); appendOpenUps(sb, -1);
// instructions // instructions
for ( int pc=pc0; pc<=b.pc1; pc++ ) { for (int pc = pc0; pc <= b.pc1; pc++) {
// open upvalue storage // open upvalue storage
appendOpenUps( sb, pc ); appendOpenUps(sb, pc);
// opcode // opcode
sb.append( " " ); sb.append(" ");
for ( int j=0; j<prototype.maxstacksize; j++ ) { for (int j = 0; j < prototype.maxstacksize; j++) {
VarInfo v = vars[j][pc]; VarInfo v = vars[j][pc];
String u = (v==null? "": v.upvalue!=null? !v.upvalue.rw? "[C] ": (v.allocupvalue&&v.pc==pc? "[*] ": "[] "): " "); String u = (v == null? ""
String s = v==null? "null ": String.valueOf(v); : v.upvalue != null? !v.upvalue.rw? "[C] ": (v.allocupvalue && v.pc == pc? "[*] ": "[] ")
sb.append( s+u); : " ");
String s = v == null? "null ": String.valueOf(v);
sb.append(s+u);
} }
sb.append( " " ); sb.append(" ");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ops = Print.ps; PrintStream ops = Print.ps;
Print.ps = new PrintStream(baos); Print.ps = new PrintStream(baos);
@@ -96,371 +98,383 @@ public class ProtoInfo {
Print.printOpCode(prototype, pc); Print.printOpCode(prototype, pc);
} finally { } finally {
Print.ps.close(); Print.ps.close();
Print.ps = ops; Print.ps = ops;
} }
sb.append( baos.toString() ); sb.append(baos.toString());
sb.append( "\n" ); sb.append("\n");
} }
} }
// nested functions // nested functions
for ( int i=0, n=subprotos!=null? subprotos.length: 0; i<n; i++ ) { for (int i = 0, n = subprotos != null? subprotos.length: 0; i < n; i++) {
sb.append( subprotos[i].toString() ); sb.append(subprotos[i].toString());
} }
return sb.toString(); return sb.toString();
} }
private void appendOpenUps(StringBuffer sb, int pc) { private void appendOpenUps(StringBuffer sb, int pc) {
for ( int j=0; j<prototype.maxstacksize; j++ ) { for (int j = 0; j < prototype.maxstacksize; j++) {
VarInfo v = (pc<0? params[j]: vars[j][pc]); VarInfo v = (pc < 0? params[j]: vars[j][pc]);
if ( v != null && v.pc == pc && v.allocupvalue ) { if (v != null && v.pc == pc && v.allocupvalue) {
sb.append( " open: "+v.upvalue+"\n" ); sb.append(" open: " + v.upvalue + "\n");
} }
} }
} }
private VarInfo[][] findVariables() { private VarInfo[][] findVariables() {
// create storage for variables. // create storage for variables.
int n = prototype.code.length; int n = prototype.code.length;
int m = prototype.maxstacksize; int m = prototype.maxstacksize;
VarInfo[][] v = new VarInfo[m][]; VarInfo[][] v = new VarInfo[m][];
for ( int i=0; i<v.length; i++ ) for (int i = 0; i < v.length; i++)
v[i] = new VarInfo[n]; v[i] = new VarInfo[n];
// process instructions // process instructions
for ( int bi=0; bi<blocklist.length; bi++ ) { for (int bi = 0; bi < blocklist.length; bi++) {
BasicBlock b0 = blocklist[bi]; BasicBlock b0 = blocklist[bi];
// input from previous blocks // input from previous blocks
int nprev = b0.prev!=null? b0.prev.length: 0; int nprev = b0.prev != null? b0.prev.length: 0;
for ( int slot=0; slot<m; slot++ ) { for (int slot = 0; slot < m; slot++) {
VarInfo var = null; VarInfo var = null;
if ( nprev == 0 ) if (nprev == 0)
var = params[slot]; var = params[slot];
else if ( nprev == 1 ) else if (nprev == 1)
var = v[slot][b0.prev[0].pc1]; var = v[slot][b0.prev[0].pc1];
else { else {
for ( int i=0; i<nprev; i++ ) { for (int i = 0; i < nprev; i++) {
BasicBlock bp = b0.prev[i]; BasicBlock bp = b0.prev[i];
if ( v[slot][bp.pc1] == VarInfo.INVALID ) if (v[slot][bp.pc1] == VarInfo.INVALID)
var = VarInfo.INVALID; var = VarInfo.INVALID;
} }
} }
if ( var == null ) if (var == null)
var = VarInfo.PHI(this, slot, b0.pc0); var = VarInfo.PHI(this, slot, b0.pc0);
v[slot][b0.pc0] = var; v[slot][b0.pc0] = var;
} }
// process instructions for this basic block // process instructions for this basic block
for ( int pc=b0.pc0; pc<=b0.pc1; pc++ ) { for (int pc = b0.pc0; pc <= b0.pc1; pc++) {
// propogate previous values except at block boundaries // propogate previous values except at block boundaries
if ( pc > b0.pc0 ) if (pc > b0.pc0)
propogateVars( v, pc-1, pc ); propogateVars(v, pc-1, pc);
int a,b,c; int a, b, c;
int ins = prototype.code[pc]; int ins = prototype.code[pc];
int op = Lua.GET_OPCODE(ins); int op = Lua.GET_OPCODE(ins);
// account for assignments, references and invalidations // account for assignments, references and invalidations
switch ( op ) { switch (op) {
case Lua.OP_LOADK:/* A Bx R(A) := Kst(Bx) */ case Lua.OP_LOADK:/* A Bx R(A) := Kst(Bx) */
case Lua.OP_LOADBOOL:/* A B C R(A) := (Bool)B; if (C) pc++ */ case Lua.OP_LOADBOOL:/* A B C R(A) := (Bool)B; if (C) pc++ */
case Lua.OP_GETUPVAL: /* A B R(A) := UpValue[B] */ case Lua.OP_GETUPVAL: /* A B R(A) := UpValue[B] */
case Lua.OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */ case Lua.OP_NEWTABLE: /* A B C R(A) := {} (size = B,C) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_MOVE:/* A B R(A) := R(B) */ case Lua.OP_MOVE:/* A B R(A) := R(B) */
case Lua.OP_UNM: /* A B R(A) := -R(B) */ case Lua.OP_UNM: /* A B R(A) := -R(B) */
case Lua.OP_NOT: /* A B R(A) := not R(B) */ case Lua.OP_NOT: /* A B R(A) := not R(B) */
case Lua.OP_LEN: /* A B R(A) := length of R(B) */ case Lua.OP_LEN: /* A B R(A) := length of R(B) */
case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
v[b][pc].isreferenced = true; v[b][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_ADD: /* A B C R(A) := RK(B) + RK(C) */ case Lua.OP_ADD: /* A B C R(A) := RK(B) + RK(C) */
case Lua.OP_SUB: /* A B C R(A) := RK(B) - RK(C) */ case Lua.OP_SUB: /* A B C R(A) := RK(B) - RK(C) */
case Lua.OP_MUL: /* A B C R(A) := RK(B) * RK(C) */ case Lua.OP_MUL: /* A B C R(A) := RK(B) * RK(C) */
case Lua.OP_DIV: /* A B C R(A) := RK(B) / RK(C) */ case Lua.OP_DIV: /* A B C R(A) := RK(B) / RK(C) */
case Lua.OP_MOD: /* A B C R(A) := RK(B) % RK(C) */ case Lua.OP_MOD: /* A B C R(A) := RK(B) % RK(C) */
case Lua.OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */ case Lua.OP_POW: /* A B C R(A) := RK(B) ^ RK(C) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
if (!Lua.ISK(b)) v[b][pc].isreferenced = true; if (!Lua.ISK(b))
if (!Lua.ISK(c)) v[c][pc].isreferenced = true; v[b][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); if (!Lua.ISK(c))
v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */ case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
if (!Lua.ISK(b)) v[b][pc].isreferenced = true; if (!Lua.ISK(b))
if (!Lua.ISK(c)) v[c][pc].isreferenced = true; v[b][pc].isreferenced = true;
if (!Lua.ISK(c))
v[c][pc].isreferenced = true;
break; break;
case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */ case Lua.OP_SETTABUP: /* A B C UpValue[A][RK(B)] := RK(C) */
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
if (!Lua.ISK(b)) v[b][pc].isreferenced = true; if (!Lua.ISK(b))
if (!Lua.ISK(c)) v[c][pc].isreferenced = true;
break;
case Lua.OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
a = Lua.GETARG_A( ins );
b = Lua.GETARG_B( ins );
c = Lua.GETARG_C( ins );
for ( ; b<=c; b++ )
v[b][pc].isreferenced = true; v[b][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); if (!Lua.ISK(c))
v[c][pc].isreferenced = true;
break; break;
case Lua.OP_CONCAT: /* A B C R(A) := R(B).. ... ..R(C) */
a = Lua.GETARG_A(ins);
b = Lua.GETARG_B(ins);
c = Lua.GETARG_C(ins);
for (; b <= c; b++)
v[b][pc].isreferenced = true;
v[a][pc] = new VarInfo(a, pc);
break;
case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2); pc+=sBx */ case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2); pc+=sBx */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
v[a+2][pc].isreferenced = true; v[a+2][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_GETTABLE: /* A B C R(A) := R(B)[RK(C)] */ case Lua.OP_GETTABLE: /* A B C R(A) := R(B)[RK(C)] */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
v[b][pc].isreferenced = true; v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true; if (!Lua.ISK(c))
v[a][pc] = new VarInfo(a,pc); v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */ case Lua.OP_GETTABUP: /* A B C R(A) := UpValue[B][RK(C)] */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
if (!Lua.ISK(c)) v[c][pc].isreferenced = true; if (!Lua.ISK(c))
v[a][pc] = new VarInfo(a,pc); v[c][pc].isreferenced = true;
v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ case Lua.OP_SELF: /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
v[b][pc].isreferenced = true; v[b][pc].isreferenced = true;
if (!Lua.ISK(c)) v[c][pc].isreferenced = true; if (!Lua.ISK(c))
v[a][pc] = new VarInfo(a,pc); v[c][pc].isreferenced = true;
v[a+1][pc] = new VarInfo(a+1,pc); v[a][pc] = new VarInfo(a, pc);
v[a+1][pc] = new VarInfo(a+1, pc);
break; break;
case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2); case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/ if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
v[a+2][pc].isreferenced = true; v[a+2][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
v[a+1][pc].isreferenced = true; v[a+1][pc].isreferenced = true;
v[a+3][pc] = new VarInfo(a+3,pc); v[a+3][pc] = new VarInfo(a+3, pc);
break; break;
case Lua.OP_LOADNIL: /* A B R(A) := ... := R(A+B) := nil */ case Lua.OP_LOADNIL: /* A B R(A) := ... := R(A+B) := nil */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
for ( ; b-->=0; a++ ) for (; b-- >= 0; a++)
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
for ( int j=1; j<b; j++, a++ ) for (int j = 1; j < b; j++, a++)
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
if ( b == 0 ) if (b == 0)
for ( ; a<m; a++ ) for (; a < m; a++)
v[a][pc] = VarInfo.INVALID; v[a][pc] = VarInfo.INVALID;
break; break;
case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
for ( int i=1; i<=b-1; i++ ) for (int i = 1; i <= b-1; i++)
v[a+i][pc].isreferenced = true; v[a+i][pc].isreferenced = true;
for ( int j=0; j<=c-2; j++, a++ ) for (int j = 0; j <= c-2; j++, a++)
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
for ( ; a<m; a++ ) for (; a < m; a++)
v[a][pc] = VarInfo.INVALID; v[a][pc] = VarInfo.INVALID;
break; break;
case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */ case Lua.OP_TFORCALL: /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
v[a++][pc].isreferenced = true; v[a++][pc].isreferenced = true;
v[a++][pc].isreferenced = true; v[a++][pc].isreferenced = true;
v[a++][pc].isreferenced = true; v[a++][pc].isreferenced = true;
for ( int j=0; j<c; j++, a++ ) for (int j = 0; j < c; j++, a++)
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
for ( ; a<m; a++ ) for (; a < m; a++)
v[a][pc] = VarInfo.INVALID; v[a][pc] = VarInfo.INVALID;
break; break;
case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */ case Lua.OP_TFORLOOP: /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
v[a+1][pc].isreferenced = true; v[a+1][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
break; break;
case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
for ( int i=1; i<=b-1; i++ ) for (int i = 1; i <= b-1; i++)
v[a+i][pc].isreferenced = true; v[a+i][pc].isreferenced = true;
break; break;
case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */ case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
for ( int i=0; i<=b-2; i++ ) for (int i = 0; i <= b-2; i++)
v[a+i][pc].isreferenced = true; v[a+i][pc].isreferenced = true;
break; break;
case Lua.OP_CLOSURE: { /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ case Lua.OP_CLOSURE: { /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_Bx( ins ); b = Lua.GETARG_Bx(ins);
Upvaldesc[] upvalues = prototype.p[b].upvalues; Upvaldesc[] upvalues = prototype.p[b].upvalues;
for (int k = 0, nups = upvalues.length; k < nups; ++k) for (int k = 0, nups = upvalues.length; k < nups; ++k)
if (upvalues[k].instack) if (upvalues[k].instack)
v[upvalues[k].idx][pc].isreferenced = true; v[upvalues[k].idx][pc].isreferenced = true;
v[a][pc] = new VarInfo(a,pc); v[a][pc] = new VarInfo(a, pc);
break; break;
} }
case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */ case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
for ( int i=1; i<=b; i++ ) for (int i = 1; i <= b; i++)
v[a+i][pc].isreferenced = true; v[a+i][pc].isreferenced = true;
break; break;
case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */ case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */
case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */ case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
v[a][pc].isreferenced = true; v[a][pc].isreferenced = true;
break; break;
case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
b = Lua.GETARG_B( ins ); b = Lua.GETARG_B(ins);
c = Lua.GETARG_C( ins ); c = Lua.GETARG_C(ins);
if (!Lua.ISK(b)) v[b][pc].isreferenced = true; if (!Lua.ISK(b))
if (!Lua.ISK(c)) v[c][pc].isreferenced = true; v[b][pc].isreferenced = true;
if (!Lua.ISK(c))
v[c][pc].isreferenced = true;
break; break;
case Lua.OP_JMP: /* sBx pc+=sBx */ case Lua.OP_JMP: /* sBx pc+=sBx */
a = Lua.GETARG_A( ins ); a = Lua.GETARG_A(ins);
if (a > 0) if (a > 0)
for ( --a; a<m; a++ ) for (--a; a < m; a++)
v[a][pc] = VarInfo.INVALID; v[a][pc] = VarInfo.INVALID;
break; break;
default: default:
throw new IllegalStateException("unhandled opcode: "+ins); throw new IllegalStateException("unhandled opcode: " + ins);
} }
} }
} }
return v; return v;
} }
private static void propogateVars(VarInfo[][] v, int pcfrom, int pcto) { private static void propogateVars(VarInfo[][] v, int pcfrom, int pcto) {
for ( int j=0, m=v.length; j<m; j++ ) for (int j = 0, m = v.length; j < m; j++)
v[j][pcto] = v[j][pcfrom]; v[j][pcto] = v[j][pcfrom];
} }
private void replaceTrivialPhiVariables() { private void replaceTrivialPhiVariables() {
for ( int i=0; i<blocklist.length; i++ ) { for (int i = 0; i < blocklist.length; i++) {
BasicBlock b0 = blocklist[i]; BasicBlock b0 = blocklist[i];
for ( int slot=0; slot<prototype.maxstacksize; slot++ ) { for (int slot = 0; slot < prototype.maxstacksize; slot++) {
VarInfo vold = vars[slot][b0.pc0]; VarInfo vold = vars[slot][b0.pc0];
VarInfo vnew = vold.resolvePhiVariableValues(); VarInfo vnew = vold.resolvePhiVariableValues();
if ( vnew != null ) if (vnew != null)
substituteVariable( slot, vold, vnew ); substituteVariable(slot, vold, vnew);
} }
} }
} }
private void substituteVariable(int slot, VarInfo vold, VarInfo vnew) { private void substituteVariable(int slot, VarInfo vold, VarInfo vnew) {
for ( int i=0, n=prototype.code.length; i<n; i++ ) for (int i = 0, n = prototype.code.length; i < n; i++)
replaceAll( vars[slot], vars[slot].length, vold, vnew ); replaceAll(vars[slot], vars[slot].length, vold, vnew);
} }
private void replaceAll(VarInfo[] v, int n, VarInfo vold, VarInfo vnew) { private void replaceAll(VarInfo[] v, int n, VarInfo vold, VarInfo vnew) {
for ( int i=0; i<n; i++ ) for (int i = 0; i < n; i++)
if ( v[i] == vold ) if (v[i] == vold)
v[i] = vnew; v[i] = vnew;
} }
private void findUpvalues() { private void findUpvalues() {
int[] code = prototype.code; int[] code = prototype.code;
int n = code.length; int n = code.length;
// propogate to inner prototypes // propogate to inner prototypes
String[] names = findInnerprotoNames(); String[] names = findInnerprotoNames();
for ( int pc=0; pc<n; pc++ ) { for (int pc = 0; pc < n; pc++) {
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE ) { if (Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE) {
int bx = Lua.GETARG_Bx(code[pc]); int bx = Lua.GETARG_Bx(code[pc]);
Prototype newp = prototype.p[bx]; Prototype newp = prototype.p[bx];
UpvalInfo[] newu = new UpvalInfo[newp.upvalues.length]; UpvalInfo[] newu = new UpvalInfo[newp.upvalues.length];
String newname = name + "$" + names[bx]; String newname = name + "$" + names[bx];
for ( int j=0; j<newp.upvalues.length; ++j ) { for (int j = 0; j < newp.upvalues.length; ++j) {
Upvaldesc u = newp.upvalues[j]; Upvaldesc u = newp.upvalues[j];
newu[j] = u.instack? findOpenUp(pc,u.idx) : upvals[u.idx]; newu[j] = u.instack? findOpenUp(pc, u.idx): upvals[u.idx];
} }
subprotos[bx] = new ProtoInfo(newp, newname, newu); subprotos[bx] = new ProtoInfo(newp, newname, newu);
} }
} }
// mark all upvalues that are written locally as read/write // mark all upvalues that are written locally as read/write
for ( int pc=0; pc<n; pc++ ) { for (int pc = 0; pc < n; pc++) {
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_SETUPVAL ) if (Lua.GET_OPCODE(code[pc]) == Lua.OP_SETUPVAL)
upvals[Lua.GETARG_B(code[pc])].rw = true; upvals[Lua.GETARG_B(code[pc])].rw = true;
} }
} }
private UpvalInfo findOpenUp(int pc, int slot) { private UpvalInfo findOpenUp(int pc, int slot) {
if ( openups[slot] == null ) if (openups[slot] == null)
openups[slot] = new UpvalInfo[prototype.code.length]; openups[slot] = new UpvalInfo[prototype.code.length];
if ( openups[slot][pc] != null ) if (openups[slot][pc] != null)
return openups[slot][pc]; return openups[slot][pc];
UpvalInfo u = new UpvalInfo(this, pc, slot); UpvalInfo u = new UpvalInfo(this, pc, slot);
for ( int i=0, n=prototype.code.length; i<n; ++i ) for (int i = 0, n = prototype.code.length; i < n; ++i)
if ( vars[slot][i] != null && vars[slot][i].upvalue == u ) if (vars[slot][i] != null && vars[slot][i].upvalue == u)
openups[slot][i] = u; openups[slot][i] = u;
return u; return u;
} }
public boolean isUpvalueAssign(int pc, int slot) { public boolean isUpvalueAssign(int pc, int slot) {
VarInfo v = pc<0? params[slot]: vars[slot][pc]; VarInfo v = pc < 0? params[slot]: vars[slot][pc];
return v != null && v.upvalue != null && v.upvalue.rw; return v != null && v.upvalue != null && v.upvalue.rw;
} }
public boolean isUpvalueCreate(int pc, int slot) { public boolean isUpvalueCreate(int pc, int slot) {
VarInfo v = pc<0? params[slot]: vars[slot][pc]; VarInfo v = pc < 0? params[slot]: vars[slot][pc];
return v != null && v.upvalue != null && v.upvalue.rw && v.allocupvalue && pc == v.pc; return v != null && v.upvalue != null && v.upvalue.rw && v.allocupvalue && pc == v.pc;
} }
public boolean isUpvalueRefer(int pc, int slot) { public boolean isUpvalueRefer(int pc, int slot) {
// special case when both refer and assign in same instruction // special case when both refer and assign in same instruction
if ( pc > 0 && vars[slot][pc] != null && vars[slot][pc].pc == pc && vars[slot][pc-1] != null ) if (pc > 0 && vars[slot][pc] != null && vars[slot][pc].pc == pc && vars[slot][pc-1] != null)
pc -= 1; pc -= 1;
VarInfo v = pc<0? params[slot]: vars[slot][pc]; VarInfo v = pc < 0? params[slot]: vars[slot][pc];
return v != null && v.upvalue != null && v.upvalue.rw; return v != null && v.upvalue != null && v.upvalue.rw;
} }
@@ -472,49 +486,49 @@ public class ProtoInfo {
public boolean isReadWriteUpvalue(UpvalInfo u) { public boolean isReadWriteUpvalue(UpvalInfo u) {
return u.rw; return u.rw;
} }
private String[] findInnerprotoNames() { private String[] findInnerprotoNames() {
if (prototype.p.length <= 0) if (prototype.p.length <= 0)
return null; return null;
// find all the prototype names // find all the prototype names
String[] names = new String[prototype.p.length]; String[] names = new String[prototype.p.length];
Hashtable used = new Hashtable(); Hashtable used = new Hashtable();
int[] code = prototype.code; int[] code = prototype.code;
int n = code.length; int n = code.length;
for ( int pc=0; pc<n; pc++ ) { for (int pc = 0; pc < n; pc++) {
if ( Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE ) { if (Lua.GET_OPCODE(code[pc]) == Lua.OP_CLOSURE) {
int bx = Lua.GETARG_Bx(code[pc]); int bx = Lua.GETARG_Bx(code[pc]);
String name = null; String name = null;
final int i = code[pc+1]; final int i = code[pc+1];
switch (Lua.GET_OPCODE(i)) { switch (Lua.GET_OPCODE(i)) {
case Lua.OP_SETTABLE: case Lua.OP_SETTABLE:
case Lua.OP_SETTABUP: { case Lua.OP_SETTABUP: {
final int b = Lua.GETARG_B(i); final int b = Lua.GETARG_B(i);
if (Lua.ISK(b)) if (Lua.ISK(b))
name = prototype.k[b&0x0ff].tojstring(); name = prototype.k[b & 0x0ff].tojstring();
break; break;
} }
case Lua.OP_SETUPVAL: { case Lua.OP_SETUPVAL: {
final int b = Lua.GETARG_B(i); final int b = Lua.GETARG_B(i);
final LuaString s = prototype.upvalues[b].name; final LuaString s = prototype.upvalues[b].name;
if (s != null) if (s != null)
name = s.tojstring(); name = s.tojstring();
break; break;
} }
default: // Local variable default: // Local variable
final int a = Lua.GETARG_A(code[pc]); final int a = Lua.GETARG_A(code[pc]);
final LuaString s = prototype.getlocalname(a+1, pc+1); final LuaString s = prototype.getlocalname(a+1, pc+1);
if (s != null) if (s != null)
name = s.tojstring(); name = s.tojstring();
break; break;
} }
name = name != null? toJavaClassPart(name): String.valueOf(bx); name = name != null? toJavaClassPart(name): String.valueOf(bx);
if (used.containsKey(name)) { if (used.containsKey(name)) {
String basename = name; String basename = name;
int count = 1; int count = 1;
do { do {
name = basename + '$' + count++; name = basename+'$'+count++;
} while (used.containsKey(name)); } while ( used.containsKey(name) );
} }
used.put(name, Boolean.TRUE); used.put(name, Boolean.TRUE);
names[bx] = name; names[bx] = name;
@@ -522,12 +536,12 @@ public class ProtoInfo {
} }
return names; return names;
} }
private static String toJavaClassPart(String s) { private static String toJavaClassPart(String s) {
final int n = s.length(); final int n = s.length();
StringBuffer sb = new StringBuffer(n); StringBuffer sb = new StringBuffer(n);
for (int i = 0; i < n; ++i) for (int i = 0; i < n; ++i)
sb.append( Character.isJavaIdentifierPart(s.charAt(i)) ? s.charAt(i): '_' ); sb.append(Character.isJavaIdentifierPart(s.charAt(i))? s.charAt(i): '_');
return sb.toString(); return sb.toString();
} }

View File

@@ -7,10 +7,10 @@ import org.luaj.vm2.Lua;
public class UpvalInfo { public class UpvalInfo {
ProtoInfo pi; // where defined ProtoInfo pi; // where defined
int slot; // where defined int slot; // where defined
int nvars; // number of vars involved int nvars; // number of vars involved
VarInfo var[]; // list of vars VarInfo var[]; // list of vars
boolean rw; // read-write boolean rw; // read-write
// Upval info representing the implied context containing only the environment. // Upval info representing the implied context containing only the environment.
public UpvalInfo(ProtoInfo pi) { public UpvalInfo(ProtoInfo pi) {
@@ -26,30 +26,30 @@ public class UpvalInfo {
this.slot = slot; this.slot = slot;
this.nvars = 0; this.nvars = 0;
this.var = null; this.var = null;
includeVarAndPosteriorVars( pi.vars[slot][pc] ); includeVarAndPosteriorVars(pi.vars[slot][pc]);
for ( int i=0; i<nvars; i++ ) for (int i = 0; i < nvars; i++)
var[i].allocupvalue = testIsAllocUpvalue( var[i] ); var[i].allocupvalue = testIsAllocUpvalue(var[i]);
this.rw = nvars > 1; this.rw = nvars > 1;
} }
private boolean includeVarAndPosteriorVars( VarInfo var ) { private boolean includeVarAndPosteriorVars(VarInfo var) {
if ( var == null || var == VarInfo.INVALID ) if (var == null || var == VarInfo.INVALID)
return false; return false;
if ( var.upvalue == this ) if (var.upvalue == this)
return true; return true;
var.upvalue = this; var.upvalue = this;
appendVar( var ); appendVar(var);
if ( isLoopVariable( var ) ) if (isLoopVariable(var))
return false; return false;
boolean loopDetected = includePosteriorVarsCheckLoops( var ); boolean loopDetected = includePosteriorVarsCheckLoops(var);
if ( loopDetected ) if (loopDetected)
includePriorVarsIgnoreLoops( var ); includePriorVarsIgnoreLoops(var);
return loopDetected; return loopDetected;
} }
private boolean isLoopVariable(VarInfo var) { private boolean isLoopVariable(VarInfo var) {
if ( var.pc >= 0 ) { if (var.pc >= 0) {
switch ( Lua.GET_OPCODE(pi.prototype.code[var.pc]) ) { switch (Lua.GET_OPCODE(pi.prototype.code[var.pc])) {
case Lua.OP_TFORLOOP: case Lua.OP_TFORLOOP:
case Lua.OP_FORLOOP: case Lua.OP_FORLOOP:
return true; return true;
@@ -58,25 +58,25 @@ public class UpvalInfo {
return false; return false;
} }
private boolean includePosteriorVarsCheckLoops( VarInfo prior ) { private boolean includePosteriorVarsCheckLoops(VarInfo prior) {
boolean loopDetected = false; boolean loopDetected = false;
for ( int i=0, n=pi.blocklist.length; i<n; i++ ) { for (int i = 0, n = pi.blocklist.length; i < n; i++) {
BasicBlock b = pi.blocklist[i]; BasicBlock b = pi.blocklist[i];
VarInfo v = pi.vars[slot][b.pc1]; VarInfo v = pi.vars[slot][b.pc1];
if ( v == prior ) { if (v == prior) {
for ( int j=0, m=b.next!=null? b.next.length: 0; j<m; j++ ) { for (int j = 0, m = b.next != null? b.next.length: 0; j < m; j++) {
BasicBlock b1 = b.next[j]; BasicBlock b1 = b.next[j];
VarInfo v1 = pi.vars[slot][b1.pc0]; VarInfo v1 = pi.vars[slot][b1.pc0];
if ( v1 != prior ) { if (v1 != prior) {
loopDetected |= includeVarAndPosteriorVars( v1 ); loopDetected |= includeVarAndPosteriorVars(v1);
if ( v1.isPhiVar() ) if (v1.isPhiVar())
includePriorVarsIgnoreLoops( v1 ); includePriorVarsIgnoreLoops(v1);
} }
} }
} else { } else {
for ( int pc=b.pc1-1; pc>=b.pc0; pc-- ) { for (int pc = b.pc1-1; pc >= b.pc0; pc--) {
if ( pi.vars[slot][pc] == prior ) { if (pi.vars[slot][pc] == prior) {
loopDetected |= includeVarAndPosteriorVars( pi.vars[slot][pc+1] ); loopDetected |= includeVarAndPosteriorVars(pi.vars[slot][pc+1]);
break; break;
} }
} }
@@ -84,22 +84,22 @@ public class UpvalInfo {
} }
return loopDetected; return loopDetected;
} }
private void includePriorVarsIgnoreLoops(VarInfo poster) { private void includePriorVarsIgnoreLoops(VarInfo poster) {
for ( int i=0, n=pi.blocklist.length; i<n; i++ ) { for (int i = 0, n = pi.blocklist.length; i < n; i++) {
BasicBlock b = pi.blocklist[i]; BasicBlock b = pi.blocklist[i];
VarInfo v = pi.vars[slot][b.pc0]; VarInfo v = pi.vars[slot][b.pc0];
if ( v == poster ) { if (v == poster) {
for ( int j=0, m=b.prev!=null? b.prev.length: 0; j<m; j++ ) { for (int j = 0, m = b.prev != null? b.prev.length: 0; j < m; j++) {
BasicBlock b0 = b.prev[j]; BasicBlock b0 = b.prev[j];
VarInfo v0 = pi.vars[slot][b0.pc1]; VarInfo v0 = pi.vars[slot][b0.pc1];
if ( v0 != poster ) if (v0 != poster)
includeVarAndPosteriorVars( v0 ); includeVarAndPosteriorVars(v0);
} }
} else { } else {
for ( int pc=b.pc0+1; pc<=b.pc1; pc++ ) { for (int pc = b.pc0+1; pc <= b.pc1; pc++) {
if ( pi.vars[slot][pc] == poster ) { if (pi.vars[slot][pc] == poster) {
includeVarAndPosteriorVars( pi.vars[slot][pc-1] ); includeVarAndPosteriorVars(pi.vars[slot][pc-1]);
break; break;
} }
} }
@@ -108,46 +108,46 @@ public class UpvalInfo {
} }
private void appendVar(VarInfo v) { private void appendVar(VarInfo v) {
if ( nvars == 0 ) { if (nvars == 0) {
var = new VarInfo[1]; var = new VarInfo[1];
} else if ( nvars+1 >= var.length ) { } else if (nvars+1 >= var.length) {
VarInfo[] s = var; VarInfo[] s = var;
var = new VarInfo[nvars*2+1]; var = new VarInfo[nvars*2+1];
System.arraycopy(s, 0, var, 0, nvars); System.arraycopy(s, 0, var, 0, nvars);
} }
var[nvars++] = v; var[nvars++] = v;
} }
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append( pi.name ); sb.append(pi.name);
for ( int i=0; i<nvars; i++ ) { for (int i = 0; i < nvars; i++) {
sb.append( i>0? ",": " " ); sb.append(i > 0? ",": " ");
sb.append( String.valueOf(var[i])); sb.append(String.valueOf(var[i]));
} }
if ( rw ) if (rw)
sb.append( "(rw)" ); sb.append("(rw)");
return sb.toString(); return sb.toString();
} }
private boolean testIsAllocUpvalue(VarInfo v) { private boolean testIsAllocUpvalue(VarInfo v) {
if ( v.pc < 0 ) if (v.pc < 0)
return true; return true;
BasicBlock b = pi.blocks[v.pc]; BasicBlock b = pi.blocks[v.pc];
if ( v.pc > b.pc0 ) if (v.pc > b.pc0)
return pi.vars[slot][v.pc-1].upvalue != this; return pi.vars[slot][v.pc-1].upvalue != this;
if ( b.prev == null ) { if (b.prev == null) {
v = pi.params[slot]; v = pi.params[slot];
if ( v != null && v.upvalue != this ) if (v != null && v.upvalue != this)
return true; return true;
} else { } else {
for ( int i=0, n=b.prev.length; i<n; i++ ) { for (int i = 0, n = b.prev.length; i < n; i++) {
v = pi.vars[slot][b.prev[i].pc1]; v = pi.vars[slot][b.prev[i].pc1];
if ( v != null && v.upvalue != this ) if (v != null && v.upvalue != this)
return true; return true;
} }
} }
return false; return false;
} }
} }

View File

@@ -24,10 +24,10 @@ public class VarInfo {
} }
public final int slot; // where assigned public final int slot; // where assigned
public final int pc; // where assigned, or -1 if for block inputs public final int pc; // where assigned, or -1 if for block inputs
public UpvalInfo upvalue; // not null if this var is an upvalue public UpvalInfo upvalue; // not null if this var is an upvalue
public boolean allocupvalue; // true if this variable allocates r/w upvalue public boolean allocupvalue; // true if this variable allocates r/w upvalue
// storage // storage
public boolean isreferenced; // true if this variable is refenced by some public boolean isreferenced; // true if this variable is refenced by some
// opcode // opcode
@@ -38,14 +38,16 @@ public class VarInfo {
} }
public String toString() { public String toString() {
return slot < 0 ? "x.x" : (slot + "." + pc); return slot < 0? "x.x": (slot + "." + pc);
} }
/** Return replacement variable if there is exactly one value possible, /**
* otherwise compute entire collection of variables and return null. * Return replacement variable if there is exactly one value possible,
* Computes the list of aall variable values, and saves it for the future. * otherwise compute entire collection of variables and return null.
* Computes the list of aall variable values, and saves it for the future.
* *
* @return new Variable to replace with if there is only one value, or null to leave alone. * @return new Variable to replace with if there is only one value, or null
* to leave alone.
*/ */
public VarInfo resolvePhiVariableValues() { public VarInfo resolvePhiVariableValues() {
return null; return null;
@@ -55,9 +57,7 @@ public class VarInfo {
vars.add(this); vars.add(this);
} }
public boolean isPhiVar() { public boolean isPhiVar() { return false; }
return false;
}
private static final class ParamVarInfo extends VarInfo { private static final class ParamVarInfo extends VarInfo {
private ParamVarInfo(int slot, int pc) { private ParamVarInfo(int slot, int pc) {
@@ -81,24 +81,22 @@ public class VarInfo {
private static final class PhiVarInfo extends VarInfo { private static final class PhiVarInfo extends VarInfo {
private final ProtoInfo pi; private final ProtoInfo pi;
VarInfo[] values; VarInfo[] values;
private PhiVarInfo(ProtoInfo pi, int slot, int pc) { private PhiVarInfo(ProtoInfo pi, int slot, int pc) {
super(slot, pc); super(slot, pc);
this.pi = pi; this.pi = pi;
} }
public boolean isPhiVar() { public boolean isPhiVar() { return true; }
return true;
}
public String toString() { public String toString() {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append( super.toString() ); sb.append(super.toString());
sb.append("={"); sb.append("={");
for (int i=0, n=(values!=null? values.length : 0); i<n; i++) { for (int i = 0, n = (values != null? values.length: 0); i < n; i++) {
if ( i>0 ) if (i > 0)
sb.append( "," ); sb.append(",");
sb.append(String.valueOf(values[i])); sb.append(String.valueOf(values[i]));
} }
sb.append("}"); sb.append("}");
@@ -119,7 +117,7 @@ public class VarInfo {
return v; return v;
} }
this.values = new VarInfo[n]; this.values = new VarInfo[n];
for ( int i=0; i<n; i++ ) { for (int i = 0; i < n; i++) {
this.values[i] = (VarInfo) it.next(); this.values[i] = (VarInfo) it.next();
this.values[i].isreferenced |= this.isreferenced; this.values[i].isreferenced |= this.isreferenced;
} }
@@ -128,17 +126,17 @@ public class VarInfo {
protected void collectUniqueValues(Set visitedBlocks, Set vars) { protected void collectUniqueValues(Set visitedBlocks, Set vars) {
BasicBlock b = pi.blocks[pc]; BasicBlock b = pi.blocks[pc];
if ( pc == 0 ) if (pc == 0)
vars.add(pi.params[slot]); vars.add(pi.params[slot]);
for (int i = 0, n = b.prev != null ? b.prev.length : 0; i < n; i++) { for (int i = 0, n = b.prev != null? b.prev.length: 0; i < n; i++) {
BasicBlock bp = b.prev[i]; BasicBlock bp = b.prev[i];
if (!visitedBlocks.contains(bp)) { if (!visitedBlocks.contains(bp)) {
visitedBlocks.add(bp); visitedBlocks.add(bp);
VarInfo v = pi.vars[slot][bp.pc1]; VarInfo v = pi.vars[slot][bp.pc1];
if ( v != null ) if (v != null)
v.collectUniqueValues(visitedBlocks, vars); v.collectUniqueValues(visitedBlocks, vars);
} }
} }
} }
} }
} }

View File

@@ -35,42 +35,42 @@ import org.luaj.vm2.lib.jse.CoerceJavaToLua;
* scripts using luaj. * scripts using luaj.
* *
* <p> * <p>
* This engine requires the types of the Bindings and ScriptContext to be * This engine requires the types of the Bindings and ScriptContext to be
* compatible with the engine. For creating new client context use * compatible with the engine. For creating new client context use
* ScriptEngine.createContext() which will return {@link LuajContext}, * ScriptEngine.createContext() which will return {@link LuajContext}, and for
* and for client bindings use the default engine scoped bindings or * client bindings use the default engine scoped bindings or construct a
* construct a {@link LuajBindings} directly. * {@link LuajBindings} directly.
*/ */
public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngine, Compilable { public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngine, Compilable {
private static final String __ENGINE_VERSION__ = Lua._VERSION;
private static final String __NAME__ = "Luaj";
private static final String __SHORT_NAME__ = "Luaj";
private static final String __LANGUAGE__ = "lua";
private static final String __LANGUAGE_VERSION__ = "5.2";
private static final String __ARGV__ = "arg";
private static final String __FILENAME__ = "?";
private static final ScriptEngineFactory myFactory = new LuaScriptEngineFactory();
private LuajContext context;
public LuaScriptEngine() { private static final String __ENGINE_VERSION__ = Lua._VERSION;
// set up context private static final String __NAME__ = "Luaj";
context = new LuajContext(); private static final String __SHORT_NAME__ = "Luaj";
context.setBindings(createBindings(), ScriptContext.ENGINE_SCOPE); private static final String __LANGUAGE__ = "lua";
setContext(context); private static final String __LANGUAGE_VERSION__ = "5.2";
private static final String __ARGV__ = "arg";
// set special values private static final String __FILENAME__ = "?";
put(LANGUAGE_VERSION, __LANGUAGE_VERSION__);
put(LANGUAGE, __LANGUAGE__); private static final ScriptEngineFactory myFactory = new LuaScriptEngineFactory();
put(ENGINE, __NAME__);
put(ENGINE_VERSION, __ENGINE_VERSION__); private LuajContext context;
put(ARGV, __ARGV__);
put(FILENAME, __FILENAME__); public LuaScriptEngine() {
put(NAME, __SHORT_NAME__); // set up context
put("THREADING", null); context = new LuajContext();
} context.setBindings(createBindings(), ScriptContext.ENGINE_SCOPE);
setContext(context);
// set special values
put(LANGUAGE_VERSION, __LANGUAGE_VERSION__);
put(LANGUAGE, __LANGUAGE__);
put(ENGINE, __NAME__);
put(ENGINE_VERSION, __ENGINE_VERSION__);
put(ARGV, __ARGV__);
put(FILENAME, __FILENAME__);
put(NAME, __SHORT_NAME__);
put("THREADING", null);
}
@Override @Override
public CompiledScript compile(String script) throws ScriptException { public CompiledScript compile(String script) throws ScriptException {
@@ -80,18 +80,18 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
@Override @Override
public CompiledScript compile(Reader script) throws ScriptException { public CompiledScript compile(Reader script) throws ScriptException {
try { try {
InputStream is = new Utf8Encoder(script); InputStream is = new Utf8Encoder(script);
try { try {
final Globals g = context.globals; final Globals g = context.globals;
final LuaFunction f = g.load(script, "script").checkfunction(); final LuaFunction f = g.load(script, "script").checkfunction();
return new LuajCompiledScript(f, g); return new LuajCompiledScript(f, g);
} catch ( LuaError lee ) { } catch (LuaError lee) {
throw new ScriptException(lee.getMessage() ); throw new ScriptException(lee.getMessage());
} finally { } finally {
is.close(); is.close();
} }
} catch ( Exception e ) { } catch (Exception e) {
throw new ScriptException("eval threw "+e.toString()); throw new ScriptException("eval threw " + e.toString());
} }
} }
@@ -116,49 +116,43 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
} }
@Override @Override
public Object eval(String script, ScriptContext context) public Object eval(String script, ScriptContext context) throws ScriptException {
throws ScriptException {
return eval(new StringReader(script), context); return eval(new StringReader(script), context);
} }
@Override @Override
public Object eval(Reader reader, ScriptContext context) public Object eval(Reader reader, ScriptContext context) throws ScriptException {
throws ScriptException { return compile(reader).eval(context);
return compile(reader).eval(context);
} }
@Override @Override
public ScriptEngineFactory getFactory() { public ScriptEngineFactory getFactory() { return myFactory; }
return myFactory;
}
class LuajCompiledScript extends CompiledScript { class LuajCompiledScript extends CompiledScript {
final LuaFunction function; final LuaFunction function;
final Globals compiling_globals; final Globals compiling_globals;
LuajCompiledScript(LuaFunction function, Globals compiling_globals) { LuajCompiledScript(LuaFunction function, Globals compiling_globals) {
this.function = function; this.function = function;
this.compiling_globals = compiling_globals; this.compiling_globals = compiling_globals;
} }
public ScriptEngine getEngine() { public ScriptEngine getEngine() { return LuaScriptEngine.this; }
return LuaScriptEngine.this;
public Object eval() throws ScriptException {
return eval(getContext());
} }
public Object eval() throws ScriptException { public Object eval(Bindings bindings) throws ScriptException {
return eval(getContext()); return eval(((LuajContext) getContext()).globals, bindings);
}
public Object eval(Bindings bindings) throws ScriptException {
return eval(((LuajContext) getContext()).globals, bindings);
}
public Object eval(ScriptContext context) throws ScriptException {
return eval(((LuajContext) context).globals, context.getBindings(ScriptContext.ENGINE_SCOPE));
} }
Object eval(Globals g, Bindings b) throws ScriptException { public Object eval(ScriptContext context) throws ScriptException {
g.setmetatable(new BindingsMetatable(b)); return eval(((LuajContext) context).globals, context.getBindings(ScriptContext.ENGINE_SCOPE));
}
Object eval(Globals g, Bindings b) throws ScriptException {
g.setmetatable(new BindingsMetatable(b));
LuaFunction f = function; LuaFunction f = function;
if (f.isclosure()) if (f.isclosure())
f = new LuaClosure(f.checkclosure().p, g); f = new LuaClosure(f.checkclosure().p, g);
@@ -178,37 +172,37 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
private final class Utf8Encoder extends InputStream { private final class Utf8Encoder extends InputStream {
private final Reader r; private final Reader r;
private final int[] buf = new int[2]; private final int[] buf = new int[2];
private int n; private int n;
private Utf8Encoder(Reader r) { private Utf8Encoder(Reader r) {
this.r = r; this.r = r;
} }
public int read() throws IOException { public int read() throws IOException {
if ( n > 0 ) if (n > 0)
return buf[--n]; return buf[--n];
int c = r.read(); int c = r.read();
if ( c < 0x80 ) if (c < 0x80)
return c; return c;
n = 0; n = 0;
if ( c < 0x800 ) { if (c < 0x800) {
buf[n++] = (0x80 | ( c & 0x3f)); buf[n++] = (0x80 | (c & 0x3f));
return (0xC0 | ((c>>6) & 0x1f)); return (0xC0 | ((c>>6) & 0x1f));
} else { } else {
buf[n++] = (0x80 | ( c & 0x3f)); buf[n++] = (0x80 | (c & 0x3f));
buf[n++] = (0x80 | ((c>>6) & 0x3f)); buf[n++] = (0x80 | ((c>>6) & 0x3f));
return (0xE0 | ((c>>12) & 0x0f)); return (0xE0 | ((c>>12) & 0x0f));
} }
} }
} }
static class BindingsMetatable extends LuaTable { static class BindingsMetatable extends LuaTable {
BindingsMetatable(final Bindings bindings) { BindingsMetatable(final Bindings bindings) {
this.rawset(LuaValue.INDEX, new TwoArgFunction() { this.rawset(LuaValue.INDEX, new TwoArgFunction() {
public LuaValue call(LuaValue table, LuaValue key) { public LuaValue call(LuaValue table, LuaValue key) {
if (key.isstring()) if (key.isstring())
return toLua(bindings.get(key.tojstring())); return toLua(bindings.get(key.tojstring()));
else else
return this.rawget(key); return this.rawget(key);
@@ -231,33 +225,38 @@ public class LuaScriptEngine extends AbstractScriptEngine implements ScriptEngin
}); });
} }
} }
static private LuaValue toLua(Object javaValue) { static private LuaValue toLua(Object javaValue) {
return javaValue == null? LuaValue.NIL: return javaValue == null? LuaValue.NIL
javaValue instanceof LuaValue? (LuaValue) javaValue: : javaValue instanceof LuaValue? (LuaValue) javaValue: CoerceJavaToLua.coerce(javaValue);
CoerceJavaToLua.coerce(javaValue);
} }
static private Object toJava(LuaValue luajValue) { static private Object toJava(LuaValue luajValue) {
switch ( luajValue.type() ) { switch (luajValue.type()) {
case LuaValue.TNIL: return null; case LuaValue.TNIL:
case LuaValue.TSTRING: return luajValue.tojstring(); return null;
case LuaValue.TUSERDATA: return luajValue.checkuserdata(Object.class); case LuaValue.TSTRING:
case LuaValue.TNUMBER: return luajValue.isinttype()? return luajValue.tojstring();
(Object) new Integer(luajValue.toint()): case LuaValue.TUSERDATA:
(Object) new Double(luajValue.todouble()); return luajValue.checkuserdata(Object.class);
default: return luajValue; case LuaValue.TNUMBER:
return luajValue.isinttype()? (Object) new Integer(luajValue.toint())
: (Object) new Double(luajValue.todouble());
default:
return luajValue;
} }
} }
static private Object toJava(Varargs v) { static private Object toJava(Varargs v) {
final int n = v.narg(); final int n = v.narg();
switch (n) { switch (n) {
case 0: return null; case 0:
case 1: return toJava(v.arg1()); return null;
case 1:
return toJava(v.arg1());
default: default:
Object[] o = new Object[n]; Object[] o = new Object[n];
for (int i=0; i<n; ++i) for (int i = 0; i < n; ++i)
o[i] = toJava(v.arg(i+1)); o[i] = toJava(v.arg(i+1));
return o; return o;
} }

View File

@@ -30,99 +30,74 @@ import javax.script.ScriptEngineFactory;
/** /**
* Jsr 223 scripting engine factory. * Jsr 223 scripting engine factory.
* *
* Exposes metadata to support the lua language, and constructs * Exposes metadata to support the lua language, and constructs instances of
* instances of LuaScriptEngine to handl lua scripts. * LuaScriptEngine to handl lua scripts.
*/ */
public class LuaScriptEngineFactory implements ScriptEngineFactory { public class LuaScriptEngineFactory implements ScriptEngineFactory {
private static final String [] EXTENSIONS = { private static final String[] EXTENSIONS = { "lua", ".lua", };
"lua",
".lua", private static final String[] MIMETYPES = { "text/lua", "application/lua" };
};
private static final String[] NAMES = { "lua", "luaj", };
private static final String [] MIMETYPES = {
"text/lua", private List<String> extensions;
"application/lua" private List<String> mimeTypes;
}; private List<String> names;
private static final String [] NAMES = { public LuaScriptEngineFactory() {
"lua", extensions = Arrays.asList(EXTENSIONS);
"luaj", mimeTypes = Arrays.asList(MIMETYPES);
}; names = Arrays.asList(NAMES);
}
private List<String> extensions;
private List<String> mimeTypes; public String getEngineName() { return getScriptEngine().get(ScriptEngine.ENGINE).toString(); }
private List<String> names;
public String getEngineVersion() { return getScriptEngine().get(ScriptEngine.ENGINE_VERSION).toString(); }
public LuaScriptEngineFactory() {
extensions = Arrays.asList(EXTENSIONS); public List<String> getExtensions() { return extensions; }
mimeTypes = Arrays.asList(MIMETYPES);
names = Arrays.asList(NAMES); public List<String> getMimeTypes() { return mimeTypes; }
}
public List<String> getNames() { return names; }
public String getEngineName() {
return getScriptEngine().get(ScriptEngine.ENGINE).toString(); public String getLanguageName() { return getScriptEngine().get(ScriptEngine.LANGUAGE).toString(); }
}
public String getLanguageVersion() { return getScriptEngine().get(ScriptEngine.LANGUAGE_VERSION).toString(); }
public String getEngineVersion() {
return getScriptEngine().get(ScriptEngine.ENGINE_VERSION).toString(); public Object getParameter(String key) {
} return getScriptEngine().get(key).toString();
}
public List<String> getExtensions() {
return extensions; public String getMethodCallSyntax(String obj, String m, String... args) {
} StringBuffer sb = new StringBuffer();
sb.append(obj + ":" + m + "(");
public List<String> getMimeTypes() { int len = args.length;
return mimeTypes; for (int i = 0; i < len; i++) {
} if (i > 0) {
sb.append(',');
public List<String> getNames() { }
return names; sb.append(args[i]);
} }
sb.append(")");
public String getLanguageName() { return sb.toString();
return getScriptEngine().get(ScriptEngine.LANGUAGE).toString(); }
}
public String getOutputStatement(String toDisplay) {
public String getLanguageVersion() { return "print(" + toDisplay + ")";
return getScriptEngine().get(ScriptEngine.LANGUAGE_VERSION).toString(); }
}
public String getProgram(String... statements) {
public Object getParameter(String key) { StringBuffer sb = new StringBuffer();
return getScriptEngine().get(key).toString(); int len = statements.length;
} for (int i = 0; i < len; i++) {
if (i > 0) {
public String getMethodCallSyntax(String obj, String m, String... args) { sb.append('\n');
StringBuffer sb = new StringBuffer(); }
sb.append(obj + ":" + m + "("); sb.append(statements[i]);
int len = args.length; }
for (int i = 0; i < len; i++) { return sb.toString();
if (i > 0) { }
sb.append(',');
} public ScriptEngine getScriptEngine() { return new LuaScriptEngine(); }
sb.append(args[i]);
}
sb.append(")");
return sb.toString();
}
public String getOutputStatement(String toDisplay) {
return "print(" + toDisplay + ")";
}
public String getProgram(String ... statements) {
StringBuffer sb = new StringBuffer();
int len = statements.length;
for (int i = 0; i < len; i++) {
if (i > 0) {
sb.append('\n');
}
sb.append(statements[i]);
}
return sb.toString();
}
public ScriptEngine getScriptEngine() {
return new LuaScriptEngine();
}
} }

View File

@@ -35,9 +35,9 @@ import org.luaj.vm2.Globals;
import org.luaj.vm2.lib.jse.JsePlatform; import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC; import org.luaj.vm2.luajc.LuaJC;
/** /**
* Context for LuaScriptEngine execution which maintains its own Globals, * Context for LuaScriptEngine execution which maintains its own Globals, and
* and manages the input and output redirection. * manages the input and output redirection.
*/ */
public class LuajContext extends SimpleScriptContext implements ScriptContext { public class LuajContext extends SimpleScriptContext implements ScriptContext {
@@ -50,93 +50,90 @@ public class LuajContext extends SimpleScriptContext implements ScriptContext {
private final PrintStream stdout; private final PrintStream stdout;
/** The initial value of globals.STDERR */ /** The initial value of globals.STDERR */
private final PrintStream stderr; private final PrintStream stderr;
/** Construct a LuajContext with its own globals which may /**
* be debug globals depending on the value of the system * Construct a LuajContext with its own globals which may be debug globals
* property 'org.luaj.debug' * depending on the value of the system property 'org.luaj.debug'
* <p> * <p>
* If the system property 'org.luaj.debug' is set, the globals * If the system property 'org.luaj.debug' is set, the globals created will
* created will be a debug globals that includes the debug * be a debug globals that includes the debug library. This may provide
* library. This may provide better stack traces, but may * better stack traces, but may have negative impact on performance.
* have negative impact on performance.
*/ */
public LuajContext() { public LuajContext() {
this("true".equals(System.getProperty("org.luaj.debug")), this("true".equals(System.getProperty("org.luaj.debug")), "true".equals(System.getProperty("org.luaj.luajc")));
"true".equals(System.getProperty("org.luaj.luajc")));
} }
/** Construct a LuajContext with its own globals, which /**
* which optionally are debug globals, and optionally use the * Construct a LuajContext with its own globals, which which optionally are
* luajc direct lua to java bytecode compiler. * debug globals, and optionally use the luajc direct lua to java bytecode
* compiler.
* <p> * <p>
* If createDebugGlobals is set, the globals * If createDebugGlobals is set, the globals created will be a debug globals
* created will be a debug globals that includes the debug * that includes the debug library. This may provide better stack traces,
* library. This may provide better stack traces, but may * but may have negative impact on performance.
* have negative impact on performance. *
* @param createDebugGlobals true to create debug globals, * @param createDebugGlobals true to create debug globals, false for
* false for standard globals. * standard globals.
* @param useLuaJCCompiler true to use the luajc compiler, * @param useLuaJCCompiler true to use the luajc compiler, reqwuires bcel
* reqwuires bcel to be on the class path. * to be on the class path.
*/ */
public LuajContext(boolean createDebugGlobals, boolean useLuaJCCompiler) { public LuajContext(boolean createDebugGlobals, boolean useLuaJCCompiler) {
globals = createDebugGlobals? globals = createDebugGlobals? JsePlatform.debugGlobals(): JsePlatform.standardGlobals();
JsePlatform.debugGlobals(): if (useLuaJCCompiler)
JsePlatform.standardGlobals(); LuaJC.install(globals);
if (useLuaJCCompiler) stdin = globals.STDIN;
LuaJC.install(globals); stdout = globals.STDOUT;
stdin = globals.STDIN; stderr = globals.STDERR;
stdout = globals.STDOUT;
stderr = globals.STDERR;
}
@Override
public void setErrorWriter(Writer writer) {
globals.STDERR = writer != null?
new PrintStream(new WriterOutputStream(writer)):
stderr;
} }
@Override @Override
public void setReader(Reader reader) { public void setErrorWriter(Writer writer) {
globals.STDIN = reader != null? globals.STDERR = writer != null? new PrintStream(new WriterOutputStream(writer)): stderr;
new ReaderInputStream(reader):
stdin;
} }
@Override
public void setReader(Reader reader) { globals.STDIN = reader != null? new ReaderInputStream(reader): stdin; }
@Override @Override
public void setWriter(Writer writer) { public void setWriter(Writer writer) {
globals.STDOUT = writer != null? globals.STDOUT = writer != null? new PrintStream(new WriterOutputStream(writer), true): stdout;
new PrintStream(new WriterOutputStream(writer), true):
stdout;
} }
static final class WriterOutputStream extends OutputStream { static final class WriterOutputStream extends OutputStream {
final Writer w; final Writer w;
WriterOutputStream(Writer w) { WriterOutputStream(Writer w) {
this.w = w; this.w = w;
} }
public void write(int b) throws IOException { public void write(int b) throws IOException {
w.write(new String(new byte[] {(byte)b})); w.write(new String(new byte[] { (byte) b }));
} }
public void write(byte[] b, int o, int l) throws IOException { public void write(byte[] b, int o, int l) throws IOException {
w.write(new String(b, o, l)); w.write(new String(b, o, l));
} }
public void write(byte[] b) throws IOException { public void write(byte[] b) throws IOException {
w.write(new String(b)); w.write(new String(b));
} }
public void close() throws IOException { public void close() throws IOException {
w.close(); w.close();
} }
public void flush() throws IOException { public void flush() throws IOException {
w.flush(); w.flush();
} }
} }
static final class ReaderInputStream extends InputStream { static final class ReaderInputStream extends InputStream {
final Reader r; final Reader r;
ReaderInputStream(Reader r) { ReaderInputStream(Reader r) {
this.r = r; this.r = r;
} }
public int read() throws IOException { public int read() throws IOException {
return r.read(); return r.read();
} }

Some files were not shown because too many files have changed in this diff Show More