[NOTHING CHANGED] Assorted fixes #48

Closed
asiekierka wants to merge 61 commits from asiekierka/master into master
413 changed files with 50531 additions and 21882 deletions
Showing only changes of commit 342d940b5e - Show all commits

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/core"/>
<classpathentry excluding="org/luaj/vm2/luajc/antlr/|org/luaj/vm2/luajc/lst/|org/luaj/vm2/luajc/JavaCodeGenerator.java" kind="src" path="src/jse"/>
<classpathentry kind="src" path="src/jme"/>
<classpathentry kind="src" path="test/java"/>
<classpathentry kind="src" path="test/junit"/>
<classpathentry kind="src" path="test/lua"/>
<classpathentry kind="src" path="examples/jse"/>
<classpathentry kind="src" path="examples/jme"/>
<classpathentry kind="src" path="examples/lua"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="lib" path="lib/midpapi20.jar"/>
<classpathentry kind="lib" path="lib/cldcapi11.jar"/>
<classpathentry kind="lib" path="lib/bcel-5.2.jar"/>
<classpathentry kind="var" path="JRE_LIB"/>
<classpathentry kind="output" path="bin"/>
</classpath>

16
.gitignore vendored
View File

@@ -1,14 +1,4 @@
bin/
target/ target/
build/ .classpath
jit/ .project
*.ser .settings/
*.gz
*.jar
*.lua
*.out
*.tar
*.txt
*.zip
docs
*.0

138
.ide/cleanup.xml Normal file
View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="2">
<profile kind="CleanUpProfile" name="Default" version="2">
<setting id="cleanup.array_with_curly" value="true"/>
<setting id="cleanup.use_autoboxing" value="true"/>
<setting id="cleanup.always_use_this_for_non_static_method_access" value="false"/>
<setting id="cleanup.remove_trailing_whitespaces_ignore_empty" value="false"/>
<setting id="cleanup.primitive_comparison" value="false"/>
<setting id="cleanup.system_property_file_encoding" value="true"/>
<setting id="cleanup.format_source_code_changes_only" value="false"/>
<setting id="cleanup.remove_redundant_semicolons" value="true"/>
<setting id="cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class" value="true"/>
<setting id="cleanup.useless_continue" value="true"/>
<setting id="cleanup.remove_redundant_type_arguments" value="true"/>
<setting id="cleanup.remove_unused_imports" value="true"/>
<setting id="cleanup.break_loop" value="false"/>
<setting id="cleanup.pull_up_assignment" value="false"/>
<setting id="cleanup.stringbuilder" value="false"/>
<setting id="cleanup.no_super" value="true"/>
<setting id="cleanup.arrays_fill" value="false"/>
<setting id="cleanup.use_lambda" value="true"/>
<setting id="cleanup.operand_factorization" value="false"/>
<setting id="cleanup.simplify_lambda_expression_and_method_ref" value="true"/>
<setting id="cleanup.always_use_blocks" value="true"/>
<setting id="cleanup.sort_members_all" value="false"/>
<setting id="cleanup.system_property_path_separator" value="true"/>
<setting id="cleanup.instanceof" value="false"/>
<setting id="cleanup.add_missing_annotations" value="true"/>
<setting id="cleanup.precompile_regex" value="false"/>
<setting id="cleanup.always_use_this_for_non_static_field_access" value="false"/>
<setting id="cleanup.boolean_literal" value="true"/>
<setting id="cleanup.always_use_parentheses_in_expressions" value="false"/>
<setting id="cleanup.sort_members" value="false"/>
<setting id="cleanup.remove_unused_local_variables" value="false"/>
<setting id="cleanup.add_missing_deprecated_annotations" value="true"/>
<setting id="cleanup.no_string_creation" value="true"/>
<setting id="cleanup.use_unboxing" value="false"/>
<setting id="cleanup.use_blocks_only_for_return_and_throw" value="false"/>
<setting id="cleanup.standard_comparison" value="false"/>
<setting id="cleanup.if_condition" value="true"/>
<setting id="cleanup.system_property_line_separator" value="true"/>
<setting id="cleanup.remove_trailing_whitespaces" value="true"/>
<setting id="cleanup.map_cloning" value="true"/>
<setting id="cleanup.add_serial_version_id" value="false"/>
<setting id="cleanup.try_with_resource" value="true"/>
<setting id="cleanup.use_this_for_non_static_method_access" value="false"/>
<setting id="cleanup.use_this_for_non_static_method_access_only_if_necessary" value="true"/>
<setting id="cleanup.single_used_field" value="false"/>
<setting id="cleanup.reduce_indentation" value="false"/>
<setting id="cleanup.primitive_parsing" value="false"/>
<setting id="cleanup.make_local_variable_final" value="false"/>
<setting id="cleanup.add_missing_methods" value="false"/>
<setting id="cleanup.qualify_static_member_accesses_with_declaring_class" value="true"/>
<setting id="cleanup.add_missing_override_annotations" value="true"/>
<setting id="cleanup.use_blocks" value="false"/>
<setting id="cleanup.multi_catch" value="true"/>
<setting id="cleanup.pull_out_if_from_if_else" value="true"/>
<setting id="cleanup.collection_cloning" value="true"/>
<setting id="cleanup.convert_to_enhanced_for_loop_if_loop_var_used" value="true"/>
<setting id="cleanup.make_variable_declarations_final" value="true"/>
<setting id="cleanup.redundant_comparator" value="false"/>
<setting id="cleanup.remove_unused_private_types" value="true"/>
<setting id="cleanup.system_property_boolean" value="true"/>
<setting id="cleanup.qualify_static_method_accesses_with_declaring_class" value="false"/>
<setting id="cleanup.organize_imports" value="true"/>
<setting id="cleanup.lazy_logical_operator" value="true"/>
<setting id="cleanup.bitwise_conditional_expression" value="false"/>
<setting id="cleanup.use_directly_map_method" value="false"/>
<setting id="cleanup.add_all" value="false"/>
<setting id="cleanup.system_property_file_separator" value="true"/>
<setting id="cleanup.qualify_static_field_accesses_with_declaring_class" value="false"/>
<setting id="cleanup.add_generated_serial_version_id" value="false"/>
<setting id="cleanup.controlflow_merge" value="false"/>
<setting id="cleanup.primitive_serialization" value="false"/>
<setting id="cleanup.comparing_on_criteria" value="true"/>
<setting id="cleanup.comparison_statement" value="true"/>
<setting id="cleanup.extract_increment" value="false"/>
<setting id="cleanup.insert_inferred_type_arguments" value="false"/>
<setting id="cleanup.make_private_fields_final" value="true"/>
<setting id="cleanup.useless_return" value="true"/>
<setting id="cleanup.instanceof_keyword" value="true"/>
<setting id="cleanup.use_this_for_non_static_field_access_only_if_necessary" value="true"/>
<setting id="cleanup.remove_trailing_whitespaces_all" value="true"/>
<setting id="cleanup.one_if_rather_than_duplicate_blocks_that_fall_through" value="true"/>
<setting id="cleanup.valueof_rather_than_instantiation" value="true"/>
<setting id="cleanup.plain_replacement" value="false"/>
<setting id="cleanup.remove_unnecessary_array_creation" value="true"/>
<setting id="cleanup.remove_private_constructors" value="true"/>
<setting id="cleanup.make_parameters_final" value="false"/>
<setting id="cleanup.substring" value="false"/>
<setting id="cleanup.ternary_operator" value="false"/>
<setting id="cleanup.merge_conditional_blocks" value="false"/>
<setting id="cleanup.return_expression" value="true"/>
<setting id="cleanup.system_property" value="true"/>
<setting id="cleanup.unlooped_while" value="true"/>
<setting id="cleanup.convert_to_enhanced_for_loop" value="true"/>
<setting id="cleanup.remove_unused_private_fields" value="true"/>
<setting id="cleanup.never_use_blocks" value="false"/>
<setting id="cleanup.remove_redundant_modifiers" value="true"/>
<setting id="cleanup.unreachable_block" value="false"/>
<setting id="cleanup.redundant_falling_through_block_end" value="false"/>
<setting id="cleanup.switch" value="false"/>
<setting id="cleanup.number_suffix" value="true"/>
<setting id="cleanup.remove_unnecessary_nls_tags" value="true"/>
<setting id="cleanup.convert_to_switch_expressions" value="false"/>
<setting id="cleanup.use_this_for_non_static_field_access" value="false"/>
<setting id="cleanup.static_inner_class" value="false"/>
<setting id="cleanup.use_string_is_blank" value="true"/>
<setting id="cleanup.add_missing_nls_tags" value="false"/>
<setting id="cleanup.qualify_static_member_accesses_through_instances_with_declaring_class" value="true"/>
<setting id="cleanup.remove_unnecessary_casts" value="true"/>
<setting id="cleanup.objects_equals" value="false"/>
<setting id="cleanup.convert_functional_interfaces" value="true"/>
<setting id="cleanup.format_source_code" value="true"/>
<setting id="cleanup.else_if" value="false"/>
<setting id="cleanup.boolean_value_rather_than_comparison" value="true"/>
<setting id="cleanup.add_default_serial_version_id" value="true"/>
<setting id="cleanup.remove_unused_private_methods" value="true"/>
<setting id="cleanup.make_type_abstract_if_missing_method" value="false"/>
<setting id="cleanup.join" value="false"/>
<setting id="cleanup.embedded_if" value="false"/>
<setting id="cleanup.use_anonymous_class_creation" value="false"/>
<setting id="cleanup.invert_equals" value="false"/>
<setting id="cleanup.add_missing_override_annotations_interface_methods" value="true"/>
<setting id="cleanup.remove_unused_private_members" value="false"/>
<setting id="cleanup.strictly_equal_or_different" value="true"/>
<setting id="cleanup.never_use_parentheses_in_expressions" value="true"/>
<setting id="cleanup.push_down_negation" value="false"/>
<setting id="cleanup.evaluate_nullable" value="false"/>
<setting id="cleanup.use_parentheses_in_expressions" value="true"/>
<setting id="cleanup.hash" value="false"/>
<setting id="cleanup.double_negation" value="true"/>
<setting id="cleanup.overridden_assignment" value="true"/>
<setting id="cleanup.primitive_rather_than_wrapper" value="true"/>
<setting id="cleanup.correct_indentation" value="false"/>
<setting id="cleanup.use_var" value="false"/>
</profile>
</profiles>

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

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>luaj-vm</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -858,37 +858,30 @@ An example skelton maven pom file for a skeleton project is in
<h2>Building the jars</h2> <h2>Building the jars</h2>
An ant file is included in the root directory which builds the libraries by default. Build the jars with maven.
<pre>
mvn clean verify
</pre>
<p> <p>
Other targets exist for creating distribution file an measuring code coverage of unit tests.
<h2>Unit tests</h2> <h2>Unit tests</h2>
<p> <p>
The main luaj JUnit tests are organized into a JUnit 3 suite: All unit tests are executed during the build.
<pre>
test/junit/org/luaj/vm2/AllTests.lua
</pre>
<p> <p>
Unit test scripts can be found in these locations Test scripts can be found in these locations
<pre> <pre>
test/lua/*.lua luaj-test/src/test/resources
test/lua/errors/*.lua
test/lua/perf/*.lua
test/lua/luaj3.0.2-tests.zip
</pre> </pre>
Executon is included in the build of luaj-test.
<h2>Code coverage</h2> <h2>Code coverage</h2>
<p> <p>
A build script for running unit tests and producing code coverage statistics is in The maven build creates the coverage report in the luaj-test/target/site folder
<pre> during the verify phase.
<a href="build-coverage.xml">build-coverage.xml</a>
</pre>
It relies on the cobertura code coverage library.
<h1>8 - <a name="8">Downloads</a></h1> <h1>8 - <a name="8">Downloads</a></h1>

View File

@@ -1,108 +0,0 @@
<!-- And build script to compile lua scripts into runnable jar files using the "luajc"
lua to java bytecode compiler.
Each source file is converted into a runnable jar file that takes arguments from the command line.
For example, the program test/lua/perf/binarytrees.lua is converted to a jar that can be run with
java -jar binarytrees.jar 15
-->
<project default="all">
<import file="build.xml"/>
<import file="build-libs.xml"/>
<available file="luaj-jse-${version}.jar" property="luaj.lib.exists"/>
<!-- this may need to be changed when building on mac -->
<property name="rt.jar" value="${java.home}/lib/rt.jar"/>
<target name="luaj-lib" unless="luaj.lib.exists">
<antcall target="jar-jse"/>
</target>
<macrodef name="perftest">
<attribute name="cmd"/>
<sequential>
<echo level="info">------ @{cmd}</echo>
<exec executable="bash">
<arg value="-c"/>
<arg value="time @{cmd}"/>
</exec>
</sequential>
</macrodef>
<macrodef name="buildappjar">
<attribute name="luaprog"/>
<attribute name="arg" default=""/>
<attribute name="srcdir" default="test/lua/perf"/>
<sequential>
<echo level="info">=========== @{srcdir}/@{luaprog} =============</echo>
<delete dir="build/@{luaprog}"/>
<mkdir dir="build/@{luaprog}/class"/>
<java classname="luajc">
<classpath>
<pathelement path="luaj-jse-${version}.jar"/>
<pathelement path="lib/bcel-5.2.jar"/>
</classpath>
<arg value="-s"/>
<arg path="@{srcdir}"/>
<arg value="-d"/>
<arg path="build/@{luaprog}/class"/>
<arg value="-m"/>
<arg value="-v"/>
<arg value="@{luaprog}.lua"/>
</java>
<jar destfile="build/@{luaprog}.jar">
<fileset dir="build/@{luaprog}/class"/>
<zipfileset includes="org/luaj/vm2/*.class,org/luaj/vm2/lib/*.class,org/luaj/vm2/lib/jse/*.class,org/luaj/vm2/compiler/*.class" src="luaj-jse-${version}.jar" />
<manifest>
<attribute name="Main-Class" value="@{luaprog}" />
</manifest>
</jar>
<unjar src="build/@{luaprog}.jar" dest="build/@{luaprog}/unjarred"/>
<perftest cmd="java -jar build/@{luaprog}.jar @{arg}"/>
<!-- The following can be adapted to produce an optimized jar.
<taskdef resource="proguard/ant/task.properties" classpath="lib/proguard.jar" />
<proguard>
-injars build/@{luaprog}.jar
-outjars build/@{luaprog}-opt.jar
-libraryjars ${rt.jar}
-overloadaggressively
-repackageclasses ''
-allowaccessmodification
-printmapping build/@{luaprog}.map
-keep public class @{luaprog} {
public static void main(java.lang.String[]);
}
</proguard>
<unjar src="build/@{luaprog}-opt.jar" dest="build/@{luaprog}/unjarred-opt"/>
<perftest cmd="java -jar build/@{luaprog}-opt.jar @{arg}"/>
-->
</sequential>
</macrodef>
<target name="binarytrees" depends="luaj-lib,proguard-lib">
<buildappjar luaprog="binarytrees" arg="15"/>
</target>
<target name="fannkuch" depends="luaj-lib,proguard-lib">
<buildappjar luaprog="fannkuch" arg="10"/>
</target>
<target name="nbody" depends="luaj-lib,proguard-lib">
<buildappjar luaprog="nbody" arg="1000000"/>
</target>
<target name="nsieve" depends="luaj-lib,proguard-lib">
<buildappjar luaprog="nsieve" arg="8"/>
</target>
<target name="swingapp" depends="luaj-lib,proguard-lib">
<buildappjar luaprog="swingapp" srcdir="examples/lua"/>
</target>
<target name="allappjars" depends="binarytrees,fannkuch,nbody,nsieve,swingapp"/>
<target name="all" depends="allappjars"/>
</project>

View File

@@ -1,116 +0,0 @@
<?xml version="1.0"?>
<project name="sample" default="main" basedir=".">
<property file="version.properties"/>
<!-- find libs -->
<import file="build-libs.xml"/>
<!-- main java class -->
<property name="java.dir" value="examples/jse"/>
<property name="java.name" value="SampleApplet"/>
<!-- main script -->
<property name="script.dir" value="examples/lua"/>
<property name="script.name" value="swingapplet"/>
<property name="image.name" value="logo.gif"/>
<!-- location of luaj jar -->
<property name="libs.dir" value="lib"/>
<property name="luaj.jar" value="${libs.dir}/luaj-jse-${version}.jar"/>
<!-- location of build results -->
<property name="build.dir" value="build/applet"/>
<target name="clean">
<delete failonerror="false" dir="${build.dir}"/>
</target>
<target name="dirs">
<mkdir dir="build/applet/classes"/>
</target>
<target name="classes" depends="dirs">
<copy todir="${build.dir}">
<fileset dir="${script.dir}" includes="${script.name}.lua,${image.name}"/>
</copy>
<javac destdir="${build.dir}/classes" source="1.4" target="1.4"
classpath="${luaj.jar}" srcdir="${java.dir}" includes="${java.name}.java"/>
</target>
<target name="manifest" depends="dirs">
<manifest file="${build.dir}/MANIFEST.MF">
<attribute name="Permissions" value="sandbox"/>
<attribute name="Main-class" value="${java.name}"/>
</manifest>
</target>
<target name="jar" depends="classes,manifest">
<jar destfile="${build.dir}/${script.name}.jar"
manifest="${build.dir}/MANIFEST.MF">
<fileset dir="${build.dir}" includes="*.lua"/>
<fileset dir="${build.dir}/classes"/>
<zipfileset
src="${luaj.jar}"
excludes="**/script/*,**/luajc/**,**/parser/**,**/ast/**,lua*"/>
</jar>
</target>
<target name="obf" depends="jar,proguard-lib">
<taskdef resource="proguard/ant/task.properties" classpath="lib/proguard.jar" />
<copy file="${build.dir}/${script.name}.jar"
tofile="${build.dir}/${script.name}-unobfuscated.jar"/>
<proguard>
-injars ${build.dir}/${script.name}-unobfuscated.jar
-outjars ${build.dir}/${script.name}.jar
-libraryjars ${java.home}/lib/rt.jar
-overloadaggressively
-repackageclasses ''
-allowaccessmodification
-printmapping ${build.dir}/mapping.txt
-keep public class * extends java.applet.Applet
-target 1.4
</proguard>
</target>
<target name="sign" depends="obf">
<signjar jar="${build.dir}/${script.name}.jar"
alias="${sign.alias}"
storepass="${sign.storepass}"
keypass="${sign.keypass}"
keystore="${sign.keystore}" />
</target>
<target name="html" depends="dirs">
<echoxml file="build/applet/LuajSampleApplet.html">
<html>
<head><title>Luaj Sample Applet</title></head>
<body>
<h1>Luaj Sample Applet</h1>
Requires browser that supports applets.
${script.name}
<applet archive='${script.name}.jar'
code='${java.name}.class'
width='800'
height='640' >
<param name='luaj.script' value='${script.name}.lua'/>
<param name="java_version" value="1.4+"/>
</applet>
</body>
</html>
</echoxml>
</target>
<target name="run" depends="jar,html">
<exec executable="open" spawn="true">
<arg value="-a Firefox"/>
<arg path="build/applet/LuajSampleApplet.html"/>
</exec>
</target>
<target name="all" depends="clean,sign,html,run"/>
<target name="main" depends="sign,html"/>
</project>

View File

@@ -1,119 +0,0 @@
<project default="all" xmlns:artifact="antlib:org.apache.maven.artifact.ant">
<!--
Run code coverage for unit tests on the luaj vm and libraries.
-->
<property name="classes.dir" value="build/classes-debug" />
<property name="instrumented.dir" value="build/instrumented" />
<property name="reports.xml.dir" value="build/reports-junit-xml" />
<property name="reports.html.dir" value="build/reports-junit-html" />
<property name="coverage.xml.dir" value="build/reports-coverage-xml" />
<property name="coverage.html.dir" value="build/reports-coverage-html" />
<property name="cobertura.serfile" value="cobertura.ser" />
<property name="cobertura.logfile" value="cobertura.log" />
<artifact:dependencies filesetId="cobutura.fileset">
<dependency groupId="net.sourceforge.cobertura" artifactId="cobertura" version="1.9.4.1"/>
<dependency groupId="junit" artifactId="junit" version="3.8.1"/>
</artifact:dependencies>
<path id="cobertura.classpath">
<fileset refid="cobutura.fileset" />
</path>
<taskdef classpathref="cobertura.classpath" resource="tasks.properties" />
<import file="wtk.xml"/>
<property environment="env"/>
<target name="clean" description="Remove all files created by the build/test process.">
<delete dir="${classes.dir}" failonerror="yes"/>
<delete dir="${instrumented.dir}" failonerror="yes"/>
<delete file="${cobertura.logfile}" />
<delete file="${cobertura.serfile}" />
</target>
<target name="init">
<ant antfile="build.xml" target="bcel-lib"/>
<ant antfile="build.xml" target="luaj1-lib"/>
<mkdir dir="${classes.dir}" />
<mkdir dir="${instrumented.dir}" />
<mkdir dir="${reports.xml.dir}" />
<mkdir dir="${reports.html.dir}" />
<mkdir dir="${coverage.xml.dir}" />
<mkdir dir="${coverage.html.dir}" />
</target>
<target name="compile" depends="init,wtk-or-fail">
<javac destdir="${classes.dir}" debug="yes" target="1.5">
<classpath refid="cobertura.classpath" />
<classpath refid="wtk-libs" />
<classpath path="lib/bcel-5.2.jar" />
<src path="src/core"/>
<src path="src/jme"/>
<src path="src/jse"/>
<src path="test/junit"/>
</javac>
</target>
<target name="instrument" depends="compile">
<delete file="${cobertura.serfile}"/>
<delete dir="${instrumented.dir}" failonerror="no"/>
<cobertura-instrument datafile="${cobertura.serfile}" todir="${instrumented.dir}">
<fileset dir="${classes.dir}">
<include name="org/luaj/vm2/*.class" />
<include name="org/luaj/vm2/lib/*.class" />
<include name="org/luaj/vm2/lib/jse/*.class" />
<include name="org/luaj/vm2/lib/jme/*.class" />
<include name="org/luaj/vm2/compiler/*.class" />
<include name="org/luaj/vm2/luajc/*.class" />
<include name="org/luaj/vm2/lua2java/*.class" />
<include name="org/luaj/vm2/parser/*.class" />
<include name="org/luaj/vm2/ast/*.class" />
<exclude name="**/*Test*.class" />
</fileset>
</cobertura-instrument>
</target>
<target name="test">
<junit fork="yes" dir="${basedir}" showoutput="yes">
<sysproperty key="net.sourceforge.cobertura.serfile"
file="${basedir}/${cobertura.serfile}" />
<classpath location="${instrumented.dir}" />
<classpath location="${classes.dir}" />
<classpath refid="cobertura.classpath" />
<classpath location="test/lua" />
<classpath location="test/junit/org/luaj/vm2/compiler" />
<classpath location="test/junit/org/luaj/vm2/vm1" />
<classpath path="lib/bcel-5.2.jar" />
<formatter type="xml" />
<batchtest todir="${reports.xml.dir}">
<fileset dir="test/junit">
<include name="org/luaj/vm2/AllTests.java" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${reports.xml.dir}">
<fileset dir="${reports.xml.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${reports.html.dir}" />
</junitreport>
</target>
<target name="report">
<cobertura-report datafile="${cobertura.serfile}" destdir="${coverage.xml.dir}" format="xml" />
<cobertura-report datafile="${cobertura.serfile}" destdir="${coverage.html.dir}">
<fileset dir="src/core"/>
<fileset dir="src/jse"/>
<fileset dir="src/jme"/>
</cobertura-report>
</target>
<target name="coverage" depends="clean,init,compile,instrument,test,report"/>
<target name="all" depends="coverage" />
</project>

View File

@@ -1,56 +0,0 @@
<project default="all-libs">
<available file="lib/midpapi20.jar" property="midpapi.lib.exists"/>
<available file="lib/bcel-5.2.jar" property="bcel.lib.exists"/>
<available file="lib/javacc.jar" property="javacc.lib.exists"/>
<available file="lib/proguard.jar" property="proguard.lib.exists"/>
<available file="lib/antenna-bin-1.2.0-beta.jar" property="antenna.lib.exists"/>
<available file="lib/junit.jar" property="junit.lib.exists"/>
<available file="lib/cobertura.jar" property="cobertura.lib.exists"/>
<available file="lib/microemulator.jar" property="microemulator.lib.exists"/>
<macrodef name="download">
<attribute name="zipname"/>
<attribute name="jars" default="**/*.jar"/>
<sequential>
<mkdir dir="lib"/>
<get src="http://luaj.sourceforge.net/lib/@{zipname}.tar.gz"
dest="lib/@{zipname}.tar.gz"/>
<gunzip src="lib/@{zipname}.tar.gz" dest="lib/@{zipname}.tar"/>
<untar src="lib/@{zipname}.tar" dest="lib" overwrite="true">
<patternset>
<include name="@{jars}"/>
</patternset>
<mapper type="flatten"/>
</untar>
</sequential>
</macrodef>
<target name="wtk-libs" unless="midpapi.lib.exists">
<download zipname="wtk-2.5.2-api"/>
</target>
<target name="bcel-lib" unless="bcel.lib.exists">
<download zipname="/bcel-5.2"/>
</target>
<target name="javacc-lib" unless="javacc.lib.exists">
<download zipname="javacc-5.0"/>
</target>
<target name="proguard-lib" unless="proguard.lib.exists">
<download zipname="proguard4.6"/>
</target>
<target name="antenna-lib" unless="antenna.lib.exists">
<download zipname="antenna-bin-1.2.0-beta"/>
</target>
<target name="junit-lib" unless="junit.lib.exists">
<download zipname="junit-3.8.2"/>
</target>
<target name="cobertura-lib" unless="cobertura.lib.exists">
<download zipname="cobertura-1.9.4.1-bin"/>
</target>
<target name="microemulator-lib" unless="microemulator.lib.exists">
<download zipname="microemulator-2.0.4" jars="**/microemulator.jar"/>
</target>
<target name="all-libs" depends="wtk-libs,bcel-lib,javacc-lib,proguard-lib,antenna-lib,junit-lib,cobertura-lib"/>
</project>

View File

@@ -1,176 +0,0 @@
<project default="usage">
<!-- Ant file to deploy to maven once the distribution is released on sourceforge.
-->
<property file="version.properties"/>
<macrodef name="write_pom">
<attribute name="platform"/>
<attribute name="snapshot" default=""/>
<sequential>
<mkdir dir="build/maven-${version}"/>
<echo file="build/maven-${version}/luaj-@{platform}-${version}@{snapshot}.pom"><![CDATA[<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.luaj</groupId>
<artifactId>luaj-]]>@{platform}<![CDATA[</artifactId>
<version>]]>${version}@{snapshot}<![CDATA[</version>
<packaging>jar</packaging>
<name>luaj-]]>@{platform}<![CDATA[</name>
<description>Luaj ]]>${version}<![CDATA[ for the ]]>@{platform}<![CDATA[ platform</description>
<url>http://sourceforge.net/projects/luaj/</url>
<licenses>
<license>
<name>MIT License</name>
<url>http://luaj.sourceforge.net/license.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>jrosebor</id>
<name>James Roseborough</name>
<email>jim.roseborough@luaj.org</email>
<timezone>-8</timezone>
<roles></roles>
</developer>
<developer>
<id>ifarmer</id>
<name>Ian Farmer</name>
<email>ian.farmer@luaj.org</email>
<timezone>-8</timezone>
<roles></roles>
</developer>
</developers>
<scm>
<url>http://luaj.cvs.sourceforge.net/viewvc/luaj/luaj-vm/</url>
</scm>
</project>
]]></echo>
</sequential>
</macrodef>
<macrodef name="prepare_files">
<attribute name="platform"/>
<attribute name="snapshot" default=""/>
<sequential>
<mkdir dir="build/maven-${version}"/>
<write_pom platform="@{platform}" snapshot="@{snapshot}"/>
<copy file="luaj-${version}.zip" todir="build/maven-${version}"/>
<unzip src="build/maven-${version}/luaj-${version}.zip" dest="build/maven-${version}"/>
<copy file="build/maven-${version}/luaj-${version}/lib/luaj-@{platform}-${version}.jar" todir="build/maven-${version}"/>
<!-- make a -sources file -->
<mkdir dir="build/maven-${version}/sources-@{platform}"/>
<copy todir="build/maven-${version}/sources-@{platform}">
<fileset dir="build/maven-${version}/luaj-${version}/src/core"/>
<fileset dir="build/maven-${version}/luaj-${version}/src/@{platform}"/>
<filterchain>
<tokenfilter><replacestring from='"Luaj 0.0"' to='"Luaj-@{platform} ${version}"'/></tokenfilter>
</filterchain>
</copy>
<zip destfile="build/maven-${version}/luaj-@{platform}-${version}-sources.jar"
basedir="build/maven-${version}/sources-@{platform}"/>
<!-- make a -javadoc file -->
<mkdir dir="build/maven-${version}/javadoc-@{platform}"/>
<javadoc defaultexcludes="yes"
destdir="build/maven-${version}/javadoc-@{platform}"
author="true"
version="true"
use="true"
windowtitle="Luaj API">
<fileset dir="build/maven-${version}/sources-@{platform}">
<include name="org/luaj/vm/*.java"/>
<include name="org/luaj/vm2/*.java"/>
<include name="org/luaj/vm2/server/*.java"/>
<include name="**/LuaC.java"/>
<include name="**/LuaJC.java"/>
<include name="**/lib/*/*.java"/>
</fileset>
<doctitle><![CDATA[<h1>Luaj API</h1>]]></doctitle>
<bottom><![CDATA[<i>Copyright &#169; 2007-2015 Luaj.org. All Rights Reserved.</i>]]></bottom>
<tag name="todo" scope="all" description="To do:"/>
<link offline="true" href="http://sourceforge.net/projects/luaj/" packagelistLoc="C:\tmp"/>
<link href="http://sourceforge.net/projects/luaj/"/>
</javadoc>
<zip destfile="build/maven-${version}/luaj-@{platform}-${version}-javadoc.jar"
basedir="build/maven-${version}/javadoc-@{platform}"/>
</sequential>
</macrodef>
<macrodef name="shapshot_files">
<attribute name="platform"/>
<sequential>
<exec executable="mvn">
<arg value="deploy:deploy-file"/>
<arg value="-Durl=https://oss.sonatype.org/content/repositories/snapshots"/>
<arg value="-DrepositoryId=nexus-releases"/>
<arg value="-DpomFile=build/maven-${version}/luaj-@{platform}-${version}-SNAPSHOT.pom"/>
<arg value="-Dfile=build/maven-${version}/luaj-@{platform}-${version}.jar"/>
<arg value="-Dsources=build/maven-${version}/luaj-@{platform}-${version}-sources.jar"/>
<arg value="-Djavadoc=build/maven-${version}/luaj-@{platform}-${version}-javadoc.jar"/>
</exec>
</sequential>
</macrodef>
<macrodef name="sign_and_deploy">
<attribute name="platform"/>
<sequential>
<exec executable="mvn">
<arg value="gpg:sign-and-deploy-file"/>
<arg value="-Durl=http://oss.sonatype.org/service/local/staging/deploy/maven2"/>
<arg value="-DrepositoryId=nexus-releases"/>
<arg value="-DpomFile=build/maven-${version}/luaj-@{platform}-${version}.pom"/>
<arg value="-Dfile=build/maven-${version}/luaj-@{platform}-${version}.jar"/>
<arg value="-Dsources=build/maven-${version}/luaj-@{platform}-${version}-sources.jar"/>
<arg value="-Djavadoc=build/maven-${version}/luaj-@{platform}-${version}-javadoc.jar"/>
</exec>
</sequential>
</macrodef>
<macrodef name="prepare_and_install">
<attribute name="platform"/>
<sequential>
<prepare_files platform="@{platform}"/>
<exec executable="mvn">
<arg value="install:install-file"/>
<arg value="-Dfile=build/maven-${version}/luaj-@{platform}-${version}.jar"/>
<arg value="-DpomFile=build/maven-${version}/luaj-@{platform}-${version}.pom"/>
</exec>
</sequential>
</macrodef>
<macrodef name="prepare_and_snapshot">
<attribute name="platform"/>
<sequential>
<prepare_files platform="@{platform}" snapshot="-SNAPSHOT"/>
<shapshot_files platform="@{platform}"/>
</sequential>
</macrodef>
<macrodef name="prepare_and_deploy">
<attribute name="platform"/>
<sequential>
<prepare_files platform="@{platform}"/>
<sign_and_deploy platform="@{platform}"/>
</sequential>
</macrodef>
<target name="install">
<prepare_and_install platform="jse"/>
<prepare_and_install platform="jme"/>
</target>
<target name="snapshot">
<prepare_and_snapshot platform="jse"/>
<prepare_and_snapshot platform="jme"/>
</target>
<target name="deploy">
<prepare_and_deploy platform="jse"/>
<prepare_and_deploy platform="jme"/>
</target>
<target name="usage">
<echo level="info">Usage: ant [-Dversion=${version}] -f build-maven.xml [install | shapshot | deploy]</echo>
</target>
</project>

View File

@@ -1,113 +0,0 @@
<?xml version="1.0"?>
<project name="sample" default="all" basedir=".">
<property file="version.properties"/>
<!-- find libs -->
<import file="build-libs.xml"/>
<!-- main script -->
<property name="script.name" value="hello"/>
<property name="script.dir" value="examples/lua"/>
<target name="clean">
<delete failonerror="false" dir="build"/>
</target>
<target name="dirs">
<mkdir dir="build"/>
<mkdir dir="build/tool"/>
<mkdir dir="build/classes"/>
</target>
<target name="tools" depends="dirs,bcel-lib,wtk-libs,microemulator-lib">
<javac destdir="build/tool" classpath="lib/bcel-5.2.jar">
<src path="src/core"/>
<src path="src/jse"/>
</javac>
</target>
<!-- compile script into lua bytecode -->
<target name="luac" depends="tools">
<java classname="luac" classpath="build/tool">
<arg line="-o build/classes/${script.name}.lua"/>
<arg line="${script.dir}/${script.name}.lua"/>
</java>
</target>
<!-- compile script into java bytecode -->
<target name="luajc" depends="tools,wtk-libs">
<java classname="luajc" classpath="build/tool:lib/bcel-5.2.jar">
<arg line="-verbose"/>
<arg line="-srcdir ${script.dir}"/>
<arg line="-destdir build/classes"/>
<arg line="${script.name}.lua"/>
</java>
</target>
<target name="classes" depends="dirs,wtk-libs">
<mkdir dir="build/midlet/src"/>
<copy todir="build/midlet/src">
<fileset dir="src/core"/>
<fileset dir="src/jme"/>
<fileset dir="examples/jme"/>
<filterchain>
<tokenfilter><replacestring from='"Luaj 0.0"' to='"Luaj-jme ${version}"'/></tokenfilter>
</filterchain>
</copy>
<path id="wtk-libs">
<pathelement path="lib/cldcapi11.jar"/>
<pathelement path="lib/midpapi20.jar"/>
<pathelement path="lib/mmapi.jar"/>
</path>
<javac destdir="build/classes" encoding="utf-8" source="1.3" target="1.2" bootclasspathref="wtk-libs"
srcdir="build/midlet/src"/>
</target>
<target name="jar" depends="luajc,classes">
<jar destfile="build/sample-plain.jar"
basedir="build/classes"/>
</target>
<target name="obf" depends="jar,proguard-lib">
<taskdef resource="proguard/ant/task.properties" classpath="lib/proguard.jar" />
<proguard>
-injars build/sample-plain.jar
-outjars build/sample.jar
-libraryjars lib/midpapi20.jar
-libraryjars lib/cldcapi11.jar
-overloadaggressively
-repackageclasses ''
-microedition
-keep public class SampleMIDlet
-keep public class * extends org.luaj.vm2.LuaValue
</proguard>
</target>
<target name="jad" depends="obf">
<length file="build/sample.jar" property="sample.jar.length" />
<echo level="info">Jar file length is ${sample.jar.length}</echo>
<manifest file="build/sample.jad">
<attribute name="Built-By" value="luaj-${version}"/>
<attribute name="MIDlet-Name" value="Luaj ${script.name}"/>
<attribute name="MIDlet-Version" value="${version}"/>
<attribute name="MIDlet-Vendor" value="luaj.org"/>
<attribute name="MIDlet-Description" value="Luaj Sample Midlet"/>
<attribute name="MIDlet-1" value="${script.name}-${version}, , SampleMIDlet"/>
<attribute name="MIDlet-Jar-URL" value="sample.jar"/>
<attribute name="MIDlet-Jar-Size" value="${sample.jar.length}"/>
<attribute name="script" value="${script.name}"/>
</manifest>
</target>
<target name="package" depends="jad,jar,obf"/>
<target name="run" depends="jad,jar,obf,microemulator-lib">
<java jar="lib/microemulator.jar" fork="true">
<arg path="build/sample.jad"/>
</java>
</target>
<target name="all" depends="clean,package,run"/>
</project>

View File

@@ -1,68 +0,0 @@
<project default="all">
<import file="build.xml"/>
<property name="lua.command" value="lua"/>
<available file="luaj-jse-${version}.jar" property="luaj.lib.exists"/>
<available file="lib/jill-1.0.1.jar" property="jill.lib.exists"/>
<available file="lib/kahlua.jar" property="kahlua.lib.exists"/>
<available file="lib/mochalua-1.0.jar" property="mochalua.lib.exists"/>
<target name="luaj-lib" unless="luaj.lib.exists">
<antcall target="jar-jse"/>
</target>
<target name="jill-lib" unless="jill.lib.exists">
<mkdir dir="lib"/>
<get src="http://jillcode.googlecode.com/files/jill-1.0.1.zip"
dest="lib/jill-1.0.1.zip"/>
<unzip src="lib/jill-1.0.1.zip" dest="lib" overwrite="true"/>
<ant dir="lib/jill-1.0.1" target="compile"/>
<jar destfile="lib/jill-1.0.1.jar" basedir="lib/jill-1.0.1/compiled"/>
</target>
<target name="kahlua-lib" unless="kahlua.lib.exists">
<get src="http://kahlua.googlecode.com/files/kahlua.jar"
dest="lib/kahlua.jar"/>
</target>
<target name="mochalua-lib" unless="mochalua.lib.exists">
<get src="http://mochalua.googlecode.com/files/Mochalua%201.0.jar"
dest="lib/mochalua-1.0.jar"/>
</target>
<target name="perf-libs" depends="luaj-lib,bcel-lib,jill-lib,kahlua-lib,mochalua-lib"/>
<macrodef name="perftest">
<attribute name="program" default="lua"/>
<attribute name="luaprog" default="fannkuch.lua 10"/>
<attribute name="basedir" default="test/lua/perf/"/>
<sequential>
<echo level="info">------ @{program} @{luaprog}</echo>
<exec executable="bash">
<arg value="-c"/>
<arg value="time @{program} @{basedir}@{luaprog}"/>
</exec>
</sequential>
</macrodef>
<macrodef name="testcase">
<attribute name="luaprog" default="fannkuch.lua 10"/>
<sequential>
<echo level="info">=========== @{luaprog} =============</echo>
<perftest program="java -version" luaprog="" basedir=""/>
<perftest program="${lua.command}" luaprog="@{luaprog}"/>
<perftest program="java -cp luaj-jse-${version}.jar lua -n" luaprog="@{luaprog}"/>
<perftest program="java -cp luaj-jse-${version}.jar${path.separator}lib/bcel-5.2.jar lua -b" luaprog="@{luaprog}"/>
</sequential>
</macrodef>
<target name="alltests">
<testcase luaprog="binarytrees.lua 15"/>
<testcase luaprog="fannkuch.lua 10"/>
<testcase luaprog="nbody.lua 1000000"/>
<testcase luaprog="nsieve.lua 9"/>
</target>
<target name="all" depends="alltests"/>
</project>

212
build.xml
View File

@@ -1,212 +0,0 @@
<project default="all">
<property file="version.properties"/>
<property name="jar.name.jme" value="luaj-jme-${version}.jar"/>
<property name="jar.name.jse" value="luaj-jse-${version}.jar"/>
<property name="jar.name.sources" value="luaj-sources-${version}.jar"/>
<target name="clean-build">
<delete dir="build"/>
</target>
<target name="clean" depends="clean-build">
<delete>
<fileset dir="." includes="luaj-*.jar"/>
</delete>
<delete dir="examples/android/bin"/>
<delete dir="examples/android/build"/>
<delete dir="examples/android/gen"/>
<delete dir="examples/android/libs"/>
<delete dir="examples/maven/target"/>
</target>
<import file="build-libs.xml"/>
<target name="parser" depends="javacc-lib">
<java classname="javacc" classpath="lib/javacc.jar">
<arg line="grammar/LuaParser.jj"/>
</java>
</target>
<target name="plain-parser" depends="javacc-lib">
<java dir="src/jse" fork="true" classname="javacc" classpath="lib/javacc.jar">
<arg line="../../grammar/Lua52.jj"/>
</java>
</target>
<target name="compile" depends="wtk-libs,bcel-lib">
<delete dir="build/jme/src"/>
<delete dir="build/jse/src"/>
<mkdir dir="build/jme/src"/>
<mkdir dir="build/jse/src"/>
<mkdir dir="build/jme/classes"/>
<mkdir dir="build/jse/classes"/>
<copy todir="build/jme/src">
<fileset dir="src/core"/>
<fileset dir="src/jme"/>
<filterchain>
<tokenfilter><replacestring from='"Luaj 0.0"' to='"Luaj-jme ${version}"'/></tokenfilter>
</filterchain>
</copy>
<copy todir="build/jse/src">
<fileset dir="src/core"/>
<filterchain>
<tokenfilter><replacestring from='"Luaj 0.0"' to='"Luaj-jse ${version}"'/></tokenfilter>
</filterchain>
</copy>
<copy todir="build/jse/src">
<fileset dir="src/jse"/>
<filterchain>
<tokenfilter><replacestring from='&lt;String&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Stat&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Exp&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Name&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Block&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;TableField&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;VarExp&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Exp.VarExp&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Object,String&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Double,String&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Integer,Integer&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Integer,LocalVariableGen&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;Exp,Integer&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;String,byte[]&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;String,Variable&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;LuaValue,String&gt;' to=''/></tokenfilter>
<tokenfilter><replacestring from='&lt;LuaString,String&gt;' to=''/></tokenfilter>
</filterchain>
</copy>
<path id="wtk-libs">
<pathelement path="lib/cldcapi11.jar"/>
<pathelement path="lib/midpapi20.jar"/>
<pathelement path="lib/mmapi.jar"/>
</path>
<javac destdir="build/jme/classes" encoding="utf-8" source="1.3" target="1.2" bootclasspathref="wtk-libs"
debug="on"
srcdir="build/jme/src"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.3" target="1.3"
classpath="lib/bcel-5.2.jar"
debug="on"
srcdir="build/jse/src"
excludes="**/script/*,**/Lua2Java*,**/server/*,lua*"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.5" target="1.5"
classpath="build/jse/classes"
debug="on"
srcdir="build/jse/src"
includes="**/script/*,**/Lua2Java*,**/server/*"/>
<javac destdir="build/jse/classes" encoding="utf-8" source="1.3" target="1.3"
classpath="build/jse/classes"
debug="on"
srcdir="build/jse/src"
includes="lua*"/>
</target>
<target name="jar-jme" depends="compile">
<jar destfile="${jar.name.jme}" basedir="build/jme/classes"/>
</target>
<target name="jar-jse" depends="compile">
<jar destfile="${jar.name.jse}">
<fileset dir="build/jse/classes"/>
<fileset dir="src/jse/">
<include name="META-INF/services/**"/>
</fileset>
</jar>
</target>
<target name="jar-jse-sources" depends="compile">
<jar destfile="${jar.name.sources}">
<fileset dir="build/jme/src"/>
<fileset dir="build/jse/src"/>
</jar>
</target>
<target name="doc">
<delete dir="docs/api"/>
<mkdir dir="docs/api"/>
<javadoc defaultexcludes="yes"
destdir="docs/api"
author="true"
version="true"
use="true"
windowtitle="Luaj API">
<fileset dir="src/core" defaultexcludes="yes" includes="org/luaj/vm2/*.java,org/luaj/vm2/compiler/LuaC.java,org/luaj/vm2/lib/*.java"/>
<fileset dir="src/jse" defaultexcludes="yes" includes="org/luaj/vm2/lib/jse/*.java,org/luaj/vm2/luajc/LuaJC.java,org/luaj/vm2/server/*.java"/>
<fileset dir="src/jme" defaultexcludes="yes" includes="org/luaj/vm2/lib/jme/*.java"/>
<doctitle><![CDATA[<h1>Luaj API</h1>]]></doctitle>
<bottom><![CDATA[<i>Copyright &#169; 2007-2015 Luaj.org. All Rights Reserved.</i>]]></bottom>
<tag name="todo" scope="all" description="To do:"/>
<group title="Core VM" packages="org.luaj.vm.*"/>
<link offline="true" href="http://sourceforge.net/projects/luaj/" packagelistLoc="C:\tmp"/>
<link href="http://sourceforge.net/projects/luaj/"/>
</javadoc>
</target>
<target name="dist" depends="all,doc">
<delete dir="build/luaj-${version}"/>
<mkdir dir="build/luaj-${version}/src"/>
<mkdir dir="build/luaj-${version}/lib"/>
<copy todir="build/luaj-${version}/src">
<fileset dir="src">
<exclude name="src/test/**"/>
<exclude name="**/antlr/**"/>
<exclude name="**/lst/**"/>
<exclude name="**/JavaCodeGenerator.java"/>
<exclude name="**/LuaJCompiler.java"/>
</fileset>
</copy>
<copy todir="build/luaj-${version}/test">
<fileset dir="test"/>
</copy>
<copy todir="build/luaj-${version}/examples">
<fileset dir="examples">
<include name="android/*.*"/>
<include name="android/assets/**"/>
<include name="android/res/**"/>
<include name="android/src/**"/>
<include name="jme/*.java"/>
<include name="jse/*.java"/>
<include name="lua/*.*"/>
<include name="maven/pom.xml"/>
<include name="maven/src/**"/>
</fileset>
</copy>
<copy todir="build/luaj-${version}/lib">
<fileset dir=".">
<include name="*-${version}.jar"/>
</fileset>
</copy>
<copy todir="build/luaj-${version}">
<fileset dir=".">
<include name="build.xml"/>
<include name="build-libs.xml"/>
<include name="build-coverage.xml"/>
<include name="version.properties"/>
<include name="wtk.xml"/>
<include name="README.html"/>
<include name="names.csv"/>
<include name=".classpath"/>
<include name=".project"/>
</fileset>
</copy>
<copy todir="build/luaj-${version}/docs">
<fileset dir="docs"/>
</copy>
<zip destfile="luaj-${version}.zip"
basedir="build" includes="luaj-${version}/**"/>
</target>
<target name="mvn_install" depends="jar-jse">
<exec executable="mvn">
<arg value="install:install-file"/>
<arg value="-Dfile=${jar.name.jse}"/>
<arg value="-DgroupId=org.luaj"/>
<arg value="-DartifactId=luaj-jse"/>
<arg value="-Dversion=${version}"/>
<arg value="-Dpackaging=jar"/>
</exec>
</target>
<target name="all" depends="clean,jar-jme,jar-jse,jar-jse-sources"/>
</project>

25
luaj-core/pom.xml Normal file
View File

@@ -0,0 +1,25 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.luaj</groupId>
<artifactId>luaj-parent</artifactId>
<version>3.0-SNAPSHOT</version>
</parent>
<artifactId>luaj-core</artifactId>
<name>luaj-core</name>
<description>Core code for LuaJ</description>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -21,16 +21,16 @@
******************************************************************************/ ******************************************************************************/
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
@@ -57,6 +57,7 @@ public final class Buffer {
/** /**
* Create buffer with default capacity * Create buffer with default capacity
*
* @see #DEFAULT_CAPACITY * @see #DEFAULT_CAPACITY
*/ */
public Buffer() { public Buffer() {
@@ -65,10 +66,11 @@ public final class Buffer {
/** /**
* 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;
@@ -76,6 +78,7 @@ public final class Buffer {
/** /**
* 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) {
@@ -86,6 +89,7 @@ public final class Buffer {
/** /**
* 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() {
@@ -94,6 +98,7 @@ public final class Buffer {
/** /**
* 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) {
@@ -105,15 +110,17 @@ public final class Buffer {
/** /**
* 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 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() {
@@ -122,125 +129,149 @@ public final class Buffer {
/** /**
* 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
*/ */
@Override
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 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 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 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 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 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 /**
* Reallocate the internal storage for the buffer
*
* @param newSize the size of the buffer to use * @param newSize the size of the buffer to use
* @param newOffset the offset to use * @param newOffset the offset to use
*/ */
private final void realloc( int newSize, int newOffset ) { private 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 *
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* globals.load( new StringReader("print 'hello'"), "main.lua" ).call(); * globals.load(new StringReader("print 'hello'"), "main.lua").call();
* } </pre> * }
* </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load( new BaseLib() ); * globals.load(new BaseLib());
* } </pre> * }
* </pre>
* *
* <h3>Loading and Executing Lua Code</h3> * <h3>Loading and Executing Lua Code</h3> Globals contains convenience
* Globals contains convenience functions to load and execute lua source code given a Reader. * functions to load and execute lua source code given a Reader. A simple
* A simple example is: * example is:
* <pre> {@code *
* <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
@@ -126,7 +147,9 @@ 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 */
@@ -135,18 +158,30 @@ public class Globals extends LuaTable {
/** 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;
} }
@@ -156,99 +191,142 @@ public class Globals extends LuaTable {
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.
*/
@Override
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 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.
* @param environment LuaTable to be used as the environment for the loaded function. * @param environment LuaTable to be used as the environment for the loaded
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * 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 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.
* @param environment LuaTable to be used as the environment for the loaded function. * @param environment LuaTable to be used as the environment for the loaded
* @return LuaValue that may be executed via .call(), .invoke(), or .method() calls. * 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. /**
* Load the content form an input stream as a binary chunk or text file.
*
* @param is InputStream containing a lua script or compiled lua" * @param is InputStream 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
* @param environment LuaTable to be used as the environment for the loaded function. * 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 +334,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
* 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 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 +364,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 +390,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())
@@ -320,21 +410,28 @@ public class Globals extends LuaTable {
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();
} }
@Override
public void close() throws IOException { public void close() throws IOException {
i = n; i = n;
} }
@Override
public int read() throws IOException { public int read() throws IOException {
return i < n ? s.charAt(i++) : -1; return i < n? s.charAt(i++): -1;
} }
@Override
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;
} }
} }
@@ -344,48 +441,66 @@ 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;
@Override
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++];
} }
@Override
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);
} }
@Override
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;
} }
@Override
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;
} }
@Override
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;
} }
@Override
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 +514,40 @@ 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;
} }
@Override
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;
} }
@Override
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 +560,29 @@ public class Globals extends LuaTable {
j += n; j += n;
return n; return n;
} }
@Override
public void close() throws IOException { public void close() throws IOException {
s.close(); s.close();
} }
@Override
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;
} }
} }
@Override
public boolean markSupported() { public boolean markSupported() {
return true; return true;
} }
@Override
public synchronized void reset() throws IOException { public synchronized void reset() throws IOException {
i = 0; i = 0;
} }

View File

@@ -25,83 +25,107 @@ 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 */ /**
* format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; 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 */ /**
* format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; 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;
@@ -113,7 +137,10 @@ public class LoadState {
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 */
@@ -122,11 +149,9 @@ public class LoadState {
/** 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;
@@ -161,126 +186,139 @@ public class LoadState {
/** 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 | 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;
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
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();
@@ -295,34 +333,34 @@ 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 = is.readByte() & 0xff;
f.upvalues[i] = new Upvaldesc(null, instack, idx); f.upvalues[i] = new Upvaldesc(null, instack, idx);
} }
} }
/** /**
* 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();
@@ -330,12 +368,13 @@ public class LoadState {
} }
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
@@ -366,44 +405,47 @@ public class LoadState {
/** /**
* 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 {
luacVersion = is.readByte(); luacVersion = is.readByte();
luacFormat = is.readByte(); luacFormat = is.readByte();
luacLittleEndian = (0 != is.readByte()); luacLittleEndian = 0 != is.readByte();
luacSizeofInt = is.readByte(); luacSizeofInt = is.readByte();
luacSizeofSizeT = is.readByte(); luacSizeofSizeT = is.readByte();
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,32 +453,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) @Override
throws IOException { public Prototype undump(InputStream stream, String chunkname) throws IOException {
return LoadState.undump(stream, chunkname); return LoadState.undump(stream, chunkname);
} }
} }

View File

@@ -22,7 +22,8 @@
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 */
@@ -36,6 +37,7 @@ public class LocVars {
/** /**
* 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
@@ -47,6 +49,6 @@ public class LocVars {
} }
public String tojstring() { public String tojstring() {
return varname+" "+startpc+"-"+endpc; return varname + " " + startpc + "-" + endpc;
} }
} }

View File

@@ -21,12 +21,11 @@
******************************************************************************/ ******************************************************************************/
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 */
@@ -54,46 +53,44 @@ 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;
public static final int MASK_B = ((1<<SIZE_B)-1)<<POS_B; public static final int MASK_B = (1<<SIZE_B)-1<<POS_B;
public static final int MASK_C = ((1<<SIZE_C)-1)<<POS_C; public static final int MASK_C = (1<<SIZE_C)-1<<POS_C;
public static final int MASK_Bx = ((1<<SIZE_Bx)-1)<<POS_Bx; public static final int MASK_Bx = (1<<SIZE_Bx)-1<<POS_Bx;
public static final int MASK_Ax = ((1<<SIZE_Ax)-1)<<POS_Ax; public static final int MASK_Ax = (1<<SIZE_Ax)-1<<POS_Ax;
public static final int MASK_NOT_OP = ~MASK_OP; public static final int MASK_NOT_OP = ~MASK_OP;
public static final int MASK_NOT_A = ~MASK_A; public static final int MASK_NOT_A = ~MASK_A;
@@ -105,72 +102,68 @@ 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) {
return 0 != ((x) & BITRK); return 0 != (x & BITRK);
} }
/** 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 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
** Kst(x) - constant (in constant table) ** Kst(x) - constant (in constant table)
** 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,10 +171,10 @@ 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] */
@@ -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; // >
@@ -263,7 +256,6 @@ public class Lua {
(*) 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
@@ -280,62 +272,66 @@ public class Lua {
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

@@ -24,16 +24,16 @@ 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
@@ -57,46 +57,56 @@ public final class LuaBoolean extends LuaValue {
this.v = b; this.v = b;
} }
@Override
public int type() { public int type() {
return LuaValue.TBOOLEAN; return LuaValue.TBOOLEAN;
} }
@Override
public String typename() { public String typename() {
return "boolean"; return "boolean";
} }
@Override
public boolean isboolean() { public boolean isboolean() {
return true; return true;
} }
@Override
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() {
return v; return v;
} }
@Override
public boolean toboolean() { public boolean toboolean() {
return v; return v;
} }
@Override
public String tojstring() { public String tojstring() {
return v ? "true" : "false"; return v? "true": "false";
} }
@Override
public boolean optboolean(boolean defval) { public boolean optboolean(boolean defval) {
return this.v; return this.v;
} }
@Override
public boolean checkboolean() { public boolean checkboolean() {
return v; return v;
} }
@Override
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 *
* <pre>
* {
* &#64;code
* String script = "print( 'hello, world' )"; * String script = "print( 'hello, world' )";
* InputStream is = new ByteArrayInputStream(script.getBytes()); * InputStream is = new ByteArrayInputStream(script.getBytes());
* Prototype p = LuaC.instance.compile(is, "script"); * Prototype p = LuaC.instance.compile(is, "script");
* LuaValue globals = JsePlatform.standardGlobals(); * LuaValue globals = JsePlatform.standardGlobals();
* LuaClosure f = new LuaClosure(p, globals); * LuaClosure f = new LuaClosure(p, globals);
* f.call(); * f.call();
* }</pre> * }
* </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:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* LuaFunction f = globals.load(new StringReader(script), "script"); * LuaFunction f = globals.load(new StringReader(script), "script");
* LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed. * LuaClosure c = f.checkclosure(); // This may fail if LuaJC is installed.
* c.call(); * c.call();
* }</pre> * }
* </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()
@@ -84,7 +97,7 @@ import org.luaj.vm2.lib.DebugLib.CallFrame;
* @see Globals#compiler * @see Globals#compiler
*/ */
public class LuaClosure extends LuaFunction { public class LuaClosure extends LuaFunction {
private static final UpValue[] NOUPVALUES = new UpValue[0]; private static final UpValue[] NOUPVALUES = {};
public final Prototype p; public final Prototype p;
@@ -92,8 +105,11 @@ public class LuaClosure extends LuaFunction {
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
* prototype has upvalues, the environment will be written into the first
* upvalue.
*
* @param p the Prototype to construct this Closure for. * @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.
*/ */
@@ -103,28 +119,32 @@ public class LuaClosure extends LuaFunction {
globals = env instanceof Globals? (Globals) env: null; globals = env instanceof Globals? (Globals) env: null;
} }
@Override
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);
} }
} }
@Override
public boolean isclosure() { public boolean isclosure() {
return true; return true;
} }
@Override
public LuaClosure optclosure(LuaClosure defval) { public LuaClosure optclosure(LuaClosure defval) {
return this; return this;
} }
@Override
public LuaClosure checkclosure() { public LuaClosure checkclosure() {
return this; return this;
} }
@Override
public String tojstring() { public String tojstring() {
return "function: " + p.toString(); return "function: " + p.toString();
} }
@@ -136,52 +156,77 @@ public class LuaClosure extends LuaFunction {
return stack; return stack;
} }
@Override
public final LuaValue call() { public final LuaValue call() {
LuaValue[] stack = getNewStack(); LuaValue[] stack = getNewStack();
return execute(stack,NONE).arg1(); return execute(stack, NONE).arg1();
} }
@Override
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();
} }
} }
@Override
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();
} }
} }
@Override
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();
} }
} }
@Override
public final Varargs invoke(Varargs varargs) { public final Varargs invoke(Varargs varargs) {
return onInvoke(varargs).eval(); return onInvoke(varargs).eval();
} }
@Override
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;
@@ -189,24 +234,24 @@ public class LuaClosure extends LuaFunction {
// 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];
@@ -221,20 +266,20 @@ public class LuaClosure extends LuaFunction {
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;
@@ -243,15 +288,16 @@ public class LuaClosure extends LuaFunction {
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) */
@@ -259,40 +305,47 @@ public class LuaClosure extends LuaFunction {
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) */
@@ -309,11 +362,10 @@ public class LuaClosure extends LuaFunction {
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 {
@@ -325,7 +377,7 @@ public class LuaClosure extends LuaFunction {
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;
@@ -334,93 +386,123 @@ public class LuaClosure extends LuaFunction {
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; pc += (i>>>14)-0x1ffff;
} }
} }
@@ -429,19 +511,19 @@ public class LuaClosure extends LuaFunction {
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 */
{ {
LuaValue init = stack[a].checknumber("'for' initial value must be a number"); 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 limit = stack[a+1].checknumber("'for' limit must be a number");
LuaValue step = stack[a + 2].checknumber("'for' step must be a number"); LuaValue step = stack[a+2].checknumber("'for' step must be a number");
stack[a] = init.sub(step); stack[a] = init.sub(step);
stack[a + 1] = limit; stack[a+1] = limit;
stack[a + 2] = step; stack[a+2] = step;
pc += (i>>>14)-0x1ffff; 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;
@@ -455,22 +537,22 @@ public class LuaClosure extends LuaFunction {
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;
@@ -480,7 +562,7 @@ public class LuaClosure extends LuaFunction {
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 */
@@ -492,11 +574,11 @@ public class LuaClosure extends LuaFunction {
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 +590,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();
@@ -528,20 +610,20 @@ 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,13 +639,13 @@ 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;
@@ -590,9 +672,9 @@ public class LuaClosure extends LuaFunction {
upValues[i].setValue(v); upValues[i].setValue(v);
} }
@Override
public String name() { public String name() {
return "<"+p.shortsource()+":"+p.linedefined+">"; return "<" + p.shortsource() + ":" + p.linedefined + ">";
} }
} }

View File

@@ -21,22 +21,21 @@
******************************************************************************/ ******************************************************************************/
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;
@@ -51,9 +50,11 @@ public class LuaError extends RuntimeException {
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.
*/ */
@Override
public String getMessage() { public String getMessage() {
if (traceback != null) if (traceback != null)
return traceback; return traceback;
@@ -65,24 +66,29 @@ 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. /**
* Construct LuaError when a program exception occurs.
* <p> * <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;
} }
@@ -93,38 +99,38 @@ public class LuaError extends RuntimeException {
* @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. * Get the cause, if any.
*/ */
public Throwable getCause() { @Override
return cause; public Throwable getCause() { return cause; }
}
} }

View File

@@ -21,71 +21,86 @@
******************************************************************************/ ******************************************************************************/
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;
@Override
public int type() { public int type() {
return TFUNCTION; return TFUNCTION;
} }
@Override
public String typename() { public String typename() {
return "function"; return "function";
} }
@Override
public boolean isfunction() { public boolean isfunction() {
return true; return true;
} }
@Override
public LuaFunction checkfunction() { public LuaFunction checkfunction() {
return this; return this;
} }
@Override
public LuaFunction optfunction(LuaFunction defval) { public LuaFunction optfunction(LuaFunction defval) {
return this; return this;
} }
@Override
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
@Override
public String tojstring() { public String tojstring() {
return "function: " + classnamestub(); return "function: " + classnamestub();
} }
@Override
public LuaString strvalue() { public LuaString strvalue() {
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

@@ -0,0 +1,372 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import org.luaj.vm2.lib.MathLib;
/**
* Extension of {@link LuaNumber} which can hold a Java int as its value.
* <p>
* These instance are not instantiated directly by clients, but indirectly via
* the static functions {@link LuaValue#valueOf(int)} or
* {@link LuaValue#valueOf(double)} functions. This ensures that policies
* regarding pooling of instances are encapsulated.
* <p>
* There are no API's specific to LuaInteger that are useful beyond what is
* already exposed in {@link LuaValue}.
*
* @see LuaValue
* @see LuaNumber
* @see LuaDouble
* @see LuaValue#valueOf(int)
* @see LuaValue#valueOf(double)
*/
public class LuaInteger extends LuaNumber {
private static final LuaInteger[] intValues = new LuaInteger[512];
static {
for (int i = 0; i < 512; i++)
intValues[i] = new LuaInteger(i-256);
}
public static LuaInteger valueOf(int i) {
return i <= 255 && i >= -256? intValues[i+256]: new LuaInteger(i);
}
// TODO consider moving this to LuaValue
/**
* Return a LuaNumber that represents the value provided
*
* @param l long value to represent.
* @return LuaNumber that is eithe LuaInteger or LuaDouble representing l
* @see LuaValue#valueOf(int)
* @see LuaValue#valueOf(double)
*/
public static LuaNumber valueOf(long l) {
int i = (int) l;
return l == i? i <= 255 && i >= -256? intValues[i+256]: (LuaNumber) new LuaInteger(i)
: (LuaNumber) LuaDouble.valueOf(l);
}
/** The value being held by this instance. */
public final int v;
/**
* Package protected constructor.
*
* @see LuaValue#valueOf(int)
**/
LuaInteger(int i) {
this.v = i;
}
@Override
public boolean isint() { return true; }
@Override
public boolean isinttype() { return true; }
@Override
public boolean islong() { return true; }
@Override
public byte tobyte() { return (byte) v; }
@Override
public char tochar() { return (char) v; }
@Override
public double todouble() { return v; }
@Override
public float tofloat() { return v; }
@Override
public int toint() { return v; }
@Override
public long tolong() { return v; }
@Override
public short toshort() { return (short) v; }
@Override
public double optdouble(double defval) { return v; }
@Override
public int optint(int defval) { return v; }
@Override
public LuaInteger optinteger(LuaInteger defval) { return this; }
@Override
public long optlong(long defval) { return v; }
@Override
public String tojstring() {
return Integer.toString(v);
}
@Override
public LuaString strvalue() {
return LuaString.valueOf(Integer.toString(v));
}
@Override
public LuaString optstring(LuaString defval) {
return LuaString.valueOf(Integer.toString(v));
}
@Override
public LuaValue tostring() {
return LuaString.valueOf(Integer.toString(v));
}
@Override
public String optjstring(String defval) {
return Integer.toString(v);
}
@Override
public LuaInteger checkinteger() {
return this;
}
@Override
public boolean isstring() {
return true;
}
@Override
public int hashCode() {
return v;
}
public static int hashCode(int x) {
return x;
}
// unary operators
@Override
public LuaValue neg() { return valueOf(-(long) v); }
// object equality, used for key comparison
@Override
public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger) o).v == v: false; }
// equality w/ metatable processing
@Override
public LuaValue eq(LuaValue val) { return val.raweq(v)? TRUE: FALSE; }
@Override
public boolean eq_b(LuaValue val) { return val.raweq(v); }
// equality w/o metatable processing
@Override
public boolean raweq(LuaValue val) { return val.raweq(v); }
@Override
public boolean raweq(double val) { return v == val; }
@Override
public boolean raweq(int val) { return v == val; }
// arithmetic operators
@Override
public LuaValue add(LuaValue rhs) { return rhs.add(v); }
@Override
public LuaValue add(double lhs) { return LuaDouble.valueOf(lhs+v); }
@Override
public LuaValue add(int lhs) { return LuaInteger.valueOf(lhs+(long) v); }
@Override
public LuaValue sub(LuaValue rhs) { return rhs.subFrom(v); }
@Override
public LuaValue sub(double rhs) { return LuaDouble.valueOf(v-rhs); }
@Override
public LuaValue sub(int rhs) { return LuaValue.valueOf(v-rhs); }
@Override
public LuaValue subFrom(double lhs) { return LuaDouble.valueOf(lhs-v); }
@Override
public LuaValue subFrom(int lhs) { return LuaInteger.valueOf(lhs-(long) v); }
@Override
public LuaValue mul(LuaValue rhs) { return rhs.mul(v); }
@Override
public LuaValue mul(double lhs) { return LuaDouble.valueOf(lhs*v); }
@Override
public LuaValue mul(int lhs) { return LuaInteger.valueOf(lhs*(long) v); }
@Override
public LuaValue pow(LuaValue rhs) { return rhs.powWith(v); }
@Override
public LuaValue pow(double rhs) { return MathLib.dpow(v, rhs); }
@Override
public LuaValue pow(int rhs) { return MathLib.dpow(v, rhs); }
@Override
public LuaValue powWith(double lhs) { return MathLib.dpow(lhs, v); }
@Override
public LuaValue powWith(int lhs) { return MathLib.dpow(lhs, v); }
@Override
public LuaValue div(LuaValue rhs) { return rhs.divInto(v); }
@Override
public LuaValue div(double rhs) { return LuaDouble.ddiv(v, rhs); }
@Override
public LuaValue div(int rhs) { return LuaDouble.ddiv(v, rhs); }
@Override
public LuaValue divInto(double lhs) { return LuaDouble.ddiv(lhs, v); }
@Override
public LuaValue mod(LuaValue rhs) { return rhs.modFrom(v); }
@Override
public LuaValue mod(double rhs) { return LuaDouble.dmod(v, rhs); }
@Override
public LuaValue mod(int rhs) { return LuaDouble.dmod(v, rhs); }
@Override
public LuaValue modFrom(double lhs) { return LuaDouble.dmod(lhs, v); }
// relational operators
@Override
public LuaValue lt(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v)? TRUE: FALSE: super.lt(rhs); }
@Override
public LuaValue lt(double rhs) { return v < rhs? TRUE: FALSE; }
@Override
public LuaValue lt(int rhs) { return v < rhs? TRUE: FALSE; }
@Override
public boolean lt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gt_b(v): super.lt_b(rhs); }
@Override
public boolean lt_b(int rhs) { return v < rhs; }
@Override
public boolean lt_b(double rhs) { return v < rhs; }
@Override
public LuaValue lteq(LuaValue rhs) {
return rhs instanceof LuaNumber? rhs.gteq_b(v)? TRUE: FALSE: super.lteq(rhs);
}
@Override
public LuaValue lteq(double rhs) { return v <= rhs? TRUE: FALSE; }
@Override
public LuaValue lteq(int rhs) { return v <= rhs? TRUE: FALSE; }
@Override
public boolean lteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.gteq_b(v): super.lteq_b(rhs); }
@Override
public boolean lteq_b(int rhs) { return v <= rhs; }
@Override
public boolean lteq_b(double rhs) { return v <= rhs; }
@Override
public LuaValue gt(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v)? TRUE: FALSE: super.gt(rhs); }
@Override
public LuaValue gt(double rhs) { return v > rhs? TRUE: FALSE; }
@Override
public LuaValue gt(int rhs) { return v > rhs? TRUE: FALSE; }
@Override
public boolean gt_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lt_b(v): super.gt_b(rhs); }
@Override
public boolean gt_b(int rhs) { return v > rhs; }
@Override
public boolean gt_b(double rhs) { return v > rhs; }
@Override
public LuaValue gteq(LuaValue rhs) {
return rhs instanceof LuaNumber? rhs.lteq_b(v)? TRUE: FALSE: super.gteq(rhs);
}
@Override
public LuaValue gteq(double rhs) { return v >= rhs? TRUE: FALSE; }
@Override
public LuaValue gteq(int rhs) { return v >= rhs? TRUE: FALSE; }
@Override
public boolean gteq_b(LuaValue rhs) { return rhs instanceof LuaNumber? rhs.lteq_b(v): super.gteq_b(rhs); }
@Override
public boolean gteq_b(int rhs) { return v >= rhs; }
@Override
public boolean gteq_b(double rhs) { return v >= rhs; }
// string comparison
@Override
public int strcmp(LuaString rhs) { typerror("attempt to compare number with string"); return 0; }
@Override
public int checkint() {
return v;
}
@Override
public long checklong() {
return v;
}
@Override
public double checkdouble() {
return v;
}
@Override
public String checkjstring() {
return String.valueOf(v);
}
@Override
public LuaString checkstring() {
return valueOf(String.valueOf(v));
}
}

View File

@@ -24,16 +24,16 @@ 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
*/ */
@@ -45,64 +45,104 @@ public class LuaNil extends LuaValue {
LuaNil() {} LuaNil() {}
@Override
public int type() { public int type() {
return LuaValue.TNIL; return LuaValue.TNIL;
} }
@Override
public String toString() { public String toString() {
return "nil"; return "nil";
} }
@Override
public String typename() { public String typename() {
return "nil"; return "nil";
} }
@Override
public String tojstring() { public String tojstring() {
return "nil"; return "nil";
} }
@Override
public LuaValue not() { public LuaValue not() {
return LuaValue.TRUE; return LuaValue.TRUE;
} }
@Override
public boolean toboolean() { public boolean toboolean() {
return false; return false;
} }
@Override
public boolean isnil() { public boolean isnil() {
return true; return true;
} }
@Override
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
@Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof LuaNil; return o instanceof LuaNil;
} }
@Override
public LuaValue checknotnil() { public LuaValue checknotnil() {
return argerror("value"); return argerror("value");
} }
@Override
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
@Override
public boolean optboolean(boolean defval) { return defval; } public boolean optboolean(boolean defval) { return defval; }
@Override
public LuaClosure optclosure(LuaClosure defval) { return defval; } public LuaClosure optclosure(LuaClosure defval) { return defval; }
@Override
public double optdouble(double defval) { return defval; } public double optdouble(double defval) { return defval; }
@Override
public LuaFunction optfunction(LuaFunction defval) { return defval; } public LuaFunction optfunction(LuaFunction defval) { return defval; }
@Override
public int optint(int defval) { return defval; } public int optint(int defval) { return defval; }
@Override
public LuaInteger optinteger(LuaInteger defval) { return defval; } public LuaInteger optinteger(LuaInteger defval) { return defval; }
@Override
public long optlong(long defval) { return defval; } public long optlong(long defval) { return defval; }
@Override
public LuaNumber optnumber(LuaNumber defval) { return defval; } public LuaNumber optnumber(LuaNumber defval) { return defval; }
@Override
public LuaTable opttable(LuaTable defval) { return defval; } public LuaTable opttable(LuaTable defval) { return defval; }
@Override
public LuaThread optthread(LuaThread defval) { return defval; } public LuaThread optthread(LuaThread defval) { return defval; }
@Override
public String optjstring(String defval) { return defval; } public String optjstring(String defval) { return defval; }
@Override
public LuaString optstring(LuaString defval) { return defval; } public LuaString optstring(LuaString defval) { return defval; }
@Override
public Object optuserdata(Object defval) { return defval; } public Object optuserdata(Object defval) { return defval; }
@Override
public Object optuserdata(Class c, Object defval) { return defval; } public Object optuserdata(Class c, Object defval) { return defval; }
@Override
public LuaValue optvalue(LuaValue defval) { return defval; } public LuaValue optvalue(LuaValue defval) { return defval; }
} }

View File

@@ -24,58 +24,74 @@ 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;
@Override
public int type() { public int type() {
return TNUMBER; return TNUMBER;
} }
@Override
public String typename() { public String typename() {
return "number"; return "number";
} }
@Override
public LuaNumber checknumber() { public LuaNumber checknumber() {
return this; return this;
} }
@Override
public LuaNumber checknumber(String errmsg) { public LuaNumber checknumber(String errmsg) {
return this; return this;
} }
@Override
public LuaNumber optnumber(LuaNumber defval) { public LuaNumber optnumber(LuaNumber defval) {
return this; return this;
} }
@Override
public LuaValue tonumber() { public LuaValue tonumber() {
return this; return this;
} }
@Override
public boolean isnumber() { public boolean isnumber() {
return true; return true;
} }
@Override
public boolean isstring() { public boolean isstring() {
return true; return true;
} }
@Override
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
@Override
public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); } public LuaValue concat(LuaValue rhs) { return rhs.concatTo(this); }
@Override
public Buffer concat(Buffer rhs) { return rhs.concatTo(this); } public Buffer concat(Buffer rhs) { return rhs.concatTo(this); }
@Override
public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); } public LuaValue concatTo(LuaNumber lhs) { return strvalue().concatTo(lhs.strvalue()); }
@Override
public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); } public LuaValue concatTo(LuaString lhs) { return strvalue().concatTo(lhs); }
} }

View File

@@ -21,43 +21,41 @@
******************************************************************************/ ******************************************************************************/
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> * <p>
* The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and * The utility classes {@link org.luaj.vm2.lib.jse.JsePlatform} and
* {@link org.luaj.vm2.lib.jme.JmePlatform} * {@link org.luaj.vm2.lib.jme.JmePlatform} see to it that this {@link Globals}
* see to it that this {@link Globals} are initialized properly. * are initialized properly.
* <p> * <p>
* The behavior of coroutine threads matches closely the behavior * The behavior of coroutine threads matches closely the behavior of C coroutine
* of C coroutine library. However, because of the use of Java threads * library. However, because of the use of Java threads to manage call state, it
* to manage call state, it is possible to yield from anywhere in luaj. * is possible to yield from anywhere in luaj.
* <p> * <p>
* Each Java thread wakes up at regular intervals and checks a weak reference * Each Java thread wakes up at regular intervals and checks a weak reference to
* to determine if it can ever be resumed. If not, it throws * determine if it can ever be resumed. If not, it throws {@link OrphanedThread}
* {@link OrphanedThread} which is an {@link java.lang.Error}. * which is an {@link java.lang.Error}. Applications should not catch
* Applications should not catch {@link OrphanedThread}, because it can break * {@link OrphanedThread}, because it can break the thread safety of luaj. The
* the thread safety of luaj. The value controlling the polling interval * value controlling the polling interval is
* is {@link #thread_orphan_check_interval} and may be set by the user. * {@link #thread_orphan_check_interval} and may be set by the user.
* <p> * <p>
* There are two main ways to abandon a coroutine. The first is to call * There are two main ways to abandon a coroutine. The first is to call
* {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)}, * {@code yield()} from lua, or equivalently {@link Globals#yield(Varargs)}, and
* and arrange to have it never resumed possibly by values passed to yield. * arrange to have it never resumed possibly by values passed to yield. The
* The second is to throw {@link OrphanedThread}, which should put the thread * second is to throw {@link OrphanedThread}, which should put the thread in a
* in a dead state. In either case all references to the thread must be * dead state. In either case all references to the thread must be dropped, and
* dropped, and the garbage collector must run for the thread to be * the garbage collector must run for the thread to be garbage collected.
* garbage collected.
* *
* *
* @see LuaValue * @see LuaValue
@@ -73,12 +71,13 @@ public class LuaThread extends LuaValue {
/** 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;
@@ -87,19 +86,16 @@ public class LuaThread extends LuaValue {
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;
@@ -116,6 +112,7 @@ public class LuaThread extends LuaValue {
/** /**
* 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) {
@@ -124,43 +121,45 @@ public class LuaThread extends LuaValue {
this.globals = globals; this.globals = globals;
} }
@Override
public int type() { public int type() {
return LuaValue.TTHREAD; return LuaValue.TTHREAD;
} }
@Override
public String typename() { public String typename() {
return "thread"; return "thread";
} }
@Override
public boolean isthread() { public boolean isthread() {
return true; return true;
} }
@Override
public LuaThread optthread(LuaThread defval) { public LuaThread optthread(LuaThread defval) {
return this; return this;
} }
@Override
public LuaThread checkthread() { public LuaThread checkthread() {
return this; return this;
} }
@Override
public LuaValue getmetatable() { public LuaValue getmetatable() {
return s_metatable; return s_metatable;
} }
public String getStatus() { public String getStatus() { return STATUS_NAMES[state.status]; }
return STATUS_NAMES[state.status];
}
public boolean isMainThread() { public boolean isMainThread() { return this.state.function == null; }
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);
} }
@@ -191,6 +190,7 @@ public class LuaThread extends LuaValue {
this.function = function; this.function = function;
} }
@Override
public synchronized void run() { public synchronized void run() {
try { try {
Varargs a = this.args; Varargs a = this.args;
@@ -211,7 +211,7 @@ public class LuaThread extends LuaValue {
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 +219,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 +229,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 +244,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;

View File

@@ -21,7 +21,6 @@
******************************************************************************/ ******************************************************************************/
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;
@@ -36,18 +35,22 @@ public class LuaUserdata extends LuaValue {
m_metatable = metatable; m_metatable = metatable;
} }
@Override
public String tojstring() { public String tojstring() {
return String.valueOf(m_instance); return String.valueOf(m_instance);
} }
@Override
public int type() { public int type() {
return LuaValue.TUSERDATA; return LuaValue.TUSERDATA;
} }
@Override
public String typename() { public String typename() {
return "userdata"; return "userdata";
} }
@Override
public int hashCode() { public int hashCode() {
return m_instance.hashCode(); return m_instance.hashCode();
} }
@@ -56,71 +59,98 @@ public class LuaUserdata extends LuaValue {
return m_instance; return m_instance;
} }
@Override
public boolean isuserdata() { return true; } public boolean isuserdata() { return true; }
@Override
public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); } public boolean isuserdata(Class c) { return c.isAssignableFrom(m_instance.getClass()); }
@Override
public Object touserdata() { return m_instance; } public Object touserdata() { return m_instance; }
@Override
public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; } public Object touserdata(Class c) { return c.isAssignableFrom(m_instance.getClass())? m_instance: null; }
@Override
public Object optuserdata(Object defval) { return m_instance; } public Object optuserdata(Object defval) { return m_instance; }
@Override
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;
} }
@Override
public LuaValue getmetatable() { public LuaValue getmetatable() {
return m_metatable; return m_metatable;
} }
@Override
public LuaValue setmetatable(LuaValue metatable) { public LuaValue setmetatable(LuaValue metatable) {
this.m_metatable = metatable; this.m_metatable = metatable;
return this; return this;
} }
@Override
public Object checkuserdata() { public Object checkuserdata() {
return m_instance; return m_instance;
} }
@Override
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 ) { @Override
return m_metatable!=null? gettable(this,key): NIL; public LuaValue get(LuaValue key) {
return m_metatable != null? gettable(this, key): NIL;
} }
public void set( LuaValue key, LuaValue value ) { @Override
if ( m_metatable==null || ! settable(this,key,value) ) public void set(LuaValue key, LuaValue value) {
error( "cannot set "+key+" for userdata" ); if (m_metatable == null || !settable(this, key, value))
error("cannot set " + key + " for userdata");
} }
public boolean equals( Object val ) { @Override
if ( this == val ) 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; } @Override
public boolean eq_b( LuaValue val ) { public LuaValue eq(LuaValue val) { return eq_b(val)? TRUE: FALSE; }
if ( val.raweq(this) ) return true;
if ( m_metatable == null || !val.isuserdata() ) return false; @Override
public boolean eq_b(LuaValue val) {
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); } @Override
public boolean raweq( LuaUserdata val ) { public boolean raweq(LuaValue val) { return val.raweq(this); }
return this == val || (m_metatable == val.m_metatable && m_instance.equals(val.m_instance));
@Override
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;
} }
} }

View File

@@ -631,7 +631,7 @@ public class LuaValue extends Varargs {
* @see #isclosure() * @see #isclosure()
* @see #TFUNCTION * @see #TFUNCTION
*/ */
public LuaClosure optclosure(LuaClosure defval) { argerror("closure"); return null; } public LuaClosure optclosure(LuaClosure defval) { argerror("function"); return null; }
/** Check that optional argument is a number or string convertible to number and return as double /** Check that optional argument is a number or string convertible to number and return as double
* @param defval double to return if {@code this} is nil or none * @param defval double to return if {@code this} is nil or none
@@ -743,7 +743,7 @@ public class LuaValue extends Varargs {
* @see #toString() * @see #toString()
* @see #TSTRING * @see #TSTRING
*/ */
public String optjstring(String defval) { argerror("String"); return null; } public String optjstring(String defval) { argerror("string"); return null; }
/** Check that optional argument is a string or number and return as {@link LuaString} /** Check that optional argument is a string or number and return as {@link LuaString}
* @param defval {@link LuaString} to return if {@code this} is nil or none * @param defval {@link LuaString} to return if {@code this} is nil or none
@@ -894,7 +894,7 @@ public class LuaValue extends Varargs {
* @see #optint(int) * @see #optint(int)
* @see #TNUMBER * @see #TNUMBER
*/ */
public int checkint() { argerror("int"); return 0; } public int checkint() { argerror("number"); return 0; }
/** Check that the value is numeric, and convert and cast value to int, or throw {@link LuaError} if not numeric /** Check that the value is numeric, and convert and cast value to int, or throw {@link LuaError} if not numeric
* <p> * <p>
@@ -2991,7 +2991,7 @@ public class LuaValue extends Varargs {
return h.call(this, op1); return h.call(this, op1);
if (LuaValue.LE.raweq(tag) && (!(h = metatag(LT)).isnil() || !(h = op1.metatag(LT)).isnil())) if (LuaValue.LE.raweq(tag) && (!(h = metatag(LT)).isnil() || !(h = op1.metatag(LT)).isnil()))
return h.call(op1, this).not(); return h.call(op1, this).not();
return error("attempt to compare "+tag+" on "+typename()+" and "+op1.typename()); return error("bad argument: attempt to compare "+tag+" on "+typename()+" and "+op1.typename());
} }
/** Perform string comparison with another value /** Perform string comparison with another value

View File

@@ -29,23 +29,23 @@ import org.luaj.vm2.LuaTable.Slot;
interface Metatable { interface Metatable {
/** Return whether or not this table's keys are weak. */ /** Return whether or not this table's keys are weak. */
public boolean useWeakKeys(); boolean useWeakKeys();
/** Return whether or not this table's values are weak. */ /** Return whether or not this table's values are weak. */
public boolean useWeakValues(); boolean useWeakValues();
/** Return this metatable as a LuaValue. */ /** Return this metatable as a LuaValue. */
public LuaValue toLuaValue(); 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 ); 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 ); 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); LuaValue arrayget(LuaValue[] array, int index);
} }

View File

@@ -10,26 +10,32 @@ class NonTableMetatable implements Metatable {
this.value = value; this.value = value;
} }
@Override
public boolean useWeakKeys() { public boolean useWeakKeys() {
return false; return false;
} }
@Override
public boolean useWeakValues() { public boolean useWeakValues() {
return false; return false;
} }
@Override
public LuaValue toLuaValue() { public LuaValue toLuaValue() {
return value; return value;
} }
@Override
public Slot entry(LuaValue key, LuaValue value) { public Slot entry(LuaValue key, LuaValue value) {
return LuaTable.defaultEntry(key, value); return LuaTable.defaultEntry(key, value);
} }
@Override
public LuaValue wrap(LuaValue value) { public LuaValue wrap(LuaValue value) {
return value; return value;
} }
@Override
public LuaValue arrayget(LuaValue[] array, int index) { public LuaValue arrayget(LuaValue[] array, int index) {
return array[index]; return array[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

@@ -26,6 +26,7 @@ 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
*/ */
@@ -36,57 +37,17 @@ public class Print extends Lua {
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) {
@@ -119,7 +80,7 @@ public class Print extends Lua {
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;
} }
} }
@@ -127,29 +88,33 @@ public class Print extends Lua {
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) {
@@ -163,16 +128,18 @@ 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
@@ -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;
@@ -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) {
@@ -307,12 +274,12 @@ public class Print extends Lua {
break; break;
case OP_SETLIST: case OP_SETLIST:
if (c == 0) if (c == 0)
ps.print(" ; " + ((int) code[++pc]) + " (stored in the next OP)"); ps.print(" ; " + code[++pc] + " (stored in the next OP)");
else else
ps.print(" ; " + ((int) c)); ps.print(" ; " + 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,7 +334,8 @@ 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.
*/ */
@@ -377,7 +343,8 @@ public class Print extends Lua {
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.
@@ -395,27 +362,29 @@ 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 cl the {@link LuaClosure}
* @param pc the program counter * @param pc the program counter
* @param stack the stack of {@link LuaValue} * @param stack the stack of {@link LuaValue}
@@ -426,12 +395,12 @@ public class Print extends Lua {
// 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,36 +408,36 @@ 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
switch (v.type()) {
case LuaValue.TSTRING: case LuaValue.TSTRING:
LuaString s = v.checkstring(); LuaString s = v.checkstring();
ps.print( s.length() < 48? ps.print(s.length() < 48? s.tojstring()
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; break;
default: default:
ps.print(v.tojstring()); ps.print(v.tojstring());
} }
if ( i+1 == top ) if (i+1 == top)
ps.print(']'); ps.print(']');
ps.print( " | " ); ps.print(" | ");
} }
ps.print(varargs); ps.print(varargs);
} }

View File

@@ -0,0 +1,180 @@
/*******************************************************************************
* Copyright (c) 2009-2011 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
/**
* Prototype representing compiled lua code.
*
* <p>
* This is both a straight translation of the corresponding C type, and the main
* data structure for execution of compiled lua bytecode.
*
* <p>
* Generally, the {@link Prototype} is not constructed directly is an
* intermediate result as lua code is loaded using
* {@link Globals#load(java.io.Reader, String)}:
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua").call();
* }
* </pre>
*
* <p>
* To create a {@link Prototype} directly, a compiler such as
* {@link org.luaj.vm2.compiler.LuaC} may be used:
*
* <pre>
* {
* &#64;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>
* {
* &#64;code
* Prototype p = globals.compileProtoytpe(is, "script");
* }
* </pre>
*
* It may also be loaded from a {@link java.io.Reader} via
* {@link Globals#compilePrototype(java.io.Reader, String)}:
*
* <pre>
* {
* &#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);
* }
* </pre>
* <p>
*
* @see LuaClosure
* @see Globals
* @see Globals#undumper
* @see Globals#compiler
* @see Print#print
*/
public class Prototype {
/* constants used by the function */
public LuaValue[] k;
public int[] code;
/* functions defined inside the function */
public Prototype[] p;
/* map from opcodes to source lines */
public int[] lineinfo;
/* information about local variables */
public LocVars[] locvars;
/* upvalue information */
public Upvaldesc[] upvalues;
public LuaString source;
public int linedefined;
public int lastlinedefined;
public int numparams;
public int is_vararg;
public int maxstacksize;
private static final Upvaldesc[] NOUPVALUES = {};
private static final Prototype[] NOSUBPROTOS = {};
public Prototype() {
p = NOSUBPROTOS;
upvalues = NOUPVALUES;
}
public Prototype(int n_upvalues) {
p = NOSUBPROTOS;
upvalues = new Upvaldesc[n_upvalues];
}
@Override
public String toString() {
return source + ":" + linedefined + "-" + lastlinedefined;
}
/**
* Get the name of a local variable.
*
* @param number the local variable number to look up
* @param pc the program counter
* @return the name, or null if not found
*/
public LuaString getlocalname(int number, int pc) {
int i;
for (i = 0; i < locvars.length && locvars[i].startpc <= pc; i++) {
if (pc < locvars[i].endpc) { /* is variable active? */
number--;
if (number == 0)
return locvars[i].varname;
}
}
return null; /* not found */
}
public String shortsource() {
String name = source.tojstring();
if (name.startsWith("@") || name.startsWith("="))
name = name.substring(1);
else if (name.startsWith("\033"))
name = "binary string";
return name;
}
}

View File

@@ -22,21 +22,20 @@
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
*/ */
@@ -56,10 +55,10 @@ public class TailcallVarargs extends Varargs {
this.args = LuaValue.varargsOf(object, args); this.args = LuaValue.varargsOf(object, args);
} }
public boolean isTailcall() { @Override
return true; public boolean isTailcall() { return true; }
}
@Override
public Varargs eval() { public Varargs eval() {
while ( result == null ) { while ( result == null ) {
Varargs r = func.onInvoke(args); Varargs r = func.onInvoke(args);
@@ -67,8 +66,7 @@ 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;
@@ -77,24 +75,28 @@ public class TailcallVarargs extends Varargs {
return result; return result;
} }
public LuaValue arg( int i ) { @Override
if ( result == null ) public LuaValue arg(int i) {
if (result == null)
eval(); eval();
return result.arg(i); return result.arg(i);
} }
@Override
public LuaValue arg1() { public LuaValue arg1() {
if (result == null) if (result == null)
eval(); eval();
return result.arg1(); return result.arg1();
} }
@Override
public int narg() { public int narg() {
if (result == null) if (result == null)
eval(); eval();
return result.narg(); return result.narg();
} }
@Override
public Varargs subargs(int start) { public Varargs subargs(int start) {
if (result == null) if (result == null)
eval(); eval();

View File

@@ -21,9 +21,10 @@
******************************************************************************/ ******************************************************************************/
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
*/ */
@@ -34,20 +35,23 @@ public final class UpValue {
/** /**
* 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;
} }
@Override
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()
*/ */
@@ -57,24 +61,22 @@ public final class UpValue {
/** /**
* 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 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 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
*/ */
public final void close() { public void close() {
LuaValue[] old = array; LuaValue[] old = array;
array = new LuaValue[] { old[index] }; array = new LuaValue[] { old[index] };
old[index] = null; old[index] = null;

View File

@@ -38,7 +38,8 @@ public class Upvaldesc {
this.idx = (short) idx; this.idx = (short) idx;
} }
@Override
public String toString() { public String toString() {
return idx + (instack? " instack ": " closed ") + String.valueOf(name); return idx+(instack? " instack ": " closed ")+String.valueOf(name);
} }
} }

View File

@@ -29,36 +29,37 @@ 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> * <p>
* Normally these are not created directly, but indirectly when changing the mode * Normally these are not created directly, but indirectly when changing the
* of a {@link LuaTable} as lua script executes. * mode of a {@link LuaTable} as lua script executes.
* <p> * <p>
* However, calling the constructors directly when weak tables are required from * However, calling the constructors directly when weak tables are required from
* Java will reduce overhead. * Java will reduce overhead.
*/ */
public class WeakTable implements Metatable { public class WeakTable implements Metatable {
private boolean weakkeys, weakvalues; private final boolean weakkeys, weakvalues;
private LuaValue backing; private final 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 LuaValue.tableOf();
} }
LuaTable table = LuaTable.tableOf(); LuaTable table = LuaValue.tableOf();
LuaTable mt = LuaTable.tableOf(new LuaValue[] { LuaValue.MODE, mode }); LuaTable mt = LuaValue.tableOf(new LuaValue[] { LuaValue.MODE, mode });
table.setmetatable(mt); table.setmetatable(mt);
return table; return table;
} }
/** /**
* 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
*/ */
@@ -68,33 +69,37 @@ public class WeakTable implements Metatable {
this.backing = backing; this.backing = backing;
} }
@Override
public boolean useWeakKeys() { public boolean useWeakKeys() {
return weakkeys; return weakkeys;
} }
@Override
public boolean useWeakValues() { public boolean useWeakValues() {
return weakvalues; return weakvalues;
} }
@Override
public LuaValue toLuaValue() { public LuaValue toLuaValue() {
return backing; return backing;
} }
@Override
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 {
@@ -109,14 +114,16 @@ public class WeakTable implements Metatable {
this.next = next; this.next = next;
} }
public abstract int keyindex( int hashMask ); @Override
public abstract int keyindex(int hashMask);
public abstract Slot set(LuaValue value); public abstract Slot set(LuaValue value);
@Override
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;
@@ -125,67 +132,75 @@ public class WeakTable implements Metatable {
} }
} }
@Override
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;
} }
@Override
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);
} }
@Override
public Slot rest() { public Slot rest() {
return next; return next;
} }
@Override
public int arraykey(int max) { public int arraykey(int max) {
// Integer keys can never be weak. // Integer keys can never be weak.
return 0; return 0;
} }
@Override
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 ) { @Override
next = ( next != null ) ? next.add( entry ) : entry; public Slot add(Slot entry) {
if ( strongkey() != null && strongvalue() != null ) { next = next != null? next.add(entry): entry;
if (strongkey() != null && strongvalue() != null) {
return this; return this;
} else { } else {
return next; return next;
} }
} }
public Slot remove( StrongSlot target ) { @Override
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 ) { @Override
if ( strongkey() != null && strongvalue() != null ) { public Slot relink(Slot rest) {
if ( rest == null && this.next == null ) { if (strongkey() != null && strongvalue() != 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,66 +215,74 @@ 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 ) { @Override
return LuaTable.hashmod( keyhash, mask ); public int keyindex(int mask) {
return LuaTable.hashmod(keyhash, mask);
} }
@Override
public Slot set(LuaValue value) { public Slot set(LuaValue value) {
this.value = value; this.value = value;
return this; return this;
} }
@Override
public LuaValue strongkey() { public LuaValue strongkey() {
return strengthen( key ); return strengthen(key);
} }
protected WeakSlot copy( Slot rest ) { @Override
return new WeakKeySlot( this, rest ); protected WeakSlot copy(Slot 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 ) { @Override
return LuaTable.hashSlot( strongkey(), mask ); public int keyindex(int mask) {
return LuaTable.hashSlot(strongkey(), mask);
} }
@Override
public Slot set(LuaValue value) { public Slot set(LuaValue value) {
this.value = weaken(value); this.value = weaken(value);
return this; return this;
} }
@Override
public LuaValue strongvalue() { public LuaValue strongvalue() {
return strengthen( value ); return strengthen(value);
} }
@Override
protected WeakSlot copy(Slot next) { protected WeakSlot copy(Slot next) {
return new WeakValueSlot( this, next ); return new WeakValueSlot(this, next);
} }
} }
@@ -267,45 +290,52 @@ 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 ) { @Override
return LuaTable.hashmod( keyhash, hashMask ); public int keyindex(int hashMask) {
return LuaTable.hashmod(keyhash, hashMask);
} }
@Override
public Slot set(LuaValue value) { public Slot set(LuaValue value) {
this.value = weaken(value); this.value = weaken(value);
return this; return this;
} }
@Override
public LuaValue strongkey() { public LuaValue strongkey() {
return strengthen( key ); return strengthen(key);
} }
@Override
public LuaValue strongvalue() { public LuaValue strongvalue() {
return strengthen( value ); return strengthen(value);
} }
protected WeakSlot copy( Slot next ) { @Override
return new WeakKeyAndValueSlot( this, next ); protected WeakSlot copy(Slot 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:
@@ -319,21 +349,24 @@ public class WeakTable implements Metatable {
/** /**
* 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 {
@@ -343,32 +376,39 @@ public class WeakTable implements Metatable {
ref = new WeakReference(value); ref = new WeakReference(value);
} }
@Override
public int type() { public int type() {
illegal("type","weak value"); illegal("type", "weak value");
return 0; return 0;
} }
@Override
public String typename() { public String typename() {
illegal("typename","weak value"); illegal("typename", "weak value");
return null; return null;
} }
@Override
public String toString() { public String toString() {
return "weak<"+ref.get()+">"; return "weak<" + ref.get() + ">";
} }
@Override
public LuaValue strongvalue() { public LuaValue strongvalue() {
Object o = ref.get(); Object o = ref.get();
return (LuaValue)o; return (LuaValue) o;
} }
@Override
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 {
@@ -381,13 +421,14 @@ public class WeakTable implements Metatable {
mt = value.getmetatable(); mt = value.getmetatable();
} }
@Override
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 {
@@ -396,10 +437,12 @@ public class WeakTable implements Metatable {
} }
} }
@Override
public LuaValue wrap(LuaValue value) { public LuaValue wrap(LuaValue value) {
return weakvalues ? weaken( value ) : value; return weakvalues? weaken(value): value;
} }
@Override
public LuaValue arrayget(LuaValue[] array, int index) { public LuaValue arrayget(LuaValue[] array, int index) {
LuaValue value = array[index]; LuaValue value = array[index];
if (value != null) { if (value != null) {

View File

@@ -44,145 +44,133 @@ public class Constants extends Lua {
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 *
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua"); * Prototype p = globals.compilePrototype(new StringReader("print('hello, world')"), "main.lua");
* ByteArrayOutputStream o = new ByteArrayOutputStream(); * ByteArrayOutputStream o = new ByteArrayOutputStream();
* DumpState.dump(p, o, false); * DumpState.dump(p, o, false);
* byte[] lua_binary_file_bytes = o.toByteArray(); * byte[] lua_binary_file_bytes = o.toByteArray();
* } </pre> * }
* </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 *
* <pre>
* {
* &#64;code
* Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b"); * Prototype p = globals.loadPrototype(new ByteArrayInputStream(lua_binary_file_bytes), "main.lua", "b");
* LuaClosure c = new LuaClosure(p, globals); * LuaClosure c = new LuaClosure(p, globals);
* c.call(); * c.call();
* } </pre> * }
* </pre>
* *
* @see luac * @see luac
* @see LoadState * @see LoadState
@@ -72,13 +84,19 @@ 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 */ /**
* format corresponding to non-number-patched lua, all numbers are floats or
* doubles
*/
public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; 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 */ /**
* format corresponding to number-patched lua, all numbers are 32-bit (4
* byte) ints
*/
public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4;
/** default number format */ /** default number format */
@@ -97,7 +115,7 @@ public class DumpState {
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,15 +125,15 @@ 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);
} }
@@ -123,27 +141,27 @@ public class DumpState {
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 {
@@ -152,13 +170,13 @@ public class DumpState {
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,7 +244,7 @@ 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);
@@ -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;
@@ -272,25 +290,29 @@ 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,7 +34,6 @@ 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 {
@@ -44,7 +43,7 @@ public class FuncState extends Constants {
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' */
@@ -65,13 +64,12 @@ public class FuncState extends Constants {
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,58 +77,53 @@ 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++)
@@ -139,17 +132,17 @@ public class FuncState extends Constants {
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;
} }
@@ -158,7 +151,7 @@ public class FuncState extends Constants {
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;
} }
@@ -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)
@@ -208,7 +201,7 @@ public class FuncState extends Constants {
} }
} }
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;
@@ -252,53 +245,52 @@ public class FuncState extends Constants {
} }
boolean hasmultret(int k) { boolean hasmultret(int k) {
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)
*/
} else {
if (cc.v.k != LexState.VVOID) if (cc.v.k != LexState.VVOID)
this.exp2nextreg(cc.v); this.exp2nextreg(cc.v);
this.setlist(cc.t.u.info, cc.na, cc.tostore); 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();
@@ -479,10 +466,10 @@ public class FuncState extends Constants {
return ((Integer) h.get(v)).intValue(); return ((Integer) h.get(v)).intValue();
} }
final int idx = this.nk; final int idx = this.nk;
this.h.put(v, new Integer(idx)); this.h.put(v, Integer.valueOf(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 == 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);
} }
@@ -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,8 +662,7 @@ 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
@@ -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;
} }
} }
@@ -855,14 +838,14 @@ public class FuncState extends Constants {
} }
static boolean vkisinreg(int k) { static boolean vkisinreg(int k) {
return ((k) == LexState.VNONRELOC || (k) == LexState.VLOCAL); return k == LexState.VNONRELOC || k == LexState.VLOCAL;
} }
void indexed(expdesc t, expdesc k) { void indexed(expdesc t, expdesc k) {
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)); Constants._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;
} }
@@ -901,22 +884,20 @@ 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;
} }
void codearith(int op, expdesc e1, expdesc e2, int line) { void codearith(int op, expdesc e1, expdesc e2, int line) {
if (constfolding(op, e1, e2)) if (constfolding(op, e1, e2)) {
return; } else {
else { int o2 = op != OP_UNM && op != OP_LEN? this.exp2RK(e2): 0;
int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2)
: 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 +912,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,7 +928,7 @@ 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) {
@@ -969,11 +950,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 +985,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 +996,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 +1005,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 +1054,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 = Constants.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 = Constants.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 +1106,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

@@ -24,13 +24,16 @@ 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;
} }

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 *
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* globals.load(new StringReader("print 'hello'"), "main.lua" ).call(); * globals.load(new StringReader("print 'hello'"), "main.lua").call();
* } </pre> * }
* </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
@@ -77,9 +84,10 @@ 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,48 +97,56 @@ 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
*/ */
@Override
public Prototype compile(InputStream stream, String chunkname) throws IOException { public Prototype compile(InputStream stream, String chunkname) throws IOException {
return (new CompileState()).luaY_parser(stream, chunkname); return new CompileState().luaY_parser(stream, chunkname);
} }
@Override
public LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException { public LuaFunction load(Prototype prototype, String chunkname, LuaValue env) throws IOException {
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.
*/ */
@Deprecated
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 final 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, 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 = LuaValue.valueOf(name);
lexstate.mainfunc(funcstate); lexstate.mainfunc(funcstate);
LuaC._assert (funcstate.prev == null); Constants._assert(funcstate.prev == null);
/* all scopes should be correctly finished */ /* all scopes should be correctly finished */
LuaC._assert (lexstate.dyd == null Constants._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;
} }

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,46 +50,61 @@ 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 *
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* globals.get("print").call(LuaValue.valueOf("hello, world")); * globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre> * }
* </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.get("print").call(LuaValue.valueOf("hello, world")); * globals.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre> * }
* Doing so will ensure the library is properly initialized * </pre>
* and loaded into the globals table. *
* 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; Globals globals;
/** Perform one-time initialization on the library by adding base functions /**
* 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.
*/ */
@Override
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());
@@ -205,6 +220,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg // "loadfile", // ( [filename [, mode [, env]]] ) -> chunk | nil, msg
final class loadfile extends VarArgFunction { final class loadfile extends VarArgFunction {
@Override
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;
@@ -218,6 +234,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "pcall", // (f, arg1, ...) -> status, result1, ... // "pcall", // (f, arg1, ...) -> status, result1, ...
final class pcall extends VarArgFunction { final class pcall extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue func = args.checkvalue(1); LuaValue func = args.checkvalue(1);
if (globals != null && globals.debuglib != null) if (globals != null && globals.debuglib != null)
@@ -243,6 +260,8 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
print(BaseLib baselib) { print(BaseLib baselib) {
this.baselib = baselib; this.baselib = baselib;
} }
@Override
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++ ) {
@@ -255,7 +274,6 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
} }
} }
// "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() {
@@ -341,7 +359,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
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);
} }
@@ -365,6 +383,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "type", // (v) -> value // "type", // (v) -> value
static final class type extends LibFunction { static final class type extends LibFunction {
@Override
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return valueOf(arg.typename()); return valueOf(arg.typename());
} }
@@ -372,6 +391,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "xpcall", // (f, err) -> result1, ... // "xpcall", // (f, err) -> result1, ...
final class xpcall extends VarArgFunction { final class xpcall extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
final LuaThread t = globals.running; final LuaThread t = globals.running;
final LuaValue preverror = t.errorfunc; final LuaValue preverror = t.errorfunc;
@@ -428,6 +448,7 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
// "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 {
@Override
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));
} }
@@ -464,7 +485,6 @@ public class BaseLib extends TwoArgFunction implements ResourceFinder {
} }
} }
private static class StringInputStream extends InputStream { private static class StringInputStream extends InputStream {
final LuaValue func; final LuaValue func;
byte[] bytes; byte[] bytes;
@@ -487,7 +507,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,88 @@ 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()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) ); * System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* } </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new Bit32Lib()); * globals.load(new Bit32Lib());
* System.out.println( globals.get("bit32").get("bnot").call( LuaValue.valueOf(2) ) ); * System.out.println(globals.get("bit32").get("bnot").call(LuaValue.valueOf(2)));
* } </pre> * }
* </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.
*/ */
@Override
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 {
@Override
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;
} }
@@ -95,13 +115,19 @@ public class Bit32Lib extends TwoArgFunction {
static final class Bit32Lib2 extends TwoArgFunction { static final class Bit32Lib2 extends TwoArgFunction {
@Override
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;
} }
@@ -110,9 +136,9 @@ public class Bit32Lib extends TwoArgFunction {
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 +146,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 +156,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 +203,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 +212,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 +223,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 +236,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(x & 0xFFFFFFFFL): valueOf(x);
} }
} }

View File

@@ -28,37 +28,48 @@ 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()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("coroutine").get("running").call() ); * System.out.println(globals.get("coroutine").get("running").call());
* } </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new CoroutineLib()); * globals.load(new CoroutineLib());
* System.out.println( globals.get("coroutine").get("running").call() ); * System.out.println(globals.get("coroutine").get("running").call());
* } </pre> * }
* </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 {
@@ -66,12 +77,17 @@ public class CoroutineLib extends TwoArgFunction {
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.
*/ */
@Override
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
LuaTable coroutine = new LuaTable(); LuaTable coroutine = new LuaTable();
@@ -82,24 +98,28 @@ 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;
} }
final class create extends LibFunction { final class create extends LibFunction {
@Override
public LuaValue call(LuaValue f) { public LuaValue call(LuaValue f) {
return new LuaThread(globals, f.checkfunction()); return new LuaThread(globals, f.checkfunction());
} }
} }
static final class resume extends VarArgFunction { static final class resume extends VarArgFunction {
@Override
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));
} }
} }
final class running extends VarArgFunction { final class running extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
final LuaThread r = globals.running; final LuaThread r = globals.running;
return varargsOf(r, valueOf(r.isMainThread())); return varargsOf(r, valueOf(r.isMainThread()));
@@ -107,19 +127,22 @@ public class CoroutineLib extends TwoArgFunction {
} }
static final class status extends LibFunction { static final class status extends LibFunction {
@Override
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 {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return globals.yield( args ); return globals.yield(args);
} }
} }
final class wrap extends LibFunction { final class wrap extends LibFunction {
@Override
public LuaValue call(LuaValue f) { public LuaValue call(LuaValue f) {
final LuaValue func = f.checkfunction(); final LuaValue func = f.checkfunction();
final LuaThread thread = new LuaThread(globals, func); final LuaThread thread = new LuaThread(globals, func);
@@ -129,15 +152,18 @@ 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;
} }
@Override
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());
} }
} }
} }

View File

@@ -39,49 +39,64 @@ import org.luaj.vm2.Prototype;
import org.luaj.vm2.Varargs; import org.luaj.vm2.Varargs;
/** /**
* Subclass of {@link LibFunction} which implements the lua standard {@code debug} * Subclass of {@link LibFunction} which implements the lua standard
* library. * {@code debug} library.
* <p> * <p>
* The debug library in luaj tries to emulate the behavior of the corresponding C-based lua library. * The debug library in luaj tries to emulate the behavior of the corresponding
* To do this, it must maintain a separate stack of calls to {@link LuaClosure} and {@link LibFunction} * C-based lua library. To do this, it must maintain a separate stack of calls
* instances. * to {@link LuaClosure} and {@link LibFunction} instances. Especially when
* Especially when lua-to-java bytecode compiling is being used * lua-to-java bytecode compiling is being used via a
* via a {@link org.luaj.vm2.Globals.Compiler} such as {@link org.luaj.vm2.luajc.LuaJC}, * {@link org.luaj.vm2.Globals.Compiler} such as
* this cannot be done in all cases. * {@link org.luaj.vm2.luajc.LuaJC}, this cannot be done in all cases.
* <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#debugGlobals()} or * {@link org.luaj.vm2.lib.jse.JsePlatform#debugGlobals()} or
* {@link org.luaj.vm2.lib.jme.JmePlatform#debugGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#debugGlobals()}
* <pre> {@code *
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.debugGlobals(); * Globals globals = JsePlatform.debugGlobals();
* System.out.println( globals.get("debug").get("traceback").call() ); * System.out.println(globals.get("debug").get("traceback").call());
* } </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new DebugLib()); * globals.load(new DebugLib());
* System.out.println( globals.get("debug").get("traceback").call() ); * System.out.println(globals.get("debug").get("traceback").call());
* } </pre> * }
* </pre>
* <p> * <p>
* This library exposes the entire state of lua code, and provides method to see and modify * This library exposes the entire state of lua code, and provides method to see
* all underlying lua values within a Java VM so should not be exposed to client code * and modify all underlying lua values within a Java VM so should not be
* in a shared server environment. * exposed to client code in a shared server environment.
* *
* @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.10">Lua 5.2 Debug Lib Reference</a> * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.10">Lua 5.2 Debug
* Lib Reference</a>
*/ */
public class DebugLib extends TwoArgFunction { public class DebugLib extends TwoArgFunction {
public static boolean CALLS; public static boolean CALLS;
public static boolean TRACE; public static boolean TRACE;
static { static {
try { CALLS = (null != System.getProperty("CALLS")); } catch (Exception e) {} try {
try { TRACE = (null != System.getProperty("TRACE")); } catch (Exception e) {} CALLS = null != System.getProperty("CALLS");
} catch (Exception e) {
}
try {
TRACE = null != System.getProperty("TRACE");
} catch (Exception e) {
}
} }
static final LuaString LUA = valueOf("Lua"); static final LuaString LUA = valueOf("Lua");
@@ -108,12 +123,17 @@ public class DebugLib extends TwoArgFunction {
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.
*/ */
@Override
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
globals.debuglib = this; globals.debuglib = this;
@@ -135,12 +155,14 @@ public class DebugLib extends TwoArgFunction {
debug.set("upvalueid", new upvalueid()); debug.set("upvalueid", new upvalueid());
debug.set("upvaluejoin", new upvaluejoin()); debug.set("upvaluejoin", new upvaluejoin());
env.set("debug", debug); env.set("debug", debug);
if (!env.get("package").isnil()) env.get("package").get("loaded").set("debug", debug); if (!env.get("package").isnil())
env.get("package").get("loaded").set("debug", debug);
return debug; return debug;
} }
// debug.debug() // debug.debug()
static final class debug extends ZeroArgFunction { static final class debug extends ZeroArgFunction {
@Override
public LuaValue call() { public LuaValue call() {
return NONE; return NONE;
} }
@@ -148,20 +170,20 @@ public class DebugLib extends TwoArgFunction {
// debug.gethook ([thread]) // debug.gethook ([thread])
final class gethook extends VarArgFunction { final class gethook extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaThread t = args.narg() > 0 ? args.checkthread(1): globals.running; LuaThread t = args.narg() > 0? args.checkthread(1): globals.running;
LuaThread.State s = t.state; LuaThread.State s = t.state;
return varargsOf( return varargsOf(s.hookfunc != null? s.hookfunc: NIL,
s.hookfunc != null? s.hookfunc: NIL, valueOf((s.hookcall? "c": "")+(s.hookline? "l": "")+(s.hookrtrn? "r": "")), valueOf(s.hookcount));
valueOf((s.hookcall?"c":"")+(s.hookline?"l":"")+(s.hookrtrn?"r":"")),
valueOf(s.hookcount));
} }
} }
// debug.getinfo ([thread,] f [, what]) // debug.getinfo ([thread,] f [, what])
final class getinfo extends VarArgFunction { final class getinfo extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int a=1; int a = 1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running; LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
LuaValue func = args.arg(a++); LuaValue func = args.arg(a++);
String what = args.optjstring(a++, "flnStu"); String what = args.optjstring(a++, "flnStu");
@@ -169,12 +191,12 @@ public class DebugLib extends TwoArgFunction {
// find the stack info // find the stack info
DebugLib.CallFrame frame; DebugLib.CallFrame frame;
if ( func.isnumber() ) { if (func.isnumber()) {
frame = callstack.getCallFrame(func.toint()); frame = callstack.getCallFrame(func.toint());
if (frame == null) if (frame == null)
return NONE; return NONE;
func = frame.f; func = frame.f;
} else if ( func.isfunction() ) { } else if (func.isfunction()) {
frame = callstack.findCallFrame(func); frame = callstack.findCallFrame(func);
} else { } else {
return argerror(a-2, "function or level"); return argerror(a-2, "function or level");
@@ -191,7 +213,7 @@ public class DebugLib extends TwoArgFunction {
info.set(LASTLINEDEFINED, valueOf(ar.lastlinedefined)); info.set(LASTLINEDEFINED, valueOf(ar.lastlinedefined));
} }
if (what.indexOf('l') >= 0) { if (what.indexOf('l') >= 0) {
info.set( CURRENTLINE, valueOf(ar.currentline) ); info.set(CURRENTLINE, valueOf(ar.currentline));
} }
if (what.indexOf('u') >= 0) { if (what.indexOf('u') >= 0) {
info.set(NUPS, valueOf(ar.nups)); info.set(NUPS, valueOf(ar.nups));
@@ -199,7 +221,7 @@ public class DebugLib extends TwoArgFunction {
info.set(ISVARARG, ar.isvararg? ONE: ZERO); info.set(ISVARARG, ar.isvararg? ONE: ZERO);
} }
if (what.indexOf('n') >= 0) { if (what.indexOf('n') >= 0) {
info.set(NAME, LuaValue.valueOf(ar.name!=null? ar.name: "?")); info.set(NAME, LuaValue.valueOf(ar.name != null? ar.name: "?"));
info.set(NAMEWHAT, LuaValue.valueOf(ar.namewhat)); info.set(NAMEWHAT, LuaValue.valueOf(ar.namewhat));
} }
if (what.indexOf('t') >= 0) { if (what.indexOf('t') >= 0) {
@@ -209,13 +231,13 @@ public class DebugLib extends TwoArgFunction {
LuaTable lines = new LuaTable(); LuaTable lines = new LuaTable();
info.set(ACTIVELINES, lines); info.set(ACTIVELINES, lines);
DebugLib.CallFrame cf; DebugLib.CallFrame cf;
for (int l = 1; (cf=callstack.getCallFrame(l)) != null; ++l) for (int l = 1; (cf = callstack.getCallFrame(l)) != null; ++l)
if (cf.f == func) if (cf.f == func)
lines.insert(-1, valueOf(cf.currentline())); lines.insert(-1, valueOf(cf.currentline()));
} }
if (what.indexOf('f') >= 0) { if (what.indexOf('f') >= 0) {
if (func != null) if (func != null)
info.set( FUNC, func ); info.set(FUNC, func);
} }
return info; return info;
} }
@@ -223,18 +245,27 @@ public class DebugLib extends TwoArgFunction {
// debug.getlocal ([thread,] f, local) // debug.getlocal ([thread,] f, local)
final class getlocal extends VarArgFunction { final class getlocal extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int a=1; int a = 1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running; LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
int level = args.checkint(a++); LuaValue func = args.arg(a++);
int local = args.checkint(a++); int local = args.checkint(a);
CallFrame f = callstack(thread).getCallFrame(level);
return f != null? f.getLocal(local): NONE; if (func.isfunction())
return func.checkclosure().p.getlocalname(local, 0);
// find the stack info
DebugLib.CallFrame frame = callstack(thread).getCallFrame(func.checkint());
if (frame == null)
return argerror(a, "level out of range");
return frame.getLocal(local);
} }
} }
// debug.getmetatable (value) // debug.getmetatable (value)
static final class getmetatable extends LibFunction { static final class getmetatable extends LibFunction {
@Override
public LuaValue call(LuaValue v) { public LuaValue call(LuaValue v) {
LuaValue mt = v.getmetatable(); LuaValue mt = v.getmetatable();
return mt != null? mt: NIL; return mt != null? mt: NIL;
@@ -243,6 +274,7 @@ public class DebugLib extends TwoArgFunction {
// debug.getregistry () // debug.getregistry ()
final class getregistry extends ZeroArgFunction { final class getregistry extends ZeroArgFunction {
@Override
public LuaValue call() { public LuaValue call() {
return globals; return globals;
} }
@@ -250,14 +282,15 @@ public class DebugLib extends TwoArgFunction {
// debug.getupvalue (f, up) // debug.getupvalue (f, up)
static final class getupvalue extends VarArgFunction { static final class getupvalue extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1); LuaValue func = args.checkfunction(1);
int up = args.checkint(2); int up = args.checkint(2);
if ( func instanceof LuaClosure ) { if (func instanceof LuaClosure) {
LuaClosure c = (LuaClosure) func; LuaClosure c = (LuaClosure) func;
LuaString name = findupvalue(c, up); LuaString name = findupvalue(c, up);
if ( name != null ) { if (name != null) {
return varargsOf(name, c.upValues[up-1].getValue() ); return varargsOf(name, c.upValues[up-1].getValue());
} }
} }
return NIL; return NIL;
@@ -266,26 +299,33 @@ public class DebugLib extends TwoArgFunction {
// debug.getuservalue (u) // debug.getuservalue (u)
static final class getuservalue extends LibFunction { static final class getuservalue extends LibFunction {
@Override
public LuaValue call(LuaValue u) { public LuaValue call(LuaValue u) {
return u.isuserdata()? u: NIL; return u.isuserdata()? u: NIL;
} }
} }
// debug.sethook ([thread,] hook, mask [, count]) // debug.sethook ([thread,] hook, mask [, count])
final class sethook extends VarArgFunction { final class sethook extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int a=1; int a = 1;
LuaThread t = args.isthread(a)? args.checkthread(a++): globals.running; LuaThread t = args.isthread(a)? args.checkthread(a++): globals.running;
LuaValue func = args.optfunction(a++, null); LuaValue func = args.optfunction(a++, null);
String str = args.optjstring(a++,""); String str = args.optjstring(a++, "");
int count = args.optint(a++,0); int count = args.optint(a++, 0);
boolean call=false,line=false,rtrn=false; boolean call = false, line = false, rtrn = false;
for ( int i=0; i<str.length(); i++ ) for (int i = 0; i < str.length(); i++)
switch ( str.charAt(i) ) { switch (str.charAt(i)) {
case 'c': call=true; break; case 'c':
case 'l': line=true; break; call = true;
case 'r': rtrn=true; break; break;
case 'l':
line = true;
break;
case 'r':
rtrn = true;
break;
} }
LuaThread.State s = t.state; LuaThread.State s = t.state;
s.hookfunc = func; s.hookfunc = func;
@@ -299,8 +339,9 @@ public class DebugLib extends TwoArgFunction {
// debug.setlocal ([thread,] level, local, value) // debug.setlocal ([thread,] level, local, value)
final class setlocal extends VarArgFunction { final class setlocal extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int a=1; int a = 1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running; LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
int level = args.checkint(a++); int level = args.checkint(a++);
int local = args.checkint(a++); int local = args.checkint(a++);
@@ -312,16 +353,30 @@ public class DebugLib extends TwoArgFunction {
// debug.setmetatable (value, table) // debug.setmetatable (value, table)
static final class setmetatable extends TwoArgFunction { static final class setmetatable extends TwoArgFunction {
@Override
public LuaValue call(LuaValue value, LuaValue table) { public LuaValue call(LuaValue value, LuaValue table) {
LuaValue mt = table.opttable(null); LuaValue mt = table.opttable(null);
switch ( value.type() ) { switch (value.type()) {
case TNIL: LuaNil.s_metatable = mt; break; case TNIL:
case TNUMBER: LuaNumber.s_metatable = mt; break; LuaNil.s_metatable = mt;
case TBOOLEAN: LuaBoolean.s_metatable = mt; break; break;
case TSTRING: LuaString.s_metatable = mt; break; case TNUMBER:
case TFUNCTION: LuaFunction.s_metatable = mt; break; LuaNumber.s_metatable = mt;
case TTHREAD: LuaThread.s_metatable = mt; break; break;
default: value.setmetatable( mt ); case TBOOLEAN:
LuaBoolean.s_metatable = mt;
break;
case TSTRING:
LuaString.s_metatable = mt;
break;
case TFUNCTION:
LuaFunction.s_metatable = mt;
break;
case TTHREAD:
LuaThread.s_metatable = mt;
break;
default:
value.setmetatable(mt);
} }
return value; return value;
} }
@@ -329,14 +384,15 @@ public class DebugLib extends TwoArgFunction {
// debug.setupvalue (f, up, value) // debug.setupvalue (f, up, value)
static final class setupvalue extends VarArgFunction { static final class setupvalue extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1); LuaValue value = args.checkvalue(3);
int up = args.checkint(2); int up = args.checkint(2);
LuaValue value = args.arg(3); LuaValue func = args.checkfunction(1);
if ( func instanceof LuaClosure ) { if (func instanceof LuaClosure) {
LuaClosure c = (LuaClosure) func; LuaClosure c = (LuaClosure) func;
LuaString name = findupvalue(c, up); LuaString name = findupvalue(c, up);
if ( name != null ) { if (name != null) {
c.upValues[up-1].setValue(value); c.upValues[up-1].setValue(value);
return name; return name;
} }
@@ -347,6 +403,7 @@ public class DebugLib extends TwoArgFunction {
// debug.setuservalue (udata, value) // debug.setuservalue (udata, value)
static final class setuservalue extends VarArgFunction { static final class setuservalue extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
Object o = args.checkuserdata(1); Object o = args.checkuserdata(1);
LuaValue v = args.checkvalue(2); LuaValue v = args.checkvalue(2);
@@ -359,24 +416,26 @@ public class DebugLib extends TwoArgFunction {
// debug.traceback ([thread,] [message [, level]]) // debug.traceback ([thread,] [message [, level]])
final class traceback extends VarArgFunction { final class traceback extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
int a=1; int a = 1;
LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running; LuaThread thread = args.isthread(a)? args.checkthread(a++): globals.running;
String message = args.optjstring(a++, null); String message = args.optjstring(a++, null);
int level = args.optint(a++,1); int level = args.optint(a++, 1);
String tb = callstack(thread).traceback(level); String tb = callstack(thread).traceback(level);
return valueOf(message!=null? message+"\n"+tb: tb); return valueOf(message != null? message + "\n" + tb: tb);
} }
} }
// debug.upvalueid (f, n) // debug.upvalueid (f, n)
static final class upvalueid extends VarArgFunction { static final class upvalueid extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue func = args.checkfunction(1);
int up = args.checkint(2); int up = args.checkint(2);
if ( func instanceof LuaClosure ) { LuaValue func = args.checkfunction(1);
if (func instanceof LuaClosure) {
LuaClosure c = (LuaClosure) func; LuaClosure c = (LuaClosure) func;
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) { if (c.upValues != null && up > 0 && up <= c.upValues.length) {
return valueOf(c.upValues[up-1].hashCode()); return valueOf(c.upValues[up-1].hashCode());
} }
} }
@@ -386,11 +445,12 @@ public class DebugLib extends TwoArgFunction {
// debug.upvaluejoin (f1, n1, f2, n2) // debug.upvaluejoin (f1, n1, f2, n2)
static final class upvaluejoin extends VarArgFunction { static final class upvaluejoin extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaClosure f1 = args.checkclosure(1);
int n1 = args.checkint(2); int n1 = args.checkint(2);
LuaClosure f2 = args.checkclosure(3); LuaClosure f1 = args.checkclosure(1);
int n2 = args.checkint(4); int n2 = args.checkint(4);
LuaClosure f2 = args.checkclosure(3);
if (n1 < 1 || n1 > f1.upValues.length) if (n1 < 1 || n1 > f1.upValues.length)
argerror("index out of range"); argerror("index out of range");
if (n2 < 1 || n2 > f2.upValues.length) if (n2 < 1 || n2 > f2.upValues.length)
@@ -402,29 +462,35 @@ public class DebugLib extends TwoArgFunction {
public void onCall(LuaFunction f) { public void onCall(LuaFunction f) {
LuaThread.State s = globals.running.state; LuaThread.State s = globals.running.state;
if (s.inhook) return; if (s.inhook)
return;
callstack().onCall(f); callstack().onCall(f);
if (s.hookcall) callHook(s, CALL, NIL); if (s.hookcall)
callHook(s, CALL, NIL);
} }
public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) { public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) {
LuaThread.State s = globals.running.state; LuaThread.State s = globals.running.state;
if (s.inhook) return; if (s.inhook)
return;
callstack().onCall(c, varargs, stack); callstack().onCall(c, varargs, stack);
if (s.hookcall) callHook(s, CALL, NIL); if (s.hookcall)
callHook(s, CALL, NIL);
} }
public void onInstruction(int pc, Varargs v, int top) { public void onInstruction(int pc, Varargs v, int top) {
LuaThread.State s = globals.running.state; LuaThread.State s = globals.running.state;
if (s.inhook) return; if (s.inhook)
return;
callstack().onInstruction(pc, v, top); callstack().onInstruction(pc, v, top);
if (s.hookfunc == null) return; if (s.hookfunc == null)
return;
if (s.hookcount > 0) if (s.hookcount > 0)
if (++s.bytecodes % s.hookcount == 0) if (++s.bytecodes%s.hookcount == 0)
callHook(s, COUNT, NIL); callHook(s, COUNT, NIL);
if (s.hookline) { if (s.hookline) {
int newline = callstack().currentline(); int newline = callstack().currentline();
if ( newline != s.lastline ) { if (newline != s.lastline) {
s.lastline = newline; s.lastline = newline;
callHook(s, LINE, LuaValue.valueOf(newline)); callHook(s, LINE, LuaValue.valueOf(newline));
} }
@@ -433,9 +499,11 @@ public class DebugLib extends TwoArgFunction {
public void onReturn() { public void onReturn() {
LuaThread.State s = globals.running.state; LuaThread.State s = globals.running.state;
if (s.inhook) return; if (s.inhook)
return;
callstack().onReturn(); callstack().onReturn();
if (s.hookrtrn) callHook(s, RETURN, NIL); if (s.hookrtrn)
callHook(s, RETURN, NIL);
} }
public String traceback(int level) { public String traceback(int level) {
@@ -447,7 +515,8 @@ public class DebugLib extends TwoArgFunction {
} }
void callHook(LuaThread.State s, LuaValue type, LuaValue arg) { void callHook(LuaThread.State s, LuaValue type, LuaValue arg) {
if (s.inhook || s.hookfunc == null) return; if (s.inhook || s.hookfunc == null)
return;
s.inhook = true; s.inhook = true;
try { try {
s.hookfunc.call(type, arg); s.hookfunc.call(type, arg);
@@ -479,7 +548,7 @@ public class DebugLib extends TwoArgFunction {
int linedefined; /* (S) */ int linedefined; /* (S) */
int lastlinedefined; /* (S) */ int lastlinedefined; /* (S) */
short nups; /* (u) number of upvalues */ short nups; /* (u) number of upvalues */
short nparams;/* (u) number of parameters */ short nparams; /* (u) number of parameters */
boolean isvararg; /* (u) */ boolean isvararg; /* (u) */
boolean istailcall; /* (t) */ boolean istailcall; /* (t) */
String short_src; /* (S) */ String short_src; /* (S) */
@@ -488,10 +557,10 @@ public class DebugLib extends TwoArgFunction {
public void funcinfo(LuaFunction f) { public void funcinfo(LuaFunction f) {
if (f.isclosure()) { if (f.isclosure()) {
Prototype p = f.checkclosure().p; Prototype p = f.checkclosure().p;
this.source = p.source != null ? p.source.tojstring() : "=?"; this.source = p.source != null? p.source.tojstring(): "=?";
this.linedefined = p.linedefined; this.linedefined = p.linedefined;
this.lastlinedefined = p.lastlinedefined; this.lastlinedefined = p.lastlinedefined;
this.what = (this.linedefined == 0) ? "main" : "Lua"; this.what = this.linedefined == 0? "main": "Lua";
this.short_src = p.shortsource(); this.short_src = p.shortsource();
} else { } else {
this.source = "=[Java]"; this.source = "=[Java]";
@@ -516,7 +585,7 @@ public class DebugLib extends TwoArgFunction {
private synchronized CallFrame pushcall() { private synchronized CallFrame pushcall() {
if (calls >= frame.length) { if (calls >= frame.length) {
int n = Math.max(4, frame.length * 3 / 2); int n = Math.max(4, frame.length*3/2);
CallFrame[] f = new CallFrame[n]; CallFrame[] f = new CallFrame[n];
System.arraycopy(frame, 0, f, 0, frame.length); System.arraycopy(frame, 0, f, 0, frame.length);
for (int i = frame.length; i < n; ++i) for (int i = frame.length; i < n; ++i)
@@ -548,32 +617,33 @@ public class DebugLib extends TwoArgFunction {
/** /**
* Get the traceback starting at a specific level. * Get the traceback starting at a specific level.
*
* @param level * @param level
* @return String containing the traceback. * @return String containing the traceback.
*/ */
synchronized String traceback(int level) { synchronized String traceback(int level) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
sb.append( "stack traceback:" ); sb.append("stack traceback:");
for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) { for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null;) {
sb.append("\n\t"); sb.append("\n\t");
sb.append( c.shortsource() ); sb.append(c.shortsource());
sb.append( ':' ); sb.append(':');
if (c.currentline() > 0) if (c.currentline() > 0)
sb.append( c.currentline()+":" ); sb.append(c.currentline() + ":");
sb.append( " in " ); sb.append(" in ");
DebugInfo ar = auxgetinfo("n", c.f, c); DebugInfo ar = auxgetinfo("n", c.f, c);
if (c.linedefined() == 0) if (c.linedefined() == 0)
sb.append("main chunk"); sb.append("main chunk");
else if ( ar.name != null ) { else if (ar.name != null) {
sb.append( "function '" ); sb.append("function '");
sb.append( ar.name ); sb.append(ar.name);
sb.append( '\'' ); sb.append('\'');
} else { } else {
sb.append( "function <" ); sb.append("function <");
sb.append( c.shortsource() ); sb.append(c.shortsource());
sb.append( ':' ); sb.append(':');
sb.append( c.linedefined() ); sb.append(c.linedefined());
sb.append( '>' ); sb.append('>');
} }
} }
sb.append("\n\t[Java]: in ?"); sb.append("\n\t[Java]: in ?");
@@ -593,7 +663,6 @@ public class DebugLib extends TwoArgFunction {
return null; return null;
} }
synchronized DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) { synchronized DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
DebugInfo ar = new DebugInfo(); DebugInfo ar = new DebugInfo();
for (int i = 0, n = what.length(); i < n; ++i) { for (int i = 0, n = what.length(); i < n; ++i) {
@@ -650,28 +719,35 @@ public class DebugLib extends TwoArgFunction {
} }
public static class CallFrame { public static class CallFrame {
static final LuaValue[] EMPTY = {};
LuaFunction f; LuaFunction f;
int pc; int pc;
int top; int top;
Varargs v; Varargs v;
LuaValue[] stack; LuaValue[] stack = EMPTY;
CallFrame previous; CallFrame previous;
void set(LuaClosure function, Varargs varargs, LuaValue[] stack) { void set(LuaClosure function, Varargs varargs, LuaValue[] stack) {
this.f = function; this.f = function;
this.v = varargs; this.v = varargs;
this.stack = stack; this.stack = stack;
} }
public String shortsource() { public String shortsource() {
return f.isclosure()? f.checkclosure().p.shortsource(): "[Java]"; return f.isclosure()? f.checkclosure().p.shortsource(): "[Java]";
} }
void set(LuaFunction function) { void set(LuaFunction function) {
this.f = function; this.f = function;
} }
void reset() { void reset() {
this.f = null; this.f = null;
this.v = null; this.v = null;
this.stack = null; this.stack = EMPTY;
} }
void instr(int pc, Varargs v, int top) { void instr(int pc, Varargs v, int top) {
this.pc = pc; this.pc = pc;
this.v = v; this.v = v;
@@ -679,57 +755,68 @@ public class DebugLib extends TwoArgFunction {
if (TRACE) if (TRACE)
Print.printState(f.checkclosure(), pc, stack, top, v); Print.printState(f.checkclosure(), pc, stack, top, v);
} }
Varargs getLocal(int i) { Varargs getLocal(int i) {
LuaString name = getlocalname(i); LuaString name = getlocalname(i);
if ( i >= 1 && i <= stack.length && stack[i-1] != null ) if (i >= 1 && i <= stack.length && stack[i-1] != null)
return varargsOf( name == null ? NIL : name, stack[i-1] ); return varargsOf(name == null? NIL: name, stack[i-1]);
else else
return NIL; return NIL;
} }
Varargs setLocal(int i, LuaValue value) { Varargs setLocal(int i, LuaValue value) {
LuaString name = getlocalname(i); LuaString name = getlocalname(i);
if ( i >= 1 && i <= stack.length && stack[i-1] != null ) { if (i >= 1 && i <= stack.length && stack[i-1] != null) {
stack[i-1] = value; stack[i-1] = value;
return name == null ? NIL : name; return name == null? NIL: name;
} else { } else {
return NIL; return NIL;
} }
} }
public int currentline() { public int currentline() {
if ( !f.isclosure() ) return -1; if (!f.isclosure())
return -1;
int[] li = f.checkclosure().p.lineinfo; int[] li = f.checkclosure().p.lineinfo;
return li==null || pc<0 || pc>=li.length? -1: li[pc]; return li == null || pc < 0 || pc >= li.length? -1: li[pc];
} }
String sourceline() { String sourceline() {
if ( !f.isclosure() ) return f.tojstring(); if (!f.isclosure())
return f.tojstring();
return f.checkclosure().p.shortsource() + ":" + currentline(); return f.checkclosure().p.shortsource() + ":" + currentline();
} }
int linedefined() { int linedefined() {
return f.isclosure()? f.checkclosure().p.linedefined: -1; return f.isclosure()? f.checkclosure().p.linedefined: -1;
} }
LuaString getlocalname(int index) { LuaString getlocalname(int index) {
if ( !f.isclosure() ) return null; if (!f.isclosure())
return null;
return f.checkclosure().p.getlocalname(index, pc); return f.checkclosure().p.getlocalname(index, pc);
} }
} }
static LuaString findupvalue(LuaClosure c, int up) { static LuaString findupvalue(LuaClosure c, int up) {
if ( c.upValues != null && up > 0 && up <= c.upValues.length ) { if (c.upValues != null && up > 0 && up <= c.upValues.length) {
if ( c.p.upvalues != null && up <= c.p.upvalues.length ) if (c.p.upvalues != null && up <= c.p.upvalues.length)
return c.p.upvalues[up-1].name; return c.p.upvalues[up-1].name;
else else
return LuaString.valueOf( "."+up ); return LuaString.valueOf("." + up);
} }
return null; return null;
} }
static void lua_assert(boolean x) { static void lua_assert(boolean x) {
if (!x) throw new RuntimeException("lua_assert failed"); if (!x)
throw new RuntimeException("lua_assert failed");
} }
static class NameWhat { static class NameWhat {
final String name; final String name;
final String namewhat; final String namewhat;
NameWhat(String name, String namewhat) { NameWhat(String name, String namewhat) {
this.name = name; this.name = name;
this.namewhat = namewhat; this.namewhat = namewhat;
@@ -753,33 +840,61 @@ public class DebugLib extends TwoArgFunction {
/* all other instructions can call only through metamethods */ /* all other instructions can call only through metamethods */
case Lua.OP_SELF: case Lua.OP_SELF:
case Lua.OP_GETTABUP: case Lua.OP_GETTABUP:
case Lua.OP_GETTABLE: tm = LuaValue.INDEX; break; case Lua.OP_GETTABLE:
tm = LuaValue.INDEX;
break;
case Lua.OP_SETTABUP: case Lua.OP_SETTABUP:
case Lua.OP_SETTABLE: tm = LuaValue.NEWINDEX; break; case Lua.OP_SETTABLE:
case Lua.OP_EQ: tm = LuaValue.EQ; break; tm = LuaValue.NEWINDEX;
case Lua.OP_ADD: tm = LuaValue.ADD; break; break;
case Lua.OP_SUB: tm = LuaValue.SUB; break; case Lua.OP_EQ:
case Lua.OP_MUL: tm = LuaValue.MUL; break; tm = LuaValue.EQ;
case Lua.OP_DIV: tm = LuaValue.DIV; break; break;
case Lua.OP_MOD: tm = LuaValue.MOD; break; case Lua.OP_ADD:
case Lua.OP_POW: tm = LuaValue.POW; break; tm = LuaValue.ADD;
case Lua.OP_UNM: tm = LuaValue.UNM; break; break;
case Lua.OP_LEN: tm = LuaValue.LEN; break; case Lua.OP_SUB:
case Lua.OP_LT: tm = LuaValue.LT; break; tm = LuaValue.SUB;
case Lua.OP_LE: tm = LuaValue.LE; break; break;
case Lua.OP_CONCAT: tm = LuaValue.CONCAT; break; case Lua.OP_MUL:
tm = LuaValue.MUL;
break;
case Lua.OP_DIV:
tm = LuaValue.DIV;
break;
case Lua.OP_MOD:
tm = LuaValue.MOD;
break;
case Lua.OP_POW:
tm = LuaValue.POW;
break;
case Lua.OP_UNM:
tm = LuaValue.UNM;
break;
case Lua.OP_LEN:
tm = LuaValue.LEN;
break;
case Lua.OP_LT:
tm = LuaValue.LT;
break;
case Lua.OP_LE:
tm = LuaValue.LE;
break;
case Lua.OP_CONCAT:
tm = LuaValue.CONCAT;
break;
default: default:
return null; /* else no useful name can be found */ return null; /* else no useful name can be found */
} }
return new NameWhat( tm.tojstring(), "metamethod" ); return new NameWhat(tm.tojstring(), "metamethod");
} }
// return NameWhat if found, null if not // return NameWhat if found, null if not
public static NameWhat getobjname(Prototype p, int lastpc, int reg) { public static NameWhat getobjname(Prototype p, int lastpc, int reg) {
int pc = lastpc; // currentpc(L, ci); int pc = lastpc; // currentpc(L, ci);
LuaString name = p.getlocalname(reg + 1, pc); LuaString name = p.getlocalname(reg+1, pc);
if (name != null) /* is a local? */ if (name != null) /* is a local? */
return new NameWhat( name.tojstring(), "local" ); return new NameWhat(name.tojstring(), "local");
/* else try symbolic execution */ /* else try symbolic execution */
pc = findsetreg(p, lastpc, reg); pc = findsetreg(p, lastpc, reg);
@@ -797,31 +912,30 @@ public class DebugLib extends TwoArgFunction {
case Lua.OP_GETTABLE: { case Lua.OP_GETTABLE: {
int k = Lua.GETARG_C(i); /* key index */ int k = Lua.GETARG_C(i); /* key index */
int t = Lua.GETARG_B(i); /* table index */ int t = Lua.GETARG_B(i); /* table index */
LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE) /* name of indexed variable */ LuaString vn = Lua.GET_OPCODE(i) == Lua.OP_GETTABLE /* name of indexed variable */
? p.getlocalname(t + 1, pc) ? p.getlocalname(t+1, pc)
: (t < p.upvalues.length ? p.upvalues[t].name : QMARK); : t < p.upvalues.length? p.upvalues[t].name: QMARK;
String jname = kname(p, pc, k); String jname = kname(p, pc, k);
return new NameWhat( jname, vn != null && vn.eq_b(ENV)? "global": "field" ); return new NameWhat(jname, vn != null && vn.eq_b(ENV)? "global": "field");
} }
case Lua.OP_GETUPVAL: { case Lua.OP_GETUPVAL: {
int u = Lua.GETARG_B(i); /* upvalue index */ int u = Lua.GETARG_B(i); /* upvalue index */
name = u < p.upvalues.length ? p.upvalues[u].name : QMARK; name = u < p.upvalues.length? p.upvalues[u].name: QMARK;
return name == null ? null : new NameWhat( name.tojstring(), "upvalue" ); return name == null? null: new NameWhat(name.tojstring(), "upvalue");
} }
case Lua.OP_LOADK: case Lua.OP_LOADK:
case Lua.OP_LOADKX: { case Lua.OP_LOADKX: {
int b = (Lua.GET_OPCODE(i) == Lua.OP_LOADK) ? Lua.GETARG_Bx(i) int b = Lua.GET_OPCODE(i) == Lua.OP_LOADK? Lua.GETARG_Bx(i): Lua.GETARG_Ax(p.code[pc+1]);
: Lua.GETARG_Ax(p.code[pc + 1]);
if (p.k[b].isstring()) { if (p.k[b].isstring()) {
name = p.k[b].strvalue(); name = p.k[b].strvalue();
return new NameWhat( name.tojstring(), "constant" ); return new NameWhat(name.tojstring(), "constant");
} }
break; break;
} }
case Lua.OP_SELF: { case Lua.OP_SELF: {
int k = Lua.GETARG_C(i); /* key index */ int k = Lua.GETARG_C(i); /* key index */
String jname = kname(p, pc, k); String jname = kname(p, pc, k);
return new NameWhat( jname, "method" ); return new NameWhat(jname, "method");
} }
default: default:
break; break;
@@ -849,7 +963,7 @@ public class DebugLib extends TwoArgFunction {
/* /*
** try to find last instruction before 'lastpc' that modified register 'reg' ** try to find last instruction before 'lastpc' that modified register 'reg'
*/ */
static int findsetreg (Prototype p, int lastpc, int reg) { static int findsetreg(Prototype p, int lastpc, int reg) {
int pc; int pc;
int setreg = -1; /* keep last instruction that changed 'reg' */ int setreg = -1; /* keep last instruction that changed 'reg' */
for (pc = 0; pc < lastpc; pc++) { for (pc = 0; pc < lastpc; pc++) {
@@ -859,33 +973,37 @@ public class DebugLib extends TwoArgFunction {
switch (op) { switch (op) {
case Lua.OP_LOADNIL: { case Lua.OP_LOADNIL: {
int b = Lua.GETARG_B(i); int b = Lua.GETARG_B(i);
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */ if (a <= reg && reg <= a+b) /* set registers from 'a' to 'a+b' */
setreg = pc; setreg = pc;
break; break;
} }
case Lua.OP_TFORCALL: { case Lua.OP_TFORCALL: {
if (reg >= a + 2) setreg = pc; /* affect all regs above its base */ if (reg >= a+2)
setreg = pc; /* affect all regs above its base */
break; break;
} }
case Lua.OP_CALL: case Lua.OP_CALL:
case Lua.OP_TAILCALL: { case Lua.OP_TAILCALL: {
if (reg >= a) setreg = pc; /* affect all registers above base */ if (reg >= a)
setreg = pc; /* affect all registers above base */
break; break;
} }
case Lua.OP_JMP: { case Lua.OP_JMP: {
int b = Lua.GETARG_sBx(i); int b = Lua.GETARG_sBx(i);
int dest = pc + 1 + b; int dest = pc+1+b;
/* jump is forward and do not skip `lastpc'? */ /* jump is forward and do not skip `lastpc'? */
if (pc < dest && dest <= lastpc) if (pc < dest && dest <= lastpc)
pc += b; /* do the jump */ pc += b; /* do the jump */
break; break;
} }
case Lua.OP_TEST: { case Lua.OP_TEST: {
if (reg == a) setreg = pc; /* jumped code can change 'a' */ if (reg == a)
setreg = pc; /* jumped code can change 'a' */
break; break;
} }
case Lua.OP_SETLIST: { // Lua.testAMode(Lua.OP_SETLIST) == false case Lua.OP_SETLIST: { // Lua.testAMode(Lua.OP_SETLIST) == false
if ( ((i>>14)&0x1ff) == 0 ) pc++; // if c == 0 then c stored in next op -> skip if ((i>>14 & 0x1ff) == 0)
pc++; // if c == 0 then c stored in next op -> skip
break; break;
} }
default: default:

View File

@@ -32,98 +32,128 @@ 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()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * 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 * </pre>
* 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}. * 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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new OsLib()); * globals.load(new OsLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre> * }
* </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 public abstract class IoLib extends TwoArgFunction {
public class IoLib extends TwoArgFunction {
protected abstract class File extends LuaValue {
public abstract void write(LuaString string) throws IOException;
public abstract void flush() throws IOException;
public abstract boolean isstdfile();
public abstract void close() throws IOException;
public abstract boolean isclosed();
abstract
protected class File extends LuaValue{
abstract public void write( LuaString string ) throws IOException;
abstract public void flush() throws IOException;
abstract public boolean isstdfile();
abstract public void close() throws IOException;
abstract public boolean isclosed();
// returns new position // returns new position
abstract public int seek(String option, int bytecount) throws IOException; public abstract int seek(String option, int bytecount) throws IOException;
abstract public void setvbuf(String mode, int size);
public abstract void setvbuf(String mode, int size);
// get length remaining to read // get length remaining to read
abstract public int remaining() throws IOException; public abstract int remaining() throws IOException;
// peek ahead one character // peek ahead one character
abstract public int peek() throws IOException, EOFException; public abstract 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; public abstract 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; public abstract 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 ) { @Override
public LuaValue get(LuaValue key) {
return filemethods.get(key); return filemethods.get(key);
} }
// essentially a userdata instance // essentially a userdata instance
@Override
public int type() { public int type() {
return LuaValue.TUSERDATA; return LuaValue.TUSERDATA;
} }
@Override
public String typename() { public String typename() {
return "userdata"; return "userdata";
} }
// displays as "file" type // displays as "file" type
@Override
public String tojstring() { public String tojstring() {
return "file: " + Integer.toHexString(hashCode()); return "file: " + Integer.toHexString(hashCode());
} }
@Override
public void finalize() { public void finalize() {
if (!isclosed()) { if (!isclosed()) {
try { try {
close(); close();
} catch (IOException ignore) {} } catch (IOException ignore) {
}
} }
} }
} }
@@ -139,6 +169,7 @@ public class IoLib extends TwoArgFunction {
/** /**
* Wrap the standard input. * Wrap the standard input.
*
* @return File * @return File
* @throws IOException * @throws IOException
*/ */
@@ -146,6 +177,7 @@ public class IoLib extends TwoArgFunction {
/** /**
* Wrap the standard output. * Wrap the standard output.
*
* @return File * @return File
* @throws IOException * @throws IOException
*/ */
@@ -153,6 +185,7 @@ public class IoLib extends TwoArgFunction {
/** /**
* Wrap the standard error output. * Wrap the standard error output.
*
* @return File * @return File
* @throws IOException * @throws IOException
*/ */
@@ -160,6 +193,7 @@ public class IoLib extends TwoArgFunction {
/** /**
* 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
@@ -168,10 +202,12 @@ public class IoLib extends TwoArgFunction {
* @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 +215,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
@@ -219,64 +256,47 @@ public class IoLib extends TwoArgFunction {
private static final int IO_INDEX = 18; private static final int IO_INDEX = 18;
private static final int LINES_ITER = 19; private static final int LINES_ITER = 19;
public static final String[] IO_NAMES = { public static final String[] IO_NAMES = { "close", "flush", "input", "lines", "open", "output", "popen", "read",
"close", "tmpfile", "type", "write", };
"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;
@Override
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;
} }
@@ -285,51 +305,74 @@ public class IoLib extends TwoArgFunction {
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();
this.f = f; this.f = f;
this.name = name; this.name = name;
this.opcode = opcode; this.opcode = opcode;
this.iolib = iolib; this.iolib = iolib;
} }
@Override
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 IO_POPEN:
return iolib._io_popen(args.checkjstring(1), args.optjstring(2, "r"));
case IO_OPEN:
return iolib._io_open(args.checkjstring(1), args.optjstring(2, "r"));
case IO_LINES:
return iolib._io_lines(args);
case IO_READ:
return iolib._io_read(args);
case IO_WRITE:
return iolib._io_write(args);
case FILE_CLOSE: return iolib._file_close(args.arg1()); case FILE_CLOSE:
case FILE_FLUSH: return iolib._file_flush(args.arg1()); return iolib._file_close(args.arg1());
case FILE_SETVBUF: return iolib._file_setvbuf(args.arg1(),args.checkjstring(2),args.optint(3,8192)); case FILE_FLUSH:
case FILE_LINES: return iolib._file_lines(args); return iolib._file_flush(args.arg1());
case FILE_READ: return iolib._file_read(args.arg1(),args.subargs(2)); case FILE_SETVBUF:
case FILE_SEEK: return iolib._file_seek(args.arg1(),args.optjstring(2,"cur"),args.optint(3,0)); return iolib._file_setvbuf(args.arg1(), args.checkjstring(2), args.optint(3, 8192));
case FILE_WRITE: return iolib._file_write(args.arg1(),args.subargs(2)); 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 IO_INDEX:
case LINES_ITER: return iolib._lines_iter(f, toclose, this.args); 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);
} }
@@ -338,7 +381,7 @@ public class IoLib extends TwoArgFunction {
} }
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
@@ -362,31 +405,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 +438,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 +446,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 +474,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 +485,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 +496,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,7 +554,7 @@ 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) {
@@ -522,53 +563,62 @@ public class IoLib extends TwoArgFunction {
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: default:
return argerror( i+1, "(invalid format)" ); 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;
} }
@@ -577,7 +627,7 @@ public class IoLib extends TwoArgFunction {
} }
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 +636,113 @@ 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) || (i == 1 && ch == '+'))
if (i == 1 && ch == '+') continue; continue;
if (i >= 1 && ch == 'b') 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,30 +111,38 @@ 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.
*/ */
@@ -138,85 +152,112 @@ abstract public class LibFunction extends LuaFunction {
protected LibFunction() { protected LibFunction() {
} }
@Override
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 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.
* @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 };
} }
@Override
public LuaValue call() { public LuaValue call() {
return argerror(1,"value expected"); return argerror(1, "value expected");
} }
@Override
public LuaValue call(LuaValue a) { public LuaValue call(LuaValue a) {
return call(); return call();
} }
@Override
public LuaValue call(LuaValue a, LuaValue b) { public LuaValue call(LuaValue a, LuaValue b) {
return call(a); return call(a);
} }
@Override
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);
} }
@Override
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,82 @@ 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 *
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* } </pre> * }
* When using {@link org.luaj.vm2.lib.jse.JsePlatform} as in this example, * </pre>
* the subclass {@link org.luaj.vm2.lib.jse.JseMathLib} will *
* be included, which also includes this base functionality. * 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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new MathLib()); * globals.load(new MathLib());
* System.out.println( globals.get("math").get("sqrt").call( LuaValue.valueOf(2) ) ); * System.out.println(globals.get("math").get("sqrt").call(LuaValue.valueOf(2)));
* } </pre> * }
* Doing so will ensure the library is properly initialized * </pre>
* and loaded into the globals table. *
* 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.
*/ */
@Override
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 +132,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,135 +147,204 @@ 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 {
@Override
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);
} }
abstract protected static class BinaryOp extends TwoArgFunction { abstract protected static class BinaryOp extends TwoArgFunction {
@Override
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); } } @Override
static final class cos extends UnaryOp { protected double call(double d) { return Math.cos(d); } } protected double call(double d) { return Math.abs(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 ceil extends UnaryOp {
static final class sin extends UnaryOp { protected double call(double d) { return Math.sin(d); } } @Override
static final class sqrt extends UnaryOp { protected double call(double d) { return Math.sqrt(d); } } protected double call(double d) { return Math.ceil(d); }
static final class tan extends UnaryOp { protected double call(double d) { return Math.tan(d); } } }
static final class cos extends UnaryOp {
@Override
protected double call(double d) { return Math.cos(d); }
}
static final class deg extends UnaryOp {
@Override
protected double call(double d) { return Math.toDegrees(d); }
}
static final class floor extends UnaryOp {
@Override
protected double call(double d) { return Math.floor(d); }
}
static final class rad extends UnaryOp {
@Override
protected double call(double d) { return Math.toRadians(d); }
}
static final class sin extends UnaryOp {
@Override
protected double call(double d) { return Math.sin(d); }
}
static final class sqrt extends UnaryOp {
@Override
protected double call(double d) { return Math.sqrt(d); }
}
static final class tan extends UnaryOp {
@Override
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;
} }
@Override
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 {
@Override
public LuaValue call(LuaValue xv, LuaValue yv) { public LuaValue call(LuaValue xv, LuaValue yv) {
if (yv.checkdouble() == 0.0d)
return LuaDouble.NAN;
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 {
@Override
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 {
@Override
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);
} }
} }
static class frexp extends VarArgFunction { static class frexp extends VarArgFunction {
@Override
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 {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue m = args.checkvalue(1); LuaValue m = args.checknumber(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.checknumber(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 {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue m = args.checkvalue(1); LuaValue m = args.checknumber(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.checknumber(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 {
@Override
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.tonumber(), 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();
@Override
public LuaValue call() { public LuaValue call() {
return valueOf( random.nextDouble() ); return valueOf(random.nextDouble());
} }
@Override
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));
} }
@Override
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;
} }
@Override
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);
@@ -261,42 +352,41 @@ public class MathLib extends TwoArgFunction {
} }
} }
/** 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
@@ -48,24 +50,29 @@ import org.luaj.vm2.Varargs;
*/ */
abstract public class OneArgFunction extends LibFunction { abstract public class OneArgFunction extends LibFunction {
@Override
abstract public LuaValue call(LuaValue arg); abstract public LuaValue call(LuaValue arg);
/** Default constructor */ /** Default constructor */
public OneArgFunction() { public OneArgFunction() {
} }
@Override
public final LuaValue call() { public final LuaValue call() {
return call(NIL); return call(NIL);
} }
@Override
public final LuaValue call(LuaValue arg1, LuaValue arg2) { public final LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(arg1); return call(arg1);
} }
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call(arg1); return call(arg1);
} }
@Override
public Varargs invoke(Varargs varargs) { public Varargs invoke(Varargs varargs) {
return call(varargs.arg1()); return call(varargs.arg1());
} }

View File

@@ -0,0 +1,522 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2.lib;
import java.io.IOException;
import java.time.format.TextStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.luaj.vm2.Buffer;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
/**
* Subclass of {@link LibFunction} which implements the standard lua {@code os}
* library.
* <p>
* It is a usable base with simplified stub functions for library functions that
* cannot be implemented uniformly on Jse and Jme.
* <p>
* This can be installed as-is on either platform, or extended and refined to be
* used in a complete Jse implementation.
* <p>
* Because the nature of the {@code os} library is to encapsulate os-specific
* features, the behavior of these functions varies considerably from their
* counterparts in the C platform.
* <p>
* The following functions have limited implementations of features that are not
* supported well on Jme:
* <ul>
* <li>{@code execute()}</li>
* <li>{@code remove()}</li>
* <li>{@code rename()}</li>
* <li>{@code tmpname()}</li>
* </ul>
* <p>
* 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()}
*
* <pre>
* {
* &#64;code
* 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>
* To instantiate and use it directly, link it into your globals table via
* {@link LuaValue#load(LuaValue)} using code such as:
*
* <pre>
* {
* &#64;code
* Globals globals = new Globals();
* globals.load(new JseBaseLib());
* globals.load(new PackageLib());
* globals.load(new OsLib());
* System.out.println(globals.get("os").get("time").call());
* }
* </pre>
* <p>
*
* @see LibFunction
* @see org.luaj.vm2.lib.jse.JseOsLib
* @see org.luaj.vm2.lib.jse.JsePlatform
* @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>
*/
public class OsLib extends TwoArgFunction {
public static final String TMP_PREFIX = ".luaj";
public static final String TMP_SUFFIX = "tmp";
private static final int CLOCK = 0;
private static final int DATE = 1;
private static final int DIFFTIME = 2;
private static final int EXECUTE = 3;
private static final int EXIT = 4;
private static final int GETENV = 5;
private static final int REMOVE = 6;
private static final int RENAME = 7;
private static final int SETLOCALE = 8;
private static final int TIME = 9;
private static final int TMPNAME = 10;
private static final String[] NAMES = { "clock", "date", "difftime", "execute", "exit", "getenv", "remove",
"rename", "setlocale", "time", "tmpname", };
private static final long t0 = System.currentTimeMillis();
private static long tmpnames = t0;
protected Globals globals;
/**
* Create and OsLib instance.
*/
public OsLib() {
}
/**
* Perform one-time initialization on the library by creating a table
* 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 env the environment to load into, typically a Globals
* instance.
*/
@Override
public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals();
LuaTable os = new LuaTable();
for (int i = 0; i < NAMES.length; ++i)
os.set(NAMES[i], new OsLibFunc(i, NAMES[i]));
env.set("os", os);
if (!env.get("package").isnil())
env.get("package").get("loaded").set("os", os);
return os;
}
class OsLibFunc extends VarArgFunction {
public OsLibFunc(int opcode, String name) {
this.opcode = opcode;
this.name = name;
}
@Override
public Varargs invoke(Varargs args) {
try {
switch (opcode) {
case CLOCK:
return valueOf(clock());
case DATE: {
String s = args.optjstring(1, "%c");
long t = args.isnumber(2)? args.tolong(2): time(null);
if (s.equals("*t")) {
Calendar d = Calendar.getInstance();
d.setTime(new Date(t*1000));
LuaTable tbl = LuaValue.tableOf();
tbl.set("year", LuaValue.valueOf(d.get(Calendar.YEAR)));
tbl.set("month", LuaValue.valueOf(d.get(Calendar.MONTH)+1));
tbl.set("day", LuaValue.valueOf(d.get(Calendar.DAY_OF_MONTH)));
tbl.set("hour", LuaValue.valueOf(d.get(Calendar.HOUR_OF_DAY)));
tbl.set("min", LuaValue.valueOf(d.get(Calendar.MINUTE)));
tbl.set("sec", LuaValue.valueOf(d.get(Calendar.SECOND)));
tbl.set("wday", LuaValue.valueOf(d.get(Calendar.DAY_OF_WEEK)));
tbl.set("yday", LuaValue.valueOf(d.get(0x6))); // Day of year
tbl.set("isdst", LuaValue.valueOf(isDaylightSavingsTime(d)));
return tbl;
}
return valueOf(date(s, t == -1? time(null): t));
}
case DIFFTIME:
return valueOf(difftime(args.checkdouble(1), args.checkdouble(2)));
case EXECUTE:
return execute(args.optjstring(1, null));
case EXIT:
exit(args.optint(1, 0));
return NONE;
case GETENV: {
final String val = getenv(args.checkjstring(1));
return val != null? valueOf(val): NIL;
}
case REMOVE:
remove(args.checkjstring(1));
return LuaValue.TRUE;
case RENAME:
rename(args.checkjstring(1), args.checkjstring(2));
return LuaValue.TRUE;
case SETLOCALE: {
String s = setlocale(args.optjstring(1, null), args.optjstring(2, "all"));
return s != null? valueOf(s): NIL;
}
case TIME:
return valueOf(time(args.opttable(1, null)));
case TMPNAME:
return valueOf(tmpname());
}
return NONE;
} catch (IOException e) {
return varargsOf(NIL, valueOf(e.getMessage()));
}
}
}
/**
* @return an approximation of the amount in seconds of CPU time used by the
* program. For luaj this simple returns the elapsed time since the
* OsLib class was loaded.
*/
protected double clock() {
return (System.currentTimeMillis()-t0)/1000.;
}
/**
* Returns the number of seconds from time t1 to time t2. In POSIX, Windows,
* and some other systems, this value is exactly t2-t1.
*
* @param t2
* @param t1
* @return diffeence in time values, in seconds
*/
protected double difftime(double t2, double t1) {
return t2-t1;
}
/**
* If the time argument is present, this is the time to be formatted (see
* the os.time function for a description of this value). Otherwise, date
* formats the current time.
*
* Date returns the date as a string, formatted according to the same rules
* as ANSII strftime, but without support for %g, %G, or %V.
*
* When called without arguments, date returns a reasonable date and time
* representation that depends on the host system and on the current locale
* (that is, os.date() is equivalent to os.date("%c")).
*
* @param format
* @param timeInSec time since epoch, or -1 if not supplied
* @return a LString or a LTable containing date and time, formatted
* according to the given string format.
*/
private static String date(String format, long timeInSec) {
Calendar d = Calendar.getInstance();
d.setTime(new Date(timeInSec*1000));
if (format.startsWith("!")) {
timeInSec -= timeZoneOffset(d);
d.setTime(new Date(timeInSec*1000));
format = format.substring(1);
}
byte[] fmt = format.getBytes();
final int n = fmt.length;
Buffer result = new Buffer(n);
for (int i = 0; i < n; i++) {
byte c = fmt[i];
switch (c) {
case '\n':
result.append("\n");
break;
case '%':
if (++i >= n)
break;
String conv = Character.toString((char) fmt[i]);
if (CONVERTERS.containsKey(conv)) {
result.append(CONVERTERS.get(conv).convert(d));
} else {
LuaValue.argerror(1, "invalid conversion specifier '%" + conv + "'");
}
break;
default:
result.append(c);
break;
}
}
return result.tojstring();
}
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[] 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 static interface DateConversion {
public String convert(Calendar d);
}
private static final Map<String, DateConversion> CONVERTERS = new HashMap<>();
static {
CONVERTERS.put("%", d -> "%");
CONVERTERS.put("a", d -> WeekdayNameAbbrev[d.get(Calendar.DAY_OF_WEEK)-1]);
CONVERTERS.put("A", d -> WeekdayName[d.get(Calendar.DAY_OF_WEEK)-1]);
CONVERTERS.put("b", d -> MonthNameAbbrev[d.get(Calendar.MONTH)]);
CONVERTERS.put("B", d -> MonthName[d.get(Calendar.MONTH)]);
CONVERTERS.put("c", d -> date("%a %b %e %H:%M:%S %Y", d.getTimeInMillis()/1000L));
CONVERTERS.put("C", d -> String.valueOf(d.get(Calendar.YEAR)).substring(0, 2));
CONVERTERS.put("d", d -> String.valueOf(100+d.get(Calendar.DAY_OF_MONTH)).substring(1));
CONVERTERS.put("D", d -> date("%m/%d/%y", d.getTimeInMillis()/1000L));
CONVERTERS.put("e", d -> String.format("%2d", d.get(Calendar.DAY_OF_MONTH)));
CONVERTERS.put("F", d -> date("%Y-%m-%d", d.getTimeInMillis()/1000L));
CONVERTERS.put("g", d -> String.valueOf(d.get(Calendar.YEAR)).substring(2));
CONVERTERS.put("G", d -> String.valueOf(d.get(Calendar.YEAR)));
CONVERTERS.put("h", d -> MonthNameAbbrev[d.get(Calendar.MONTH)]);
CONVERTERS.put("H", d -> String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)).substring(1));
CONVERTERS.put("I", d -> String.valueOf(100+d.get(Calendar.HOUR_OF_DAY)%12).substring(1));
// day of year
CONVERTERS.put("j", d -> {
Calendar y0 = beginningOfYear(d);
int dayOfYear = (int) ((d.getTimeInMillis()-y0.getTimeInMillis())/(24*3600L*1000L));
return String.valueOf(1001+dayOfYear).substring(1);
});
CONVERTERS.put("m", d -> String.valueOf(101+d.get(Calendar.MONTH)).substring(1));
CONVERTERS.put("M", d -> String.valueOf(100+d.get(Calendar.MINUTE)).substring(1));
CONVERTERS.put("n", d -> "\n");
CONVERTERS.put("p", d -> d.get(Calendar.HOUR_OF_DAY) < 12? "AM": "PM");
CONVERTERS.put("r", d -> date("%I:%M:%S %p", d.getTimeInMillis()/1000L));
CONVERTERS.put("R", d -> date("%H:%M", d.getTimeInMillis()/1000L));
CONVERTERS.put("S", d -> String.valueOf(100+d.get(Calendar.SECOND)).substring(1));
CONVERTERS.put("t", d -> "\t");
CONVERTERS.put("T", d -> date("%H:%M:%S", d.getTimeInMillis()/1000L));
CONVERTERS.put("u", d -> String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
CONVERTERS.put("U", d -> String.valueOf(weekNumber(d, 0)));
CONVERTERS.put("V", d -> String.valueOf(weekNumber(d, 0)));
CONVERTERS.put("w", d -> String.valueOf((d.get(Calendar.DAY_OF_WEEK)+6)%7));
CONVERTERS.put("W", d -> String.valueOf(weekNumber(d, 1)));
CONVERTERS.put("x", d -> date("%m/%d/%y", d.getTimeInMillis()/1000L));
CONVERTERS.put("X", d -> date("%H:%M:%S", d.getTimeInMillis()/1000L));
CONVERTERS.put("y", d -> String.valueOf(d.get(Calendar.YEAR)).substring(2));
CONVERTERS.put("Y", d -> String.valueOf(d.get(Calendar.YEAR)));
CONVERTERS.put("z", d -> {
final int tzo = timeZoneOffset(d)/60;
final int a = Math.abs(tzo);
final String h = String.valueOf(100+a/60).substring(1);
final String m = String.valueOf(100+a%60).substring(1);
return (tzo >= 0? "+": "-")+h+m;
});
CONVERTERS.put("Z", d -> d.getTimeZone().toZoneId().getDisplayName(TextStyle.SHORT, Locale.getDefault()));
}
private static Calendar beginningOfYear(Calendar d) {
Calendar y0 = Calendar.getInstance();
y0.setTime(d.getTime());
y0.set(Calendar.MONTH, 0);
y0.set(Calendar.DAY_OF_MONTH, 1);
y0.set(Calendar.HOUR_OF_DAY, 0);
y0.set(Calendar.MINUTE, 0);
y0.set(Calendar.SECOND, 0);
y0.set(Calendar.MILLISECOND, 0);
return y0;
}
private static int weekNumber(Calendar d, int startDay) {
Calendar y0 = beginningOfYear(d);
y0.set(Calendar.DAY_OF_MONTH, 1+(startDay+8-y0.get(Calendar.DAY_OF_WEEK))%7);
if (y0.after(d)) {
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);
}
long dt = d.getTime().getTime()-y0.getTime().getTime();
return 1+(int) (dt/(7L*24L*3600L*1000L));
}
private static int timeZoneOffset(Calendar d) {
int localStandarTimeMillis = (d.get(Calendar.HOUR_OF_DAY)*3600+d.get(Calendar.MINUTE)*60+d.get(Calendar.SECOND))
*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) {
return timeZoneOffset(d) != d.getTimeZone().getRawOffset()/1000;
}
/**
* This function is equivalent to the C function system. It passes command
* to be executed by an operating system shell. It returns a status code,
* which is system-dependent. If command is absent, then it returns nonzero
* if a shell is available and zero otherwise.
*
* @param command command to pass to the system
*/
protected Varargs execute(String command) {
return varargsOf(NIL, valueOf("exit"), ONE);
}
/**
* Calls the C function exit, with an optional code, to terminate the host
* program.
*
* @param code
*/
protected void exit(int code) {
System.exit(code);
}
/**
* Returns the value of the process environment variable varname, or the
* System property value for varname, or null if the variable is not defined
* in either environment.
*
* The default implementation, which is used by the JmePlatform, only
* queryies System.getProperty().
*
* The JsePlatform overrides this behavior and returns the environment
* variable value using System.getenv() if it exists, or the System property
* value if it does not.
*
* A SecurityException may be thrown if access is not allowed for 'varname'.
*
* @param varname
* @return String value, or null if not defined
*/
protected String getenv(String varname) {
return System.getProperty(varname);
}
/**
* Deletes the file or directory with the given name. Directories must be
* empty to be removed. If this function fails, it throws and IOException
*
* @param filename
* @throws IOException if it fails
*/
protected void remove(String filename) throws IOException {
throw new IOException("not implemented");
}
/**
* Renames file or directory named oldname to newname. If this function
* fails,it throws and IOException
*
* @param oldname old file name
* @param newname new file name
* @throws IOException if it fails
*/
protected void rename(String oldname, String newname) throws IOException {
throw new IOException("not implemented");
}
/**
* Sets the current locale of the program. locale is a string specifying a
* locale; category is an optional string describing which category to
* change: "all", "collate", "ctype", "monetary", "numeric", or "time"; the
* default category is "all".
*
* If locale is the empty string, the current locale is set to an
* implementation- defined native locale. If locale is the string "C", the
* current locale is set to the standard C locale.
*
* When called with null as the first argument, this function only returns
* the name of the current locale for the given category.
*
* @param locale
* @param category
* @return the name of the new locale, or null if the request cannot be
* honored.
*/
protected String setlocale(String locale, String category) {
return "C";
}
/**
* Returns the current time when called without arguments, or a time
* representing the date and time specified by the given table. This table
* must have fields year, month, and day, and may have fields hour, min,
* sec, and isdst (for a description of these fields, see the os.date
* function).
*
* @param table
* @return long value for the time
*/
protected long time(LuaTable table) {
java.util.Date d;
if (table == null) {
d = new java.util.Date();
} else {
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, table.get("year").checkint());
c.set(Calendar.MONTH, table.get("month").checkint()-1);
c.set(Calendar.DAY_OF_MONTH, table.get("day").checkint());
c.set(Calendar.HOUR_OF_DAY, table.get("hour").optint(12));
c.set(Calendar.MINUTE, table.get("min").optint(0));
c.set(Calendar.SECOND, table.get("sec").optint(0));
c.set(Calendar.MILLISECOND, 0);
d = c.getTime();
}
return d.getTime()/1000L;
}
/**
* Returns a string with a file name that can be used for a temporary file.
* The file must be explicitly opened before its use and explicitly removed
* when no longer needed.
*
* On some systems (POSIX), this function also creates a file with that
* name, to avoid security risks. (Someone else might create the file with
* wrong permissions in the time between getting the name and creating the
* file.) You still have to open the file to use it and to remove it (even
* if you do not use it).
*
* @return String filename to use
*/
protected String tmpname() {
synchronized (OsLib.class) {
return TMP_PREFIX+tmpnames+++TMP_SUFFIX;
}
}
}

View File

@@ -22,6 +22,7 @@
package org.luaj.vm2.lib; package org.luaj.vm2.lib;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.FileSystems;
import org.luaj.vm2.Globals; import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaFunction; import org.luaj.vm2.LuaFunction;
@@ -31,56 +32,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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* System.out.println( globals.get("require").call("foo") ); * System.out.println(globals.get("require").call("foo"));
* } </pre> * }
* <h3>Limitations</h3> * </pre>
* 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 * <h3>Limitations</h3> This library has been implemented to match as closely as
* as outlined in the {@link BaseLib} and {@link org.luaj.vm2.lib.jse.JseBaseLib} documentation. * 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;
@@ -101,6 +119,7 @@ public class PackageLib extends TwoArgFunction {
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");
static final LuaString _SEEALL = valueOf("seeall");
/** The globals that were used to load this library. */ /** The globals that were used to load this library. */
Globals globals; Globals globals;
@@ -111,25 +130,35 @@ public class PackageLib extends TwoArgFunction {
/** 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 = FileSystems.getDefault().getSeparator();
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.
*/ */
@Override
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
globals = env.checkglobals(); globals = env.checkglobals();
globals.set("require", new require()); globals.set("require", new require());
@@ -139,6 +168,7 @@ public class PackageLib extends TwoArgFunction {
package_.set(_PATH, LuaValue.valueOf(DEFAULT_LUA_PATH)); package_.set(_PATH, LuaValue.valueOf(DEFAULT_LUA_PATH));
package_.set(_LOADLIB, new loadlib()); package_.set(_LOADLIB, new loadlib());
package_.set(_SEARCHPATH, new searchpath()); package_.set(_SEARCHPATH, new searchpath());
package_.set(_SEEALL, new seeall());
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());
@@ -156,13 +186,15 @@ public class PackageLib extends TwoArgFunction {
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));
} }
@Override
public String tojstring() { public String tojstring() {
return "package"; return "package";
} }
@@ -172,38 +204,44 @@ public class PackageLib extends TwoArgFunction {
/** /**
* 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 ) { @Override
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;
} }
@@ -211,55 +249,56 @@ public class PackageLib extends TwoArgFunction {
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 ) { @Override
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"));
} }
} }
public class preload_searcher extends VarArgFunction { public class preload_searcher extends VarArgFunction {
@Override
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 {
@Override
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.
@@ -272,15 +311,16 @@ public class PackageLib extends TwoArgFunction {
// 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()));
} }
} }
public class searchpath extends VarArgFunction { public class searchpath extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
String name = args.checkjstring(1); String name = args.checkjstring(1);
String path = args.checkjstring(2); String path = args.checkjstring(2);
@@ -296,80 +336,92 @@ public class PackageLib extends TwoArgFunction {
// 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 seeall extends OneArgFunction {
@Override
public LuaValue call(LuaValue arg) {
LuaTable mt = new LuaTable();
mt.set(LuaValue.INDEX, globals);
arg.checktable().setmetatable(mt);
return LuaValue.NONE;
}
}
public class java_searcher extends VarArgFunction { public class java_searcher extends VarArgFunction {
@Override
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

@@ -28,16 +28,17 @@ 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
@@ -55,5 +56,5 @@ public interface ResourceFinder {
* @param filename * @param filename
* @return InputStream, or null if not found. * @return InputStream, or null if not found.
*/ */
public InputStream findResource( String filename ); InputStream findResource(String filename);
} }

View File

@@ -699,6 +699,8 @@ public class StringLib extends TwoArgFunction {
int lastmatch = -1; /* end of last match */ int lastmatch = -1; /* end of last match */
LuaValue repl = args.arg( 3 ); LuaValue repl = args.arg( 3 );
int max_s = args.optint( 4, srclen + 1 ); int max_s = args.optint( 4, srclen + 1 );
if (max_s < 0)
max_s = srclen+1;
final boolean anchor = p.length() > 0 && p.charAt( 0 ) == '^'; final boolean anchor = p.length() > 0 && p.charAt( 0 ) == '^';
Buffer lbuf = new Buffer( srclen ); Buffer lbuf = new Buffer( srclen );

View File

@@ -26,41 +26,58 @@ 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()}
*
* <pre>
* {
* &#64;code
* Globals globals = JsePlatform.standardGlobals(); * Globals globals = JsePlatform.standardGlobals();
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) ); * System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* } </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JseBaseLib()); * globals.load(new JseBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new TableLib()); * globals.load(new TableLib());
* System.out.println( globals.get("table").get("length").call( LuaValue.tableOf() ) ); * System.out.println(globals.get("table").get("length").call(LuaValue.tableOf()));
* } </pre> * }
* </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.
*/ */
@Override
public LuaValue call(LuaValue modname, LuaValue env) { public LuaValue call(LuaValue modname, LuaValue env) {
LuaTable table = new LuaTable(); LuaTable table = new LuaTable();
table.set("concat", new concat()); table.set("concat", new concat());
@@ -70,40 +87,50 @@ 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 {
@Override
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());
} }
@Override
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());
} }
@Override
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());
} }
@Override
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());
} }
} }
// "insert" (table, [pos,] value) // "insert" (table, [pos,] value)
static class insert extends VarArgFunction { static class insert extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
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;
} }
@@ -116,6 +143,7 @@ public class TableLib extends TwoArgFunction {
// "pack" (...) -> table // "pack" (...) -> table
static class pack extends VarArgFunction { static class pack extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
LuaValue t = tableOf(args, 1); LuaValue t = tableOf(args, 1);
t.set("n", args.narg()); t.set("n", args.narg());
@@ -125,12 +153,13 @@ public class TableLib extends TwoArgFunction {
// "remove" (table [, pos]) -> removed-ele // "remove" (table [, pos]) -> removed-ele
static class remove extends VarArgFunction { static class remove extends VarArgFunction {
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
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);
} }
@@ -138,20 +167,20 @@ public class TableLib extends TwoArgFunction {
// "sort" (table [, comp]) // "sort" (table [, comp])
static class sort extends VarArgFunction { static class sort extends VarArgFunction {
@Override
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 {
@Override
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

@@ -3,6 +3,7 @@ package org.luaj.vm2.lib;
import org.luaj.vm2.LuaValue; import org.luaj.vm2.LuaValue;
class TableLibFunction extends LibFunction { class TableLibFunction extends LibFunction {
@Override
public LuaValue call() { public LuaValue call() {
return argerror(1, "table expected, got no value"); return argerror(1, "table expected, got no value");
} }

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
@@ -48,26 +51,31 @@ import org.luaj.vm2.Varargs;
*/ */
abstract public class ThreeArgFunction extends LibFunction { abstract public class ThreeArgFunction extends LibFunction {
@Override
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() {
} }
@Override
public final LuaValue call() { public final LuaValue call() {
return call(NIL, NIL, NIL); return call(NIL, NIL, NIL);
} }
@Override
public final LuaValue call(LuaValue arg) { public final LuaValue call(LuaValue arg) {
return call(arg, NIL, NIL); return call(arg, NIL, NIL);
} }
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(arg1, arg2, NIL); return call(arg1, arg2, NIL);
} }
@Override
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
@@ -48,26 +51,31 @@ import org.luaj.vm2.Varargs;
*/ */
abstract public class TwoArgFunction extends LibFunction { abstract public class TwoArgFunction extends LibFunction {
@Override
abstract public LuaValue call(LuaValue arg1, LuaValue arg2); abstract public LuaValue call(LuaValue arg1, LuaValue arg2);
/** Default constructor */ /** Default constructor */
public TwoArgFunction() { public TwoArgFunction() {
} }
@Override
public final LuaValue call() { public final LuaValue call() {
return call(NIL, NIL); return call(NIL, NIL);
} }
@Override
public final LuaValue call(LuaValue arg) { public final LuaValue call(LuaValue arg) {
return call(arg, NIL); return call(arg, NIL);
} }
@Override
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);
} }
@Override
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
@@ -50,33 +53,39 @@ abstract public class VarArgFunction extends LibFunction {
public VarArgFunction() { public VarArgFunction() {
} }
@Override
public LuaValue call() { public LuaValue call() {
return invoke(NONE).arg1(); return invoke(NONE).arg1();
} }
@Override
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return invoke(arg).arg1(); return invoke(arg).arg1();
} }
@Override
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();
} }
@Override
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.
*/ */
@Override
public Varargs invoke(Varargs args) { public Varargs invoke(Varargs args) {
return onInvoke(args).eval(); return onInvoke(args).eval();
} }
@Override
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
@@ -46,24 +48,29 @@ import org.luaj.vm2.Varargs;
*/ */
abstract public class ZeroArgFunction extends LibFunction { abstract public class ZeroArgFunction extends LibFunction {
@Override
abstract public LuaValue call(); abstract public LuaValue call();
/** Default constructor */ /** Default constructor */
public ZeroArgFunction() { public ZeroArgFunction() {
} }
@Override
public LuaValue call(LuaValue arg) { public LuaValue call(LuaValue arg) {
return call(); return call();
} }
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) { public LuaValue call(LuaValue arg1, LuaValue arg2) {
return call(); return call();
} }
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
return call(); return call();
} }
@Override
public Varargs invoke(Varargs varargs) { public Varargs invoke(Varargs varargs) {
return call(); return call();
} }

View File

@@ -21,33 +21,29 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import junit.framework.TestCase; import org.junit.jupiter.api.Test;
import org.luaj.vm2.Globals.BufferedStream; import org.luaj.vm2.Globals.BufferedStream;
class BufferedStreamTest {
public class BufferedStreamTest extends TestCase {
public BufferedStreamTest() {}
private BufferedStream NewBufferedStream(int buflen, String contents) { private BufferedStream NewBufferedStream(int buflen, String contents) {
return new BufferedStream(buflen, new ByteArrayInputStream(contents.getBytes())); return new BufferedStream(buflen, new ByteArrayInputStream(contents.getBytes()));
} }
protected void setUp() throws Exception { @Test
super.setUp(); void testReadEmptyStream() throws java.io.IOException {
}
public void testReadEmptyStream() throws java.io.IOException {
BufferedStream bs = NewBufferedStream(4, ""); BufferedStream bs = NewBufferedStream(4, "");
assertEquals(-1, bs.read()); assertEquals(-1, bs.read());
assertEquals(-1, bs.read(new byte[10])); assertEquals(-1, bs.read(new byte[10]));
assertEquals(-1, bs.read(new byte[10], 0, 10)); assertEquals(-1, bs.read(new byte[10], 0, 10));
} }
public void testReadByte() throws java.io.IOException { @Test
void testReadByte() throws java.io.IOException {
BufferedStream bs = NewBufferedStream(2, "abc"); BufferedStream bs = NewBufferedStream(2, "abc");
assertEquals('a', bs.read()); assertEquals('a', bs.read());
assertEquals('b', bs.read()); assertEquals('b', bs.read());
@@ -55,7 +51,8 @@ public class BufferedStreamTest extends TestCase {
assertEquals(-1, bs.read()); assertEquals(-1, bs.read());
} }
public void testReadByteArray() throws java.io.IOException { @Test
void testReadByteArray() throws java.io.IOException {
byte[] array = new byte[3]; byte[] array = new byte[3];
BufferedStream bs = NewBufferedStream(4, "abcdef"); BufferedStream bs = NewBufferedStream(4, "abcdef");
assertEquals(3, bs.read(array)); assertEquals(3, bs.read(array));
@@ -67,7 +64,8 @@ public class BufferedStreamTest extends TestCase {
assertEquals(-1, bs.read()); assertEquals(-1, bs.read());
} }
public void testReadByteArrayOffsetLength() throws java.io.IOException { @Test
void testReadByteArrayOffsetLength() throws java.io.IOException {
byte[] array = new byte[10]; byte[] array = new byte[10];
BufferedStream bs = NewBufferedStream(8, "abcdefghijklmn"); BufferedStream bs = NewBufferedStream(8, "abcdefghijklmn");
assertEquals(4, bs.read(array, 0, 4)); assertEquals(4, bs.read(array, 0, 4));
@@ -79,7 +77,8 @@ public class BufferedStreamTest extends TestCase {
assertEquals(-1, bs.read()); assertEquals(-1, bs.read());
} }
public void testMarkOffsetBeginningOfStream() throws java.io.IOException { @Test
void testMarkOffsetBeginningOfStream() throws java.io.IOException {
byte[] array = new byte[4]; byte[] array = new byte[4];
BufferedStream bs = NewBufferedStream(8, "abcdefghijkl"); BufferedStream bs = NewBufferedStream(8, "abcdefghijkl");
assertEquals(true, bs.markSupported()); assertEquals(true, bs.markSupported());
@@ -96,7 +95,8 @@ public class BufferedStreamTest extends TestCase {
assertEquals(-1, bs.read()); assertEquals(-1, bs.read());
} }
public void testMarkOffsetMiddleOfStream() throws java.io.IOException { @Test
void testMarkOffsetMiddleOfStream() throws java.io.IOException {
byte[] array = new byte[4]; byte[] array = new byte[4];
BufferedStream bs = NewBufferedStream(8, "abcdefghijkl"); BufferedStream bs = NewBufferedStream(8, "abcdefghijkl");
assertEquals(true, bs.markSupported()); assertEquals(true, bs.markSupported());

View File

@@ -0,0 +1,135 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.lang.reflect.InvocationTargetException;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.TypeTest.MyData;
import org.luaj.vm2.lib.ZeroArgFunction;
class LuaOperationsTest {
private final int sampleint = 77;
private final long samplelong = 123400000000L;
private final double sampledouble = 55.25;
private final String samplestringstring = "abcdef";
private final String samplestringint = String.valueOf(sampleint);
private final String samplestringlong = String.valueOf(samplelong);
private final String samplestringdouble = String.valueOf(sampledouble);
private final Object sampleobject = new Object();
private final MyData sampledata = new MyData();
private final LuaValue somenil = LuaValue.NIL;
private final LuaValue sometrue = LuaValue.TRUE;
private final LuaValue somefalse = LuaValue.FALSE;
private final LuaValue zero = LuaValue.ZERO;
private final LuaValue intint = LuaValue.valueOf(sampleint);
private final LuaValue longdouble = LuaValue.valueOf(samplelong);
private final LuaValue doubledouble = LuaValue.valueOf(sampledouble);
private final LuaValue stringstring = LuaValue.valueOf(samplestringstring);
private final LuaValue stringint = LuaValue.valueOf(samplestringint);
private final LuaValue stringlong = LuaValue.valueOf(samplestringlong);
private final LuaValue stringdouble = LuaValue.valueOf(samplestringdouble);
private final LuaTable table = LuaValue
.listOf(new LuaValue[] { LuaValue.valueOf("aaa"), LuaValue.valueOf("bbb") });
private final LuaValue somefunc = new ZeroArgFunction() {
@Override
public LuaValue call() { return NONE; }
};
private final LuaThread thread = new LuaThread(new Globals(), somefunc);
private final Prototype proto = new Prototype(1);
private final LuaClosure someclosure = new LuaClosure(proto, table);
private final LuaUserdata userdataobj = LuaValue.userdataOf(sampleobject);
private final LuaUserdata userdatacls = LuaValue.userdataOf(sampledata);
private void throwsLuaError(String methodName, Object obj) {
try {
LuaValue.class.getMethod(methodName).invoke(obj);
fail("failed to throw LuaError as required");
} catch (InvocationTargetException e) {
if (!(e.getTargetException() instanceof LuaError))
fail("not a LuaError: " + e.getTargetException());
return; // pass
} catch (Exception e) {
fail("bad exception: " + e);
}
}
private void throwsLuaError(String methodName, Object obj, Object arg) {
try {
LuaValue.class.getMethod(methodName, LuaValue.class).invoke(obj, arg);
fail("failed to throw LuaError as required");
} catch (InvocationTargetException e) {
if (!(e.getTargetException() instanceof LuaError))
fail("not a LuaError: " + e.getTargetException());
return; // pass
} catch (Exception e) {
fail("bad exception: " + e);
}
}
@Test
void testLen() {
throwsLuaError("len", somenil);
throwsLuaError("len", sometrue);
throwsLuaError("len", somefalse);
throwsLuaError("len", zero);
throwsLuaError("len", intint);
throwsLuaError("len", longdouble);
throwsLuaError("len", doubledouble);
assertEquals(LuaInteger.valueOf(samplestringstring.length()), stringstring.len());
assertEquals(LuaInteger.valueOf(samplestringint.length()), stringint.len());
assertEquals(LuaInteger.valueOf(samplestringlong.length()), stringlong.len());
assertEquals(LuaInteger.valueOf(samplestringdouble.length()), stringdouble.len());
assertEquals(LuaInteger.valueOf(2), table.len());
throwsLuaError("len", somefunc);
throwsLuaError("len", thread);
throwsLuaError("len", someclosure);
throwsLuaError("len", userdataobj);
throwsLuaError("len", userdatacls);
}
@Test
void testLength() {
throwsLuaError("length", somenil);
throwsLuaError("length", sometrue);
throwsLuaError("length", somefalse);
throwsLuaError("length", zero);
throwsLuaError("length", intint);
throwsLuaError("length", longdouble);
throwsLuaError("length", doubledouble);
assertEquals(samplestringstring.length(), stringstring.length());
assertEquals(samplestringint.length(), stringint.length());
assertEquals(samplestringlong.length(), stringlong.length());
assertEquals(samplestringdouble.length(), stringdouble.length());
assertEquals(2, table.length());
throwsLuaError("length", somefunc);
throwsLuaError("length", thread);
throwsLuaError("length", someclosure);
throwsLuaError("length", userdataobj);
throwsLuaError("length", userdatacls);
}
}

View File

@@ -0,0 +1,372 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.TypeTest.MyData;
import org.luaj.vm2.lib.StringLib;
import org.luaj.vm2.lib.ThreeArgFunction;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.ZeroArgFunction;
class MetatableTest {
private final String samplestring = "abcdef";
private final Object sampleobject = new Object();
private final MyData sampledata = new MyData();
private final LuaValue string = LuaValue.valueOf(samplestring);
private final LuaTable table = LuaValue.tableOf();
private final LuaFunction function = new ZeroArgFunction() {
@Override
public LuaValue call() { return NONE; }
};
private final LuaThread thread = new LuaThread(new Globals(), function);
private final LuaClosure closure = new LuaClosure(new Prototype(), new LuaTable());
private final LuaUserdata userdata = LuaValue.userdataOf(sampleobject);
private final LuaUserdata userdatamt = LuaValue.userdataOf(sampledata, table);
@BeforeEach
protected void setUp() throws Exception {
// needed for metatable ops to work on strings
new StringLib();
}
@AfterEach
protected void tearDown() throws Exception {
LuaBoolean.s_metatable = null;
LuaFunction.s_metatable = null;
LuaNil.s_metatable = null;
LuaNumber.s_metatable = null;
// LuaString.s_metatable = null;
LuaThread.s_metatable = null;
}
@Test
void testGetMetatable() {
assertEquals(null, LuaValue.NIL.getmetatable());
assertEquals(null, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, table.getmetatable());
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
assertEquals(null, userdata.getmetatable());
assertEquals(table, userdatamt.getmetatable());
}
@Test
void testSetMetatable() {
LuaValue mt = LuaValue.tableOf();
assertEquals(null, table.getmetatable());
assertEquals(null, userdata.getmetatable());
assertEquals(table, userdatamt.getmetatable());
assertEquals(table, table.setmetatable(mt));
assertEquals(userdata, userdata.setmetatable(mt));
assertEquals(userdatamt, userdatamt.setmetatable(mt));
assertEquals(mt, table.getmetatable());
assertEquals(mt, userdata.getmetatable());
assertEquals(mt, userdatamt.getmetatable());
// these all get metatable behind-the-scenes
assertEquals(null, LuaValue.NIL.getmetatable());
assertEquals(null, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaNil.s_metatable = mt;
assertEquals(mt, LuaValue.NIL.getmetatable());
assertEquals(null, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaBoolean.s_metatable = mt;
assertEquals(mt, LuaValue.TRUE.getmetatable());
assertEquals(null, LuaValue.ONE.getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaNumber.s_metatable = mt;
assertEquals(mt, LuaValue.ONE.getmetatable());
assertEquals(mt, LuaValue.valueOf(1.25).getmetatable());
// assertEquals( null, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
// LuaString.s_metatable = mt;
// assertEquals( mt, string.getmetatable() );
assertEquals(null, function.getmetatable());
assertEquals(null, thread.getmetatable());
assertEquals(null, closure.getmetatable());
LuaFunction.s_metatable = mt;
assertEquals(mt, function.getmetatable());
assertEquals(null, thread.getmetatable());
LuaThread.s_metatable = mt;
assertEquals(mt, thread.getmetatable());
}
@Test
void testMetatableIndex() {
assertEquals(table, table.setmetatable(null));
assertEquals(userdata, userdata.setmetatable(null));
assertEquals(userdatamt, userdatamt.setmetatable(null));
assertEquals(LuaValue.NIL, table.get(1));
assertEquals(LuaValue.NIL, userdata.get(1));
assertEquals(LuaValue.NIL, userdatamt.get(1));
// empty metatable
LuaValue mt = LuaValue.tableOf();
assertEquals(table, table.setmetatable(mt));
assertEquals(userdata, userdata.setmetatable(mt));
LuaBoolean.s_metatable = mt;
LuaFunction.s_metatable = mt;
LuaNil.s_metatable = mt;
LuaNumber.s_metatable = mt;
// LuaString.s_metatable = mt;
LuaThread.s_metatable = mt;
assertEquals(mt, table.getmetatable());
assertEquals(mt, userdata.getmetatable());
assertEquals(mt, LuaValue.NIL.getmetatable());
assertEquals(mt, LuaValue.TRUE.getmetatable());
assertEquals(mt, LuaValue.ONE.getmetatable());
// assertEquals( StringLib.instance, string.getmetatable() );
assertEquals(mt, function.getmetatable());
assertEquals(mt, thread.getmetatable());
// plain metatable
LuaValue abc = LuaValue.valueOf("abc");
mt.set(LuaValue.INDEX, LuaValue.listOf(new LuaValue[] { abc }));
assertEquals(abc, table.get(1));
assertEquals(abc, userdata.get(1));
assertEquals(abc, LuaValue.NIL.get(1));
assertEquals(abc, LuaValue.TRUE.get(1));
assertEquals(abc, LuaValue.ONE.get(1));
// assertEquals( abc, string.get(1) );
assertEquals(abc, function.get(1));
assertEquals(abc, thread.get(1));
// plain metatable
mt.set(LuaValue.INDEX, new TwoArgFunction() {
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2) {
return LuaValue.valueOf(arg1.typename() + "[" + arg2.tojstring() + "]=xyz");
}
});
assertEquals("table[1]=xyz", table.get(1).tojstring());
assertEquals("userdata[1]=xyz", userdata.get(1).tojstring());
assertEquals("nil[1]=xyz", LuaValue.NIL.get(1).tojstring());
assertEquals("boolean[1]=xyz", LuaValue.TRUE.get(1).tojstring());
assertEquals("number[1]=xyz", LuaValue.ONE.get(1).tojstring());
// assertEquals( "string[1]=xyz", string.get(1).tojstring() );
assertEquals("function[1]=xyz", function.get(1).tojstring());
assertEquals("thread[1]=xyz", thread.get(1).tojstring());
}
@Test
void testMetatableNewIndex() {
// empty metatable
LuaValue mt = LuaValue.tableOf();
assertEquals(table, table.setmetatable(mt));
assertEquals(userdata, userdata.setmetatable(mt));
LuaBoolean.s_metatable = mt;
LuaFunction.s_metatable = mt;
LuaNil.s_metatable = mt;
LuaNumber.s_metatable = mt;
// LuaString.s_metatable = mt;
LuaThread.s_metatable = mt;
// plain metatable
final LuaValue fallback = LuaValue.tableOf();
LuaValue abc = LuaValue.valueOf("abc");
mt.set(LuaValue.NEWINDEX, fallback);
table.set(2, abc);
userdata.set(3, abc);
LuaValue.NIL.set(4, abc);
LuaValue.TRUE.set(5, abc);
LuaValue.ONE.set(6, abc);
// string.set(7,abc);
function.set(8, abc);
thread.set(9, abc);
assertEquals(abc, fallback.get(2));
assertEquals(abc, fallback.get(3));
assertEquals(abc, fallback.get(4));
assertEquals(abc, fallback.get(5));
assertEquals(abc, fallback.get(6));
// assertEquals( abc, StringLib.instance.get(7) );
assertEquals(abc, fallback.get(8));
assertEquals(abc, fallback.get(9));
// metatable with function call
mt.set(LuaValue.NEWINDEX, new ThreeArgFunction() {
@Override
public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) {
fallback.rawset(arg2, LuaValue.valueOf("via-func-" + arg3));
return NONE;
}
});
table.set(12, abc);
userdata.set(13, abc);
LuaValue.NIL.set(14, abc);
LuaValue.TRUE.set(15, abc);
LuaValue.ONE.set(16, abc);
// string.set(17,abc);
function.set(18, abc);
thread.set(19, abc);
LuaValue via = LuaValue.valueOf("via-func-abc");
assertEquals(via, fallback.get(12));
assertEquals(via, fallback.get(13));
assertEquals(via, fallback.get(14));
assertEquals(via, fallback.get(15));
assertEquals(via, fallback.get(16));
// assertEquals( via, StringLib.instance.get(17) );
assertEquals(via, fallback.get(18));
assertEquals(via, fallback.get(19));
}
private void checkTable(LuaValue t, LuaValue aa, LuaValue bb, LuaValue cc, LuaValue dd, LuaValue ee, LuaValue ff,
LuaValue gg, LuaValue ra, LuaValue rb, LuaValue rc, LuaValue rd, LuaValue re, LuaValue rf, LuaValue rg) {
assertEquals(aa, t.get("aa"));
assertEquals(bb, t.get("bb"));
assertEquals(cc, t.get("cc"));
assertEquals(dd, t.get("dd"));
assertEquals(ee, t.get("ee"));
assertEquals(ff, t.get("ff"));
assertEquals(gg, t.get("gg"));
assertEquals(ra, t.rawget("aa"));
assertEquals(rb, t.rawget("bb"));
assertEquals(rc, t.rawget("cc"));
assertEquals(rd, t.rawget("dd"));
assertEquals(re, t.rawget("ee"));
assertEquals(rf, t.rawget("ff"));
assertEquals(rg, t.rawget("gg"));
}
private LuaValue makeTable(String key1, String val1, String key2, String val2) {
return LuaValue.tableOf(new LuaValue[] { LuaValue.valueOf(key1), LuaValue.valueOf(val1), LuaValue.valueOf(key2),
LuaValue.valueOf(val2), });
}
@Test
void testRawsetMetatableSet() {
// set up tables
LuaValue m = makeTable("aa", "aaa", "bb", "bbb");
m.set(LuaValue.INDEX, m);
m.set(LuaValue.NEWINDEX, m);
LuaValue s = makeTable("cc", "ccc", "dd", "ddd");
LuaValue t = makeTable("cc", "ccc", "dd", "ddd");
t.setmetatable(m);
LuaValue aaa = LuaValue.valueOf("aaa");
LuaValue bbb = LuaValue.valueOf("bbb");
LuaValue ccc = LuaValue.valueOf("ccc");
LuaValue ddd = LuaValue.valueOf("ddd");
LuaValue ppp = LuaValue.valueOf("ppp");
LuaValue qqq = LuaValue.valueOf("qqq");
LuaValue rrr = LuaValue.valueOf("rrr");
LuaValue sss = LuaValue.valueOf("sss");
LuaValue ttt = LuaValue.valueOf("ttt");
LuaValue www = LuaValue.valueOf("www");
LuaValue xxx = LuaValue.valueOf("xxx");
LuaValue yyy = LuaValue.valueOf("yyy");
LuaValue zzz = LuaValue.valueOf("zzz");
LuaValue nil = LuaValue.NIL;
// check initial values
// values via "bet()" values via "rawget()"
checkTable(s, nil, nil, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(t, aaa, bbb, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
// rawset()
s.rawset("aa", www);
checkTable(s, www, nil, ccc, ddd, nil, nil, nil, www, nil, ccc, ddd, nil, nil, nil);
checkTable(t, aaa, bbb, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
s.rawset("cc", xxx);
checkTable(s, www, nil, xxx, ddd, nil, nil, nil, www, nil, xxx, ddd, nil, nil, nil);
checkTable(t, aaa, bbb, ccc, ddd, nil, nil, nil, nil, nil, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
t.rawset("bb", yyy);
checkTable(s, www, nil, xxx, ddd, nil, nil, nil, www, nil, xxx, ddd, nil, nil, nil);
checkTable(t, aaa, yyy, ccc, ddd, nil, nil, nil, nil, yyy, ccc, ddd, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
t.rawset("dd", zzz);
checkTable(s, www, nil, xxx, ddd, nil, nil, nil, www, nil, xxx, ddd, nil, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, nil, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
// set() invoking metatables
s.set("ee", ppp);
checkTable(s, www, nil, xxx, ddd, ppp, nil, nil, www, nil, xxx, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, nil, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
s.set("cc", qqq);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, nil, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, nil, nil, aaa, bbb, nil, nil, nil, nil, nil);
t.set("ff", rrr);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, zzz, nil, rrr, nil, nil, yyy, ccc, zzz, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, nil, aaa, bbb, nil, nil, nil, rrr, nil);
t.set("dd", sss);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, nil, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, nil, aaa, bbb, nil, nil, nil, rrr, nil);
m.set("gg", ttt);
checkTable(s, www, nil, qqq, ddd, ppp, nil, nil, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, ttt, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
// make s fall back to t
s.setmetatable(LuaValue.tableOf(new LuaValue[] { LuaValue.INDEX, t, LuaValue.NEWINDEX, t }));
checkTable(s, www, yyy, qqq, ddd, ppp, rrr, ttt, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, ttt, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("aa", www);
checkTable(s, www, yyy, qqq, ddd, ppp, rrr, ttt, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, yyy, ccc, sss, nil, rrr, ttt, nil, yyy, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("bb", zzz);
checkTable(s, www, zzz, qqq, ddd, ppp, rrr, ttt, www, nil, qqq, ddd, ppp, nil, nil);
checkTable(t, aaa, zzz, ccc, sss, nil, rrr, ttt, nil, zzz, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("ee", xxx);
checkTable(s, www, zzz, qqq, ddd, xxx, rrr, ttt, www, nil, qqq, ddd, xxx, nil, nil);
checkTable(t, aaa, zzz, ccc, sss, nil, rrr, ttt, nil, zzz, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, rrr, ttt, aaa, bbb, nil, nil, nil, rrr, ttt);
s.set("ff", yyy);
checkTable(s, www, zzz, qqq, ddd, xxx, yyy, ttt, www, nil, qqq, ddd, xxx, nil, nil);
checkTable(t, aaa, zzz, ccc, sss, nil, yyy, ttt, nil, zzz, ccc, sss, nil, nil, nil);
checkTable(m, aaa, bbb, nil, nil, nil, yyy, ttt, aaa, bbb, nil, nil, nil, yyy, ttt);
}
}

View File

@@ -1,98 +1,102 @@
package org.luaj.vm2; package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import junit.framework.TestCase; import org.junit.jupiter.api.Test;
import org.luaj.vm2.lib.jse.JsePlatform; class StringTest {
public class StringTest extends TestCase { @Test
void testToInputStream() throws IOException {
protected void setUp() throws Exception {
JsePlatform.standardGlobals();
}
public void testToInputStream() throws IOException {
LuaString str = LuaString.valueOf("Hello"); LuaString str = LuaString.valueOf("Hello");
InputStream is = str.toInputStream(); InputStream is = str.toInputStream();
assertEquals( 'H', is.read() ); assertEquals('H', is.read());
assertEquals( 'e', is.read() ); assertEquals('e', is.read());
assertEquals( 2, is.skip( 2 ) ); assertEquals(2, is.skip(2));
assertEquals( 'o', is.read() ); assertEquals('o', is.read());
assertEquals( -1, is.read() ); assertEquals(-1, is.read());
assertTrue( is.markSupported() ); assertTrue(is.markSupported());
is.reset(); is.reset();
assertEquals( 'H', is.read() ); assertEquals('H', is.read());
is.mark( 4 ); is.mark(4);
assertEquals( 'e', is.read() ); assertEquals('e', is.read());
is.reset(); is.reset();
assertEquals( 'e', is.read() ); assertEquals('e', is.read());
LuaString substr = str.substring( 1, 4 ); LuaString substr = str.substring(1, 4);
assertEquals( 3, substr.length() ); assertEquals(3, substr.length());
is.close(); is.close();
is = substr.toInputStream(); is = substr.toInputStream();
assertEquals( 'e', is.read() ); assertEquals('e', is.read());
assertEquals( 'l', is.read() ); assertEquals('l', is.read());
assertEquals( 'l', is.read() ); assertEquals('l', is.read());
assertEquals( -1, is.read() ); assertEquals(-1, is.read());
is = substr.toInputStream(); is = substr.toInputStream();
is.reset(); is.reset();
assertEquals( 'e', is.read() ); assertEquals('e', is.read());
} }
private static final String userFriendly(String s) {
private static final String userFriendly( String s ) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
for ( int i=0, n=s.length(); i<n; i++ ) { for (int i = 0, n = s.length(); i < n; i++) {
int c = s.charAt(i); int c = s.charAt(i);
if ( c < ' ' || c >= 0x80 ) { if (c < ' ' || c >= 0x80) {
sb.append( "\\u"+Integer.toHexString(0x10000+c).substring(1) ); sb.append("\\u" + Integer.toHexString(0x10000+c).substring(1));
} else { } else {
sb.append( (char) c ); sb.append((char) c);
} }
} }
return sb.toString(); return sb.toString();
} }
public void testUtf820482051() throws UnsupportedEncodingException { @Test
void testUtf820482051() throws UnsupportedEncodingException {
int i = 2048; int i = 2048;
char[] c = { (char) (i+0), (char) (i+1), (char) (i+2), (char) (i+3) }; char[] c = { (char) (i+0), (char) (i+1), (char) (i+2), (char) (i+3) };
String before = new String(c)+" "+i+"-"+(i+4); String before = new String(c) + " " + i + "-" + (i+4);
LuaString ls = LuaString.valueOf(before); LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring(); String after = ls.tojstring();
assertEquals( userFriendly( before ), userFriendly( after ) ); assertEquals(userFriendly(before), userFriendly(after));
} }
public void testUtf8() { @Test
for ( int i=4; i<0xffff; i+=4 ) { void testUtf8() {
for (int i = 4; i < 0xffff; i += 4) {
char[] c = { (char) (i+0), (char) (i+1), (char) (i+2), (char) (i+3) }; char[] c = { (char) (i+0), (char) (i+1), (char) (i+2), (char) (i+3) };
String before = new String(c)+" "+i+"-"+(i+4); String before = new String(c) + " " + i + "-" + (i+4);
LuaString ls = LuaString.valueOf(before); LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring(); String after = ls.tojstring();
assertEquals( userFriendly( before ), userFriendly( after ) ); assertEquals(userFriendly(before), userFriendly(after));
} }
char[] c = { (char) (1), (char) (2), (char) (3) }; char[] c = { (char) (1), (char) (2), (char) (3) };
String before = new String(c)+" 1-3"; String before = new String(c) + " 1-3";
LuaString ls = LuaString.valueOf(before); LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring(); String after = ls.tojstring();
assertEquals( userFriendly( before ), userFriendly( after ) ); assertEquals(userFriendly(before), userFriendly(after));
} }
public void testSpotCheckUtf8() throws UnsupportedEncodingException { @Test
byte[] bytes = {(byte)194,(byte)160,(byte)194,(byte)161,(byte)194,(byte)162,(byte)194,(byte)163,(byte)194,(byte)164}; void testSpotCheckUtf8() throws UnsupportedEncodingException {
byte[] bytes = { (byte) 194, (byte) 160, (byte) 194, (byte) 161, (byte) 194, (byte) 162, (byte) 194, (byte) 163,
(byte) 194, (byte) 164 };
String expected = new String(bytes, "UTF8"); String expected = new String(bytes, "UTF8");
String actual = LuaString.valueOf(bytes).tojstring(); String actual = LuaString.valueOf(bytes).tojstring();
char[] d = actual.toCharArray(); char[] d = actual.toCharArray();
@@ -104,58 +108,63 @@ public class StringTest extends TestCase {
assertEquals(expected, actual); assertEquals(expected, actual);
} }
public void testNullTerminated() { @Test
void testNullTerminated() {
char[] c = { 'a', 'b', 'c', '\0', 'd', 'e', 'f' }; char[] c = { 'a', 'b', 'c', '\0', 'd', 'e', 'f' };
String before = new String(c); String before = new String(c);
LuaString ls = LuaString.valueOf(before); LuaString ls = LuaString.valueOf(before);
String after = ls.tojstring(); String after = ls.tojstring();
assertEquals( userFriendly( "abc\0def" ), userFriendly( after ) ); assertEquals(userFriendly("abc\0def"), userFriendly(after));
} }
public void testRecentStringsCacheDifferentHashcodes() { @Test
final byte[] abc = {'a', 'b', 'c' }; void testRecentStringsCacheDifferentHashcodes() {
final byte[] xyz = {'x', 'y', 'z' }; final byte[] abc = { 'a', 'b', 'c' };
final byte[] xyz = { 'x', 'y', 'z' };
final LuaString abc1 = LuaString.valueOf(abc); final LuaString abc1 = LuaString.valueOf(abc);
final LuaString xyz1 = LuaString.valueOf(xyz); final LuaString xyz1 = LuaString.valueOf(xyz);
final LuaString abc2 = LuaString.valueOf(abc); final LuaString abc2 = LuaString.valueOf(abc);
final LuaString xyz2 = LuaString.valueOf(xyz); final LuaString xyz2 = LuaString.valueOf(xyz);
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE; final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertTrue(abc1.hashCode() % mod != xyz1.hashCode() % mod); assertTrue(abc1.hashCode()%mod != xyz1.hashCode()%mod);
assertSame(abc1, abc2); assertSame(abc1, abc2);
assertSame(xyz1, xyz2); assertSame(xyz1, xyz2);
} }
public void testRecentStringsCacheHashCollisionCacheHit() { @Test
final byte[] abc = {'a', 'b', 'c' }; void testRecentStringsCacheHashCollisionCacheHit() {
final byte[] lyz = {'l', 'y', 'z' }; // chosen to have hash collision with 'abc' final byte[] abc = { 'a', 'b', 'c' };
final byte[] lyz = { 'l', 'y', 'z' }; // chosen to have hash collision with 'abc'
final LuaString abc1 = LuaString.valueOf(abc); final LuaString abc1 = LuaString.valueOf(abc);
final LuaString abc2 = LuaString.valueOf(abc); // in cache: 'abc' final LuaString abc2 = LuaString.valueOf(abc); // in cache: 'abc'
final LuaString lyz1 = LuaString.valueOf(lyz); final LuaString lyz1 = LuaString.valueOf(lyz);
final LuaString lyz2 = LuaString.valueOf(lyz); // in cache: 'lyz' final LuaString lyz2 = LuaString.valueOf(lyz); // in cache: 'lyz'
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE; final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertEquals(abc1.hashCode() % mod, lyz1.hashCode() % mod); assertEquals(abc1.hashCode()%mod, lyz1.hashCode()%mod);
assertNotSame(abc1, lyz1); assertNotSame(abc1, lyz1);
assertFalse(abc1.equals(lyz1)); assertFalse(abc1.equals(lyz1));
assertSame(abc1, abc2); assertSame(abc1, abc2);
assertSame(lyz1, lyz2); assertSame(lyz1, lyz2);
} }
public void testRecentStringsCacheHashCollisionCacheMiss() { @Test
final byte[] abc = {'a', 'b', 'c' }; void testRecentStringsCacheHashCollisionCacheMiss() {
final byte[] lyz = {'l', 'y', 'z' }; // chosen to have hash collision with 'abc' final byte[] abc = { 'a', 'b', 'c' };
final byte[] lyz = { 'l', 'y', 'z' }; // chosen to have hash collision with 'abc'
final LuaString abc1 = LuaString.valueOf(abc); final LuaString abc1 = LuaString.valueOf(abc);
final LuaString lyz1 = LuaString.valueOf(lyz); // in cache: 'abc' final LuaString lyz1 = LuaString.valueOf(lyz); // in cache: 'abc'
final LuaString abc2 = LuaString.valueOf(abc); // in cache: 'lyz' final LuaString abc2 = LuaString.valueOf(abc); // in cache: 'lyz'
final LuaString lyz2 = LuaString.valueOf(lyz); // in cache: 'abc' final LuaString lyz2 = LuaString.valueOf(lyz); // in cache: 'abc'
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE; final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertEquals(abc1.hashCode() % mod, lyz1.hashCode() % mod); assertEquals(abc1.hashCode()%mod, lyz1.hashCode()%mod);
assertNotSame(abc1, lyz1); assertNotSame(abc1, lyz1);
assertFalse(abc1.equals(lyz1)); assertFalse(abc1.equals(lyz1));
assertNotSame(abc1, abc2); assertNotSame(abc1, abc2);
assertNotSame(lyz1, lyz2); assertNotSame(lyz1, lyz2);
} }
public void testRecentStringsLongStrings() { @Test
void testRecentStringsLongStrings() {
byte[] abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(); byte[] abc = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();
assertTrue(abc.length > LuaString.RECENT_STRINGS_MAX_LENGTH); assertTrue(abc.length > LuaString.RECENT_STRINGS_MAX_LENGTH);
LuaString abc1 = LuaString.valueOf(abc); LuaString abc1 = LuaString.valueOf(abc);
@@ -163,7 +172,8 @@ public class StringTest extends TestCase {
assertNotSame(abc1, abc2); assertNotSame(abc1, abc2);
} }
public void testRecentStringsUsingJavaStrings() { @Test
void testRecentStringsUsingJavaStrings() {
final String abc = "abc"; final String abc = "abc";
final String lyz = "lyz"; // chosen to have hash collision with 'abc' final String lyz = "lyz"; // chosen to have hash collision with 'abc'
final String xyz = "xyz"; final String xyz = "xyz";
@@ -175,8 +185,8 @@ public class StringTest extends TestCase {
final LuaString xyz1 = LuaString.valueOf(xyz); final LuaString xyz1 = LuaString.valueOf(xyz);
final LuaString xyz2 = LuaString.valueOf(xyz); final LuaString xyz2 = LuaString.valueOf(xyz);
final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE; final int mod = LuaString.RECENT_STRINGS_CACHE_SIZE;
assertEquals(abc1.hashCode() % mod, lyz1.hashCode() % mod); assertEquals(abc1.hashCode()%mod, lyz1.hashCode()%mod);
assertFalse(abc1.hashCode() % mod == xyz1.hashCode() % mod); assertFalse(abc1.hashCode()%mod == xyz1.hashCode()%mod);
assertSame(abc1, abc2); assertSame(abc1, abc2);
assertSame(lyz1, lyz2); assertSame(lyz1, lyz2);
assertSame(xyz1, xyz2); assertSame(xyz1, xyz2);
@@ -193,7 +203,8 @@ public class StringTest extends TestCase {
assertSame(xyz3, xyz4); // because hashes do not collide assertSame(xyz3, xyz4); // because hashes do not collide
} }
public void testLongSubstringGetsOldBacking() { @Test
void testLongSubstringGetsOldBacking() {
LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
LuaString sub1 = src.substring(10, 40); LuaString sub1 = src.substring(10, 40);
assertSame(src.m_bytes, sub1.m_bytes); assertSame(src.m_bytes, sub1.m_bytes);
@@ -201,7 +212,8 @@ public class StringTest extends TestCase {
assertEquals(sub1.m_length, 30); assertEquals(sub1.m_length, 30);
} }
public void testShortSubstringGetsNewBacking() { @Test
void testShortSubstringGetsNewBacking() {
LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
LuaString sub1 = src.substring(10, 20); LuaString sub1 = src.substring(10, 20);
LuaString sub2 = src.substring(10, 20); LuaString sub2 = src.substring(10, 20);
@@ -211,10 +223,10 @@ public class StringTest extends TestCase {
assertFalse(src.m_bytes == sub1.m_bytes); assertFalse(src.m_bytes == sub1.m_bytes);
} }
public void testShortSubstringOfVeryLongStringGetsNewBacking() { @Test
LuaString src = LuaString.valueOf( void testShortSubstringOfVeryLongStringGetsNewBacking() {
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + LuaString src = LuaString.valueOf("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ); + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
LuaString sub1 = src.substring(10, 50); LuaString sub1 = src.substring(10, 50);
LuaString sub2 = src.substring(10, 50); LuaString sub2 = src.substring(10, 50);
assertEquals(sub1.m_offset, 0); assertEquals(sub1.m_offset, 0);
@@ -223,7 +235,8 @@ public class StringTest extends TestCase {
assertFalse(src.m_bytes == sub1.m_bytes); assertFalse(src.m_bytes == sub1.m_bytes);
} }
public void testIndexOfByteInSubstring() { @Test
void testIndexOfByteInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi"); LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10); LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length); assertEquals(10, str.m_length);
@@ -256,7 +269,8 @@ public class StringTest extends TestCase {
assertEquals(-1, sub.indexOf((byte) 'z', 7)); assertEquals(-1, sub.indexOf((byte) 'z', 7));
} }
public void testIndexOfPatternInSubstring() { @Test
void testIndexOfPatternInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi"); LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10); LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length); assertEquals(10, str.m_length);
@@ -293,7 +307,8 @@ public class StringTest extends TestCase {
assertEquals(-1, sub.indexOf(xyz, 7)); assertEquals(-1, sub.indexOf(xyz, 7));
} }
public void testLastIndexOfPatternInSubstring() { @Test
void testLastIndexOfPatternInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi"); LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10); LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length); assertEquals(10, str.m_length);
@@ -314,7 +329,8 @@ public class StringTest extends TestCase {
assertEquals(-1, sub.lastIndexOf(xyz)); assertEquals(-1, sub.lastIndexOf(xyz));
} }
public void testIndexOfAnyInSubstring() { @Test
void testIndexOfAnyInSubstring() {
LuaString str = LuaString.valueOf("abcdef:ghi"); LuaString str = LuaString.valueOf("abcdef:ghi");
LuaString sub = str.substring(2, 10); LuaString sub = str.substring(2, 10);
assertEquals(10, str.m_length); assertEquals(10, str.m_length);
@@ -325,7 +341,7 @@ public class StringTest extends TestCase {
LuaString ghi = LuaString.valueOf("ghi"); LuaString ghi = LuaString.valueOf("ghi");
LuaString ihg = LuaString.valueOf("ihg"); LuaString ihg = LuaString.valueOf("ihg");
LuaString ijk = LuaString.valueOf("ijk"); LuaString ijk = LuaString.valueOf("ijk");
LuaString kji= LuaString.valueOf("kji"); LuaString kji = LuaString.valueOf("kji");
LuaString xyz = LuaString.valueOf("xyz"); LuaString xyz = LuaString.valueOf("xyz");
LuaString ABCdEFGHIJKL = LuaString.valueOf("ABCdEFGHIJKL"); LuaString ABCdEFGHIJKL = LuaString.valueOf("ABCdEFGHIJKL");
LuaString EFGHIJKL = ABCdEFGHIJKL.substring(4, 12); LuaString EFGHIJKL = ABCdEFGHIJKL.substring(4, 12);
@@ -349,33 +365,4 @@ public class StringTest extends TestCase {
assertEquals(1, sub.indexOfAny(CdEFGHIJ)); assertEquals(1, sub.indexOfAny(CdEFGHIJ));
assertEquals(-1, sub.indexOfAny(EFGHIJKL)); assertEquals(-1, sub.indexOfAny(EFGHIJKL));
} }
public void testMatchShortPatterns() {
LuaValue[] args = { LuaString.valueOf("%bxy") };
LuaString _ = LuaString.valueOf("");
LuaString a = LuaString.valueOf("a");
LuaString ax = LuaString.valueOf("ax");
LuaString axb = LuaString.valueOf("axb");
LuaString axby = LuaString.valueOf("axby");
LuaString xbya = LuaString.valueOf("xbya");
LuaString bya = LuaString.valueOf("bya");
LuaString xby = LuaString.valueOf("xby");
LuaString axbya = LuaString.valueOf("axbya");
LuaValue nil = LuaValue.NIL;
assertEquals(nil, _.invokemethod("match", args));
assertEquals(nil, a.invokemethod("match", args));
assertEquals(nil, ax.invokemethod("match", args));
assertEquals(nil, axb.invokemethod("match", args));
assertEquals(xby, axby.invokemethod("match", args));
assertEquals(xby, xbya.invokemethod("match", args));
assertEquals(nil, bya.invokemethod("match", args));
assertEquals(xby, xby.invokemethod("match", args));
assertEquals(xby, axbya.invokemethod("match", args));
assertEquals(xby, axbya.substring(0,4).invokemethod("match", args));
assertEquals(nil, axbya.substring(0,3).invokemethod("match", args));
assertEquals(xby, axbya.substring(1,5).invokemethod("match", args));
assertEquals(nil, axbya.substring(2,5).invokemethod("match", args));
}
} }

View File

@@ -0,0 +1,325 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.lib.TwoArgFunction;
/**
* Tests for tables used as lists.
*/
public class TableHashTest {
protected LuaTable new_Table() {
return new LuaTable();
}
protected LuaTable new_Table(int n, int m) {
return new LuaTable(n, m);
}
@Test
void testSetRemove() {
LuaTable t = new_Table();
assertEquals(0, t.getHashLength());
assertEquals(0, t.length());
assertEquals(0, t.keyCount());
String[] keys = { "abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "wxy", "z01", "cd", "ef", "g", "hi", "jk",
"lm", "no", "pq", "rs", };
int[] capacities = { 0, 2, 2, 4, 4, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 32, 32, 32 };
for (int i = 0; i < keys.length; ++i) {
assertEquals(capacities[i], t.getHashLength());
String si = "Test Value! " + i;
t.set(keys[i], si);
assertEquals(0, t.length());
assertEquals(i+1, t.keyCount());
}
assertEquals(capacities[keys.length], t.getHashLength());
for (int i = 0; i < keys.length; ++i) {
LuaValue vi = LuaString.valueOf("Test Value! " + i);
assertEquals(vi, t.get(keys[i]));
assertEquals(vi, t.get(LuaString.valueOf(keys[i])));
assertEquals(vi, t.rawget(keys[i]));
assertEquals(vi, t.rawget(keys[i]));
}
// replace with new values
for (int i = 0; i < keys.length; ++i) {
t.set(keys[i], LuaString.valueOf("Replacement Value! " + i));
assertEquals(0, t.length());
assertEquals(keys.length, t.keyCount());
assertEquals(capacities[keys.length], t.getHashLength());
}
for (int i = 0; i < keys.length; ++i) {
LuaValue vi = LuaString.valueOf("Replacement Value! " + i);
assertEquals(vi, t.get(keys[i]));
}
// remove
for (int i = 0; i < keys.length; ++i) {
t.set(keys[i], LuaValue.NIL);
assertEquals(0, t.length());
assertEquals(keys.length-i-1, t.keyCount());
if (i < keys.length-1)
assertEquals(capacities[keys.length], t.getHashLength());
else
assertTrue(0 <= t.getHashLength());
}
for (int i = 0; i < keys.length; ++i) {
assertEquals(LuaValue.NIL, t.get(keys[i]));
}
}
@Test
void testIndexMetatag() {
LuaTable t = new_Table();
LuaTable mt = new_Table();
LuaTable fb = new_Table();
// set basic values
t.set("ppp", "abc");
t.set(123, "def");
mt.set(LuaValue.INDEX, fb);
fb.set("qqq", "ghi");
fb.set(456, "jkl");
// check before setting metatable
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
assertEquals("nil", fb.get("ppp").tojstring());
assertEquals("nil", fb.get(123).tojstring());
assertEquals("ghi", fb.get("qqq").tojstring());
assertEquals("jkl", fb.get(456).tojstring());
assertEquals("nil", mt.get("ppp").tojstring());
assertEquals("nil", mt.get(123).tojstring());
assertEquals("nil", mt.get("qqq").tojstring());
assertEquals("nil", mt.get(456).tojstring());
// check before setting metatable
t.setmetatable(mt);
assertEquals(mt, t.getmetatable());
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("ghi", t.get("qqq").tojstring());
assertEquals("jkl", t.get(456).tojstring());
assertEquals("nil", fb.get("ppp").tojstring());
assertEquals("nil", fb.get(123).tojstring());
assertEquals("ghi", fb.get("qqq").tojstring());
assertEquals("jkl", fb.get(456).tojstring());
assertEquals("nil", mt.get("ppp").tojstring());
assertEquals("nil", mt.get(123).tojstring());
assertEquals("nil", mt.get("qqq").tojstring());
assertEquals("nil", mt.get(456).tojstring());
// set metatable to metatable without values
t.setmetatable(fb);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
// set metatable to null
t.setmetatable(null);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
}
@Test
void testIndexFunction() {
final LuaTable t = new_Table();
final LuaTable mt = new_Table();
final TwoArgFunction fb = new TwoArgFunction() {
@Override
public LuaValue call(LuaValue tbl, LuaValue key) {
assertEquals(tbl, t);
return valueOf("from mt: " + key);
}
};
// set basic values
t.set("ppp", "abc");
t.set(123, "def");
mt.set(LuaValue.INDEX, fb);
// check before setting metatable
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
// check before setting metatable
t.setmetatable(mt);
assertEquals(mt, t.getmetatable());
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("from mt: qqq", t.get("qqq").tojstring());
assertEquals("from mt: 456", t.get(456).tojstring());
// use raw set
t.rawset("qqq", "alt-qqq");
t.rawset(456, "alt-456");
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("alt-qqq", t.get("qqq").tojstring());
assertEquals("alt-456", t.get(456).tojstring());
// remove using raw set
t.rawset("qqq", LuaValue.NIL);
t.rawset(456, LuaValue.NIL);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("from mt: qqq", t.get("qqq").tojstring());
assertEquals("from mt: 456", t.get(456).tojstring());
// set metatable to null
t.setmetatable(null);
assertEquals("abc", t.get("ppp").tojstring());
assertEquals("def", t.get(123).tojstring());
assertEquals("nil", t.get("qqq").tojstring());
assertEquals("nil", t.get(456).tojstring());
}
@Test
void testNext() {
final LuaTable t = new_Table();
assertEquals(LuaValue.NIL, t.next(LuaValue.NIL));
// insert array elements
t.set(1, "one");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.ONE));
t.set(2, "two");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.valueOf(2), t.next(LuaValue.ONE).arg(1));
assertEquals(LuaValue.valueOf("two"), t.next(LuaValue.ONE).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.valueOf(2)));
// insert hash elements
t.set("aa", "aaa");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.valueOf(2), t.next(LuaValue.ONE).arg(1));
assertEquals(LuaValue.valueOf("two"), t.next(LuaValue.ONE).arg(2));
assertEquals(LuaValue.valueOf("aa"), t.next(LuaValue.valueOf(2)).arg(1));
assertEquals(LuaValue.valueOf("aaa"), t.next(LuaValue.valueOf(2)).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.valueOf("aa")));
t.set("bb", "bbb");
assertEquals(LuaValue.valueOf(1), t.next(LuaValue.NIL).arg(1));
assertEquals(LuaValue.valueOf("one"), t.next(LuaValue.NIL).arg(2));
assertEquals(LuaValue.valueOf(2), t.next(LuaValue.ONE).arg(1));
assertEquals(LuaValue.valueOf("two"), t.next(LuaValue.ONE).arg(2));
assertEquals(LuaValue.valueOf("aa"), t.next(LuaValue.valueOf(2)).arg(1));
assertEquals(LuaValue.valueOf("aaa"), t.next(LuaValue.valueOf(2)).arg(2));
assertEquals(LuaValue.valueOf("bb"), t.next(LuaValue.valueOf("aa")).arg(1));
assertEquals(LuaValue.valueOf("bbb"), t.next(LuaValue.valueOf("aa")).arg(2));
assertEquals(LuaValue.NIL, t.next(LuaValue.valueOf("bb")));
}
@Test
void testLoopWithRemoval() {
final LuaTable t = new_Table();
t.set(LuaValue.valueOf(1), LuaValue.valueOf("1"));
t.set(LuaValue.valueOf(3), LuaValue.valueOf("3"));
t.set(LuaValue.valueOf(8), LuaValue.valueOf("4"));
t.set(LuaValue.valueOf(17), LuaValue.valueOf("5"));
t.set(LuaValue.valueOf(26), LuaValue.valueOf("6"));
t.set(LuaValue.valueOf(35), LuaValue.valueOf("7"));
t.set(LuaValue.valueOf(42), LuaValue.valueOf("8"));
t.set(LuaValue.valueOf(60), LuaValue.valueOf("10"));
t.set(LuaValue.valueOf(63), LuaValue.valueOf("11"));
Varargs entry = t.next(LuaValue.NIL);
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
LuaValue v = entry.arg(2);
if ((k.toint() & 1) == 0) {
t.set(k, LuaValue.NIL);
}
entry = t.next(k);
}
int numEntries = 0;
entry = t.next(LuaValue.NIL);
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
// Only odd keys should remain
assertTrue((k.toint() & 1) == 1);
numEntries++;
entry = t.next(k);
}
assertEquals(5, numEntries);
}
@Test
void testLoopWithRemovalAndSet() {
final LuaTable t = new_Table();
t.set(LuaValue.valueOf(1), LuaValue.valueOf("1"));
t.set(LuaValue.valueOf(3), LuaValue.valueOf("3"));
t.set(LuaValue.valueOf(8), LuaValue.valueOf("4"));
t.set(LuaValue.valueOf(17), LuaValue.valueOf("5"));
t.set(LuaValue.valueOf(26), LuaValue.valueOf("6"));
t.set(LuaValue.valueOf(35), LuaValue.valueOf("7"));
t.set(LuaValue.valueOf(42), LuaValue.valueOf("8"));
t.set(LuaValue.valueOf(60), LuaValue.valueOf("10"));
t.set(LuaValue.valueOf(63), LuaValue.valueOf("11"));
Varargs entry = t.next(LuaValue.NIL);
Varargs entry2 = entry;
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
LuaValue v = entry.arg(2);
if ((k.toint() & 1) == 0) {
t.set(k, LuaValue.NIL);
} else {
t.set(k, v.tonumber());
entry2 = t.next(entry2.arg1());
}
entry = t.next(k);
}
int numEntries = 0;
entry = t.next(LuaValue.NIL);
while ( !entry.isnil(1) ) {
LuaValue k = entry.arg1();
// Only odd keys should remain
assertTrue((k.toint() & 1) == 1);
assertTrue(entry.arg(2).type() == LuaValue.TNUMBER);
numEntries++;
entry = t.next(k);
}
assertEquals(5, numEntries);
}
}

View File

@@ -0,0 +1,437 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.junit.jupiter.api.Test;
class TableTest {
protected LuaTable new_Table() {
return new LuaTable();
}
protected LuaTable new_Table(int n, int m) {
return new LuaTable(n, m);
}
private int keyCount(LuaTable t) {
return keys(t).length;
}
private LuaValue[] keys(LuaTable t) {
ArrayList<LuaValue> l = new ArrayList<LuaValue>();
LuaValue k = LuaValue.NIL;
while ( true ) {
Varargs n = t.next(k);
if ((k = n.arg1()).isnil())
break;
l.add(k);
}
return l.toArray(new LuaValue[t.length()]);
}
@Test
void testInOrderIntegerKeyInsertion() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
}
// Ensure all keys are still there.
for (int i = 1; i <= 32; ++i) {
assertEquals("Test Value! " + i, t.get(i).tojstring());
}
// Ensure capacities make sense
assertEquals(0, t.getHashLength());
assertTrue(t.getArrayLength() >= 32);
assertTrue(t.getArrayLength() <= 64);
}
@Test
void testRekeyCount() {
LuaTable t = new_Table();
// NOTE: This order of insertion is important.
t.set(3, LuaInteger.valueOf(3));
t.set(1, LuaInteger.valueOf(1));
t.set(5, LuaInteger.valueOf(5));
t.set(4, LuaInteger.valueOf(4));
t.set(6, LuaInteger.valueOf(6));
t.set(2, LuaInteger.valueOf(2));
for (int i = 1; i < 6; ++i) {
assertEquals(LuaInteger.valueOf(i), t.get(i));
}
assertTrue(t.getArrayLength() >= 3);
assertTrue(t.getArrayLength() <= 12);
assertTrue(t.getHashLength() <= 3);
}
@Test
void testOutOfOrderIntegerKeyInsertion() {
LuaTable t = new_Table();
for (int i = 32; i > 0; --i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
}
// Ensure all keys are still there.
for (int i = 1; i <= 32; ++i) {
assertEquals("Test Value! " + i, t.get(i).tojstring());
}
// Ensure capacities make sense
assertEquals(32, t.getArrayLength());
assertEquals(0, t.getHashLength());
}
@Test
void testStringAndIntegerKeys() {
LuaTable t = new_Table();
for (int i = 0; i < 10; ++i) {
LuaString str = LuaValue.valueOf(String.valueOf(i));
t.set(i, str);
t.set(str, LuaInteger.valueOf(i));
}
assertTrue(t.getArrayLength() >= 8); // 1, 2, ..., 9
assertTrue(t.getArrayLength() <= 16);
assertTrue(t.getHashLength() >= 11); // 0, "0", "1", ..., "9"
assertTrue(t.getHashLength() <= 33);
LuaValue[] keys = keys(t);
int intKeys = 0;
int stringKeys = 0;
assertEquals(20, keys.length);
for (int i = 0; i < keys.length; ++i) {
LuaValue k = keys[i];
if (k instanceof LuaInteger) {
final int ik = k.toint();
assertTrue(ik >= 0 && ik < 10);
final int mask = 1<<ik;
assertTrue((intKeys & mask) == 0);
intKeys |= mask;
} else if (k instanceof LuaString) {
final int ik = Integer.parseInt(k.strvalue().tojstring());
assertEquals(String.valueOf(ik), k.strvalue().tojstring());
assertTrue(ik >= 0 && ik < 10);
final int mask = 1<<ik;
assertTrue((stringKeys & mask) == 0, "Key \"" + ik + "\" found more than once");
stringKeys |= mask;
} else {
fail("Unexpected type of key found");
}
}
assertEquals(0x03FF, intKeys);
assertEquals(0x03FF, stringKeys);
}
@Test
void testBadInitialCapacity() {
LuaTable t = new_Table(0, 1);
t.set("test", LuaValue.valueOf("foo"));
t.set("explode", LuaValue.valueOf("explode"));
assertEquals(2, keyCount(t));
}
@Test
void testRemove0() {
LuaTable t = new_Table(2, 0);
t.set(1, LuaValue.valueOf("foo"));
t.set(2, LuaValue.valueOf("bah"));
assertNotSame(LuaValue.NIL, t.get(1));
assertNotSame(LuaValue.NIL, t.get(2));
assertEquals(LuaValue.NIL, t.get(3));
t.set(1, LuaValue.NIL);
t.set(2, LuaValue.NIL);
t.set(3, LuaValue.NIL);
assertEquals(LuaValue.NIL, t.get(1));
assertEquals(LuaValue.NIL, t.get(2));
assertEquals(LuaValue.NIL, t.get(3));
}
@Test
void testRemove1() {
LuaTable t = new_Table(0, 1);
t.set("test", LuaValue.valueOf("foo"));
t.set("explode", LuaValue.NIL);
t.set(42, LuaValue.NIL);
t.set(new_Table(), LuaValue.NIL);
t.set("test", LuaValue.NIL);
assertEquals(0, keyCount(t));
t.set(10, LuaInteger.valueOf(5));
t.set(10, LuaValue.NIL);
assertEquals(0, keyCount(t));
}
@Test
void testRemove2() {
LuaTable t = new_Table(0, 1);
t.set("test", LuaValue.valueOf("foo"));
t.set("string", LuaInteger.valueOf(10));
assertEquals(2, keyCount(t));
t.set("string", LuaValue.NIL);
t.set("three", LuaValue.valueOf(3.14));
assertEquals(2, keyCount(t));
t.set("test", LuaValue.NIL);
assertEquals(1, keyCount(t));
t.set(10, LuaInteger.valueOf(5));
assertEquals(2, keyCount(t));
t.set(10, LuaValue.NIL);
assertEquals(1, keyCount(t));
t.set("three", LuaValue.NIL);
assertEquals(0, keyCount(t));
}
@Test
void testShrinkNonPowerOfTwoArray() {
LuaTable t = new_Table(6, 2);
t.set(1, "one");
t.set(2, "two");
t.set(3, "three");
t.set(4, "four");
t.set(5, "five");
t.set(6, "six");
t.set("aa", "aaa");
t.set("bb", "bbb");
t.set(3, LuaValue.NIL);
t.set(4, LuaValue.NIL);
t.set(6, LuaValue.NIL);
t.set("cc", "ccc");
t.set("dd", "ddd");
assertEquals(4, t.getArrayLength());
assertTrue(t.getHashLength() < 10);
assertEquals(5, t.hashEntries);
assertEquals("one", t.get(1).tojstring());
assertEquals("two", t.get(2).tojstring());
assertEquals(LuaValue.NIL, t.get(3));
assertEquals(LuaValue.NIL, t.get(4));
assertEquals("five", t.get(5).tojstring());
assertEquals(LuaValue.NIL, t.get(6));
assertEquals("aaa", t.get("aa").tojstring());
assertEquals("bbb", t.get("bb").tojstring());
assertEquals("ccc", t.get("cc").tojstring());
assertEquals("ddd", t.get("dd").tojstring());
}
@Test
void testInOrderLuaLength() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
assertEquals(i, t.length());
}
}
@Test
void testOutOfOrderLuaLength() {
LuaTable t = new_Table();
for (int j = 8; j < 32; j += 8) {
for (int i = j; i > 0; --i) {
t.set(i, LuaValue.valueOf("Test Value! " + i));
}
assertEquals(j, t.length());
}
}
@Test
void testStringKeysLuaLength() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set("str-" + i, LuaValue.valueOf("String Key Test Value! " + i));
assertEquals(0, t.length());
}
}
@Test
void testMixedKeysLuaLength() {
LuaTable t = new_Table();
for (int i = 1; i <= 32; ++i) {
t.set("str-" + i, LuaValue.valueOf("String Key Test Value! " + i));
t.set(i, LuaValue.valueOf("Int Key Test Value! " + i));
assertEquals(i, t.length());
}
}
private static final void compareLists(LuaTable t, Vector<LuaString> v) {
int n = v.size();
assertEquals(v.size(), t.length());
for (int j = 0; j < n; j++) {
LuaString vj = v.elementAt(j);
String tj = t.get(j+1).tojstring();
assertEquals(vj.tojstring(), tj);
}
}
@Test
void testInsertBeginningOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
t.insert(1, test);
v.insertElementAt(test, 0);
compareLists(t, v);
}
}
@Test
void testInsertEndOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
t.insert(0, test);
v.insertElementAt(test, v.size());
compareLists(t, v);
}
}
@Test
void testInsertMiddleOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
int m = i/2;
t.insert(m+1, test);
v.insertElementAt(test, m);
compareLists(t, v);
}
}
private static final void prefillLists(LuaTable t, Vector<LuaString> v) {
for (int i = 1; i <= 32; ++i) {
LuaString test = LuaValue.valueOf("Test Value! " + i);
t.insert(0, test);
v.insertElementAt(test, v.size());
}
}
@Test
void testRemoveBeginningOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
prefillLists(t, v);
for (int i = 1; i <= 32; ++i) {
t.remove(1);
v.removeElementAt(0);
compareLists(t, v);
}
}
@Test
void testRemoveEndOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
prefillLists(t, v);
for (int i = 1; i <= 32; ++i) {
t.remove(0);
v.removeElementAt(v.size()-1);
compareLists(t, v);
}
}
@Test
void testRemoveMiddleOfList() {
LuaTable t = new_Table();
Vector<LuaString> v = new Vector<>();
prefillLists(t, v);
for (int i = 1; i <= 32; ++i) {
int m = v.size()/2;
t.remove(m+1);
v.removeElementAt(m);
compareLists(t, v);
}
}
@Test
void testRemoveWhileIterating() {
LuaTable t = LuaValue.tableOf(
new LuaValue[] { LuaValue.valueOf("a"), LuaValue.valueOf("aa"), LuaValue.valueOf("b"),
LuaValue.valueOf("bb"), LuaValue.valueOf("c"), LuaValue.valueOf("cc"), LuaValue.valueOf("d"),
LuaValue.valueOf("dd"), LuaValue.valueOf("e"), LuaValue.valueOf("ee"), },
new LuaValue[] { LuaValue.valueOf("11"), LuaValue.valueOf("22"), LuaValue.valueOf("33"),
LuaValue.valueOf("44"), LuaValue.valueOf("55"), });
// Find expected order after removal.
List<String> expected = new ArrayList<>();
Varargs n;
int i;
for (n = t.next(LuaValue.NIL), i = 0; !n.arg1().isnil(); n = t.next(n.arg1()), ++i) {
if (i%2 == 0)
expected.add(n.arg1() + "=" + n.arg(2));
}
// Remove every other key while iterating over the table.
for (n = t.next(LuaValue.NIL), i = 0; !n.arg1().isnil(); n = t.next(n.arg1()), ++i) {
if (i%2 != 0)
t.set(n.arg1(), LuaValue.NIL);
}
// Iterate over remaining table, and form list of entries still in table.
List<String> actual = new ArrayList<>();
for (n = t.next(LuaValue.NIL); !n.arg1().isnil(); n = t.next(n.arg1())) {
actual.add(n.arg1() + "=" + n.arg(2));
}
assertEquals(expected, actual);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -21,12 +21,15 @@
******************************************************************************/ ******************************************************************************/
package org.luaj.vm2; package org.luaj.vm2;
import junit.framework.TestCase; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
/** /**
* Tests of basic unary and binary operators on main value types. * Tests of basic unary and binary operators on main value types.
*/ */
public class VarargsTest extends TestCase { class VarargsTest {
static LuaValue A = LuaValue.valueOf("a"); static LuaValue A = LuaValue.valueOf("a");
static LuaValue B = LuaValue.valueOf("b"); static LuaValue B = LuaValue.valueOf("b");
@@ -45,30 +48,31 @@ public class VarargsTest extends TestCase {
static Varargs DE = LuaValue.varargsOf(new LuaValue[] { D, E }); static Varargs DE = LuaValue.varargsOf(new LuaValue[] { D, E });
static Varargs E_G = LuaValue.varargsOf(new LuaValue[] { E, F, G }); static Varargs E_G = LuaValue.varargsOf(new LuaValue[] { E, F, G });
static Varargs FG = LuaValue.varargsOf(new LuaValue[] { F, G }); static Varargs FG = LuaValue.varargsOf(new LuaValue[] { F, G });
static LuaValue[] Z_H_array = {Z, A, B, C, D, E, F, G, H }; static LuaValue[] Z_H_array = { Z, A, B, C, D, E, F, G, H };
static Varargs A_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 1, 7); static Varargs A_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 1, 7);
static Varargs B_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 2, 4); static Varargs B_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 2, 4);
static Varargs C_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 5); static Varargs C_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 5);
static Varargs C_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 3); static Varargs C_E_alt = new Varargs.ArrayPartVarargs(Z_H_array, 3, 3);
static Varargs C_E_alt2 = LuaValue.varargsOf(C, D, E); static Varargs C_E_alt2 = LuaValue.varargsOf(C, D, E);
static Varargs DE_alt = new Varargs.PairVarargs(D,E); static Varargs DE_alt = new Varargs.PairVarargs(D, E);
static Varargs DE_alt2 = LuaValue.varargsOf(D,E); static Varargs DE_alt2 = LuaValue.varargsOf(D, E);
static Varargs E_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 5, 3); static Varargs E_G_alt = new Varargs.ArrayPartVarargs(Z_H_array, 5, 3);
static Varargs FG_alt = new Varargs.PairVarargs(F, G); static Varargs FG_alt = new Varargs.PairVarargs(F, G);
static Varargs NONE = LuaValue.NONE; static Varargs NONE = LuaValue.NONE;
static void expectEquals(Varargs x, Varargs y) { private void expectEquals(Varargs x, Varargs y) {
assertEquals(x.narg(), y.narg()); assertEquals(x.narg(), y.narg());
assertEquals(x.arg1(), y.arg1()); assertEquals(x.arg1(), y.arg1());
assertEquals(x.arg(0), y.arg(0)); assertEquals(x.arg(0), y.arg(0));
assertEquals(x.arg(-1), y.arg(-1)); assertEquals(x.arg(-1), y.arg(-1));
assertEquals(x.arg(2), y.arg(2)); assertEquals(x.arg(2), y.arg(2));
assertEquals(x.arg(3), y.arg(3)); assertEquals(x.arg(3), y.arg(3));
for (int i = 4; i < x.narg() + 2; ++i) for (int i = 4; i < x.narg()+2; ++i)
assertEquals(x.arg(i), y.arg(i)); assertEquals(x.arg(i), y.arg(i));
} }
public void testSanity() { @Test
void testSanity() {
expectEquals(A_G, A_G); expectEquals(A_G, A_G);
expectEquals(A_G_alt, A_G_alt); expectEquals(A_G_alt, A_G_alt);
expectEquals(A_G, A_G_alt); expectEquals(A_G, A_G_alt);
@@ -86,7 +90,8 @@ public class VarargsTest extends TestCase {
expectEquals(NIL, NIL); expectEquals(NIL, NIL);
} }
public void testNegativeIndices() { @Test
void testNegativeIndices() {
expectNegSubargsError(A_G); expectNegSubargsError(A_G);
expectNegSubargsError(A_G_alt); expectNegSubargsError(A_G_alt);
expectNegSubargsError(B_E); expectNegSubargsError(B_E);
@@ -106,7 +111,7 @@ public class VarargsTest extends TestCase {
expectNegSubargsError(NIL); expectNegSubargsError(NIL);
} }
static void standardTestsA_G(Varargs a_g) { private void standardTestsA_G(Varargs a_g) {
expectEquals(A_G, a_g); expectEquals(A_G, a_g);
expectEquals(A_G, a_g.subargs(1)); expectEquals(A_G, a_g.subargs(1));
expectEquals(C_G, a_g.subargs(3).subargs(1)); expectEquals(C_G, a_g.subargs(3).subargs(1));
@@ -121,7 +126,7 @@ public class VarargsTest extends TestCase {
standardTestsC_G(A_G.subargs(3)); standardTestsC_G(A_G.subargs(3));
} }
static void standardTestsC_G(Varargs c_g) { private void standardTestsC_G(Varargs c_g) {
expectEquals(C_G, c_g.subargs(1)); expectEquals(C_G, c_g.subargs(1));
expectEquals(E_G, c_g.subargs(3)); expectEquals(E_G, c_g.subargs(3));
expectEquals(E_G, c_g.subargs(3).subargs(1)); expectEquals(E_G, c_g.subargs(3).subargs(1));
@@ -134,7 +139,7 @@ public class VarargsTest extends TestCase {
standardTestsE_G(c_g.subargs(3)); standardTestsE_G(c_g.subargs(3));
} }
static void standardTestsE_G(Varargs e_g) { private void standardTestsE_G(Varargs e_g) {
expectEquals(E_G, e_g.subargs(1)); expectEquals(E_G, e_g.subargs(1));
expectEquals(FG, e_g.subargs(2)); expectEquals(FG, e_g.subargs(2));
expectEquals(FG, e_g.subargs(2).subargs(1)); expectEquals(FG, e_g.subargs(2).subargs(1));
@@ -145,7 +150,7 @@ public class VarargsTest extends TestCase {
standardTestsFG(e_g.subargs(2)); standardTestsFG(e_g.subargs(2));
} }
static void standardTestsFG(Varargs fg) { private void standardTestsFG(Varargs fg) {
expectEquals(FG, fg.subargs(1)); expectEquals(FG, fg.subargs(1));
expectEquals(G, fg.subargs(2)); expectEquals(G, fg.subargs(2));
expectEquals(G, fg.subargs(2).subargs(1)); expectEquals(G, fg.subargs(2).subargs(1));
@@ -153,12 +158,13 @@ public class VarargsTest extends TestCase {
expectEquals(NONE, fg.subargs(3).subargs(1)); expectEquals(NONE, fg.subargs(3).subargs(1));
} }
static void standardTestsNone(Varargs none) { private void standardTestsNone(Varargs none) {
expectEquals(NONE, none.subargs(1)); expectEquals(NONE, none.subargs(1));
expectEquals(NONE, none.subargs(2)); expectEquals(NONE, none.subargs(2));
} }
public void testVarargsSubargs() { @Test
void testVarargsSubargs() {
standardTestsA_G(A_G); standardTestsA_G(A_G);
standardTestsA_G(A_G_alt); standardTestsA_G(A_G_alt);
standardTestsC_G(C_G); standardTestsC_G(C_G);
@@ -170,33 +176,32 @@ public class VarargsTest extends TestCase {
standardTestsNone(NONE); standardTestsNone(NONE);
} }
public void testVarargsMore() { @Test
void testVarargsMore() {
Varargs a_g; Varargs a_g;
a_g = LuaValue.varargsOf(new LuaValue[] { A, }, LuaValue.varargsOf( new LuaValue[] { B, C, D, E, F, G })); a_g = LuaValue.varargsOf(new LuaValue[] { A, }, LuaValue.varargsOf(new LuaValue[] { B, C, D, E, F, G }));
standardTestsA_G(a_g); standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, }, LuaValue.varargsOf( new LuaValue[] { C, D, E, F, G })); a_g = LuaValue.varargsOf(new LuaValue[] { A, B, }, LuaValue.varargsOf(new LuaValue[] { C, D, E, F, G }));
standardTestsA_G(a_g); standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, }, LuaValue.varargsOf( new LuaValue[] { D, E, F, G })); a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, }, LuaValue.varargsOf(new LuaValue[] { D, E, F, G }));
standardTestsA_G(a_g); standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, }, LuaValue.varargsOf(E, F, G)); a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, }, LuaValue.varargsOf(E, F, G));
standardTestsA_G(a_g); standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E }, LuaValue.varargsOf( F, G )); a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E }, LuaValue.varargsOf(F, G));
standardTestsA_G(a_g); standardTestsA_G(a_g);
a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E, F, }, G ); a_g = LuaValue.varargsOf(new LuaValue[] { A, B, C, D, E, F, }, G);
standardTestsA_G(a_g); standardTestsA_G(a_g);
} }
public void testPairVarargsMore() { @Test
Varargs a_g = new Varargs.PairVarargs(A, void testPairVarargsMore() {
new Varargs.PairVarargs(B, Varargs a_g = new Varargs.PairVarargs(A, new Varargs.PairVarargs(B, new Varargs.PairVarargs(C,
new Varargs.PairVarargs(C, new Varargs.PairVarargs(D, new Varargs.PairVarargs(E, new Varargs.PairVarargs(F, G))))));
new Varargs.PairVarargs(D,
new Varargs.PairVarargs(E,
new Varargs.PairVarargs(F, G))))));
standardTestsA_G(a_g); standardTestsA_G(a_g);
} }
public void testArrayPartMore() { @Test
void testArrayPartMore() {
Varargs a_g; Varargs a_g;
a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 1, new Varargs.ArrayPartVarargs(Z_H_array, 2, 6)); a_g = new Varargs.ArrayPartVarargs(Z_H_array, 1, 1, new Varargs.ArrayPartVarargs(Z_H_array, 2, 6));
standardTestsA_G(a_g); standardTestsA_G(a_g);
@@ -212,18 +217,18 @@ public class VarargsTest extends TestCase {
standardTestsA_G(a_g); standardTestsA_G(a_g);
} }
static void expectNegSubargsError(Varargs v) { private void expectNegSubargsError(Varargs v) {
String expected_msg = "bad argument #1: start must be > 0"; String expected_msg = "bad argument #1: start must be > 0";
try { try {
v.subargs(0); v.subargs(0);
fail("Failed to throw exception for index 0"); fail("Failed to throw exception for index 0");
} catch ( LuaError e ) { } catch (LuaError e) {
assertEquals(expected_msg, e.getMessage()); assertEquals(expected_msg, e.getMessage());
} }
try { try {
v.subargs(-1); v.subargs(-1);
fail("Failed to throw exception for index -1"); fail("Failed to throw exception for index -1");
} catch ( LuaError e ) { } catch (LuaError e) {
assertEquals(expected_msg, e.getMessage()); assertEquals(expected_msg, e.getMessage());
} }
} }

View File

@@ -0,0 +1,262 @@
/*******************************************************************************
* Copyright (c) 2009 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
package org.luaj.vm2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.ref.WeakReference;
import org.junit.jupiter.api.Test;
class WeakTableTest {
@Test
void testWeakValuesTable() {
LuaTable t = WeakTable.make(false, true);
Object obj = new Object();
LuaTable tableValue = new LuaTable();
LuaString stringValue = LuaString.valueOf("this is a test");
LuaTable tableValue2 = new LuaTable();
t.set("table", tableValue);
t.set("userdata", LuaValue.userdataOf(obj, null));
t.set("string", stringValue);
t.set("string2", LuaValue.valueOf("another string"));
t.set(1, tableValue2);
assertTrue(t.getHashLength() >= 4, "table must have at least 4 elements");
// TODO fix assert
// assertTrue(t.getArrayLength() >= 1, "array part must have 1 element");
// check that table can be used to get elements
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
assertEquals(tableValue2, t.get(1));
// nothing should be collected, since we have strong references here
collectGarbage();
// check that elements are still there
assertEquals(tableValue, t.get("table"));
assertEquals(stringValue, t.get("string"));
assertEquals(obj, t.get("userdata").checkuserdata());
assertEquals(tableValue2, t.get(1));
// drop our strong references
obj = null;
tableValue = null;
tableValue2 = null;
stringValue = null;
// Garbage collection should cause weak entries to be dropped.
collectGarbage();
// check that they are dropped
assertEquals(LuaValue.NIL, t.get("table"));
assertEquals(LuaValue.NIL, t.get("userdata"));
assertEquals(LuaValue.NIL, t.get(1));
assertFalse(t.get("string").isnil(), "strings should not be in weak references");
}
@Test
void testWeakKeysTable() {
LuaTable t = WeakTable.make(true, false);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
// set up the table
t.set(key, val);
assertEquals(val, t.get(key));
System.gc();
assertEquals(val, t.get(key));
// drop key and value references, replace them with new ones
WeakReference<LuaValue> origkey = new WeakReference<>(key);
WeakReference<LuaValue> origval = new WeakReference<>(val);
key = LuaValue.userdataOf(new MyData(111));
val = LuaValue.userdataOf(new MyData(222));
// new key and value should be interchangeable (feature of this test class)
assertEquals(key, origkey.get());
assertEquals(val, origval.get());
assertEquals(val, t.get(key));
assertEquals(val, t.get(origkey.get()));
assertEquals(origval.get(), t.get(key));
// value should not be reachable after gc
collectGarbage();
assertEquals(null, origkey.get());
assertEquals(LuaValue.NIL, t.get(key));
collectGarbage();
assertEquals(null, origval.get());
}
@Test
void testNext() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set(key, val);
t.set(key2, val2);
t.set(key3, val3);
// forget one of the keys
key2 = null;
val2 = null;
collectGarbage();
// table should have 2 entries
int size = 0;
for (LuaValue k = t.next(LuaValue.NIL).arg1(); !k.isnil(); k = t.next(k).arg1()) {
size++;
}
assertEquals(2, size);
}
@Test
void testWeakKeysValuesTable() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set(key, val);
t.set(key2, val2);
t.set(key3, val3);
assertEquals(val, t.get(key));
assertEquals(val2, t.get(key2));
assertEquals(val3, t.get(key3));
System.gc();
assertEquals(val, t.get(key));
assertEquals(val2, t.get(key2));
assertEquals(val3, t.get(key3));
// drop key and value references, replace them with new ones
WeakReference<LuaValue> origkey = new WeakReference<>(key);
WeakReference<LuaValue> origval = new WeakReference<>(val);
WeakReference<LuaValue> origkey2 = new WeakReference<>(key2);
WeakReference<LuaValue> origval2 = new WeakReference<>(val2);
WeakReference<LuaValue> origkey3 = new WeakReference<>(key3);
WeakReference<LuaValue> origval3 = new WeakReference<>(val3);
key = LuaValue.userdataOf(new MyData(111));
val = LuaValue.userdataOf(new MyData(222));
key2 = LuaValue.userdataOf(new MyData(333));
// don't drop val2, or key3
val3 = LuaValue.userdataOf(new MyData(666));
// no values should be reachable after gc
collectGarbage();
assertEquals(null, origkey.get());
assertEquals(null, origval.get());
assertEquals(null, origkey2.get());
assertEquals(null, origval3.get());
assertEquals(LuaValue.NIL, t.get(key));
assertEquals(LuaValue.NIL, t.get(key2));
assertEquals(LuaValue.NIL, t.get(key3));
// all originals should be gone after gc, then access
val2 = null;
key3 = null;
collectGarbage();
assertEquals(null, origval2.get());
assertEquals(null, origkey3.get());
}
@Test
void testReplace() {
LuaTable t = WeakTable.make(true, true);
LuaValue key = LuaValue.userdataOf(new MyData(111));
LuaValue val = LuaValue.userdataOf(new MyData(222));
LuaValue key2 = LuaValue.userdataOf(new MyData(333));
LuaValue val2 = LuaValue.userdataOf(new MyData(444));
LuaValue key3 = LuaValue.userdataOf(new MyData(555));
LuaValue val3 = LuaValue.userdataOf(new MyData(666));
// set up the table
t.set(key, val);
t.set(key2, val2);
t.set(key3, val3);
LuaValue val4 = LuaValue.userdataOf(new MyData(777));
t.set(key2, val4);
// table should have 3 entries
int size = 0;
for (LuaValue k = t.next(LuaValue.NIL).arg1(); !k.isnil() && size < 1000; k = t.next(k).arg1()) {
size++;
}
assertEquals(3, size);
}
public static class MyData {
public final int value;
public MyData(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
@Override
public boolean equals(Object o) {
return (o instanceof MyData) && ((MyData) o).value == value;
}
@Override
public String toString() {
return "mydata-" + value;
}
}
static void collectGarbage() {
Runtime rt = Runtime.getRuntime();
rt.gc();
try {
Thread.sleep(20);
rt.gc();
Thread.sleep(20);
} catch (Exception e) {
e.printStackTrace();
}
rt.gc();
}
}

BIN
luaj-jme-3.0.2.jar Normal file

Binary file not shown.

39
luaj-jme/pom.xml Normal file
View File

@@ -0,0 +1,39 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.luaj</groupId>
<artifactId>luaj-parent</artifactId>
<version>3.0-SNAPSHOT</version>
</parent>
<artifactId>luaj-jme</artifactId>
<name>luaj-jme</name>
<description>LuaJ for Java ME</description>
<dependencies>
<dependency>
<groupId>org.luaj</groupId>
<artifactId>luaj-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
</dependency>
<dependency>
<groupId>com.github.mcpat.apistubs</groupId>
<artifactId>cldc-1.1-stub</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -35,60 +35,76 @@ 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>
* The implementation of the is based on CLDC 1.0 and StreamConnection. * The implementation of the is based on CLDC 1.0 and StreamConnection. However,
* However, seek is not supported. * seek is not supported.
* <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.jme.JmePlatform#standardGlobals()} * {@link org.luaj.vm2.lib.jme.JmePlatform#standardGlobals()}
* <pre> {@code *
* <pre>
* {
* &#64;code
* Globals globals = JmePlatform.standardGlobals(); * Globals globals = JmePlatform.standardGlobals();
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre> * }
* </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 *
* <pre>
* {
* &#64;code
* Globals globals = new Globals(); * Globals globals = new Globals();
* globals.load(new JmeBaseLib()); * globals.load(new JmeBaseLib());
* globals.load(new PackageLib()); * globals.load(new PackageLib());
* globals.load(new JmeIoLib()); * globals.load(new JmeIoLib());
* globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n")); * globals.get("io").get("write").call(LuaValue.valueOf("hello, world\n"));
* } </pre> * }
* <p>However, other libraries such as <em>MathLib</em> are not loaded in this case. * </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 {
@Override
protected File wrapStdin() throws IOException { protected File wrapStdin() throws IOException {
return new FileImpl(globals.STDIN); return new FileImpl(globals.STDIN);
} }
@Override
protected File wrapStdout() throws IOException { protected File wrapStdout() throws IOException {
return new FileImpl(globals.STDOUT); return new FileImpl(globals.STDOUT);
} }
@Override
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 { @Override
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?
new FileImpl(conn, conn.openInputStream(), null):
new FileImpl(conn, conn.openInputStream(), conn.openOutputStream());
/* /*
if ( appendMode ) { if ( appendMode ) {
f.seek("end",0); f.seek("end",0);
@@ -97,18 +113,21 @@ public class JmeIoLib extends IoLib {
conn.truncate(0); conn.truncate(0);
} }
*/ */
return f; return readMode? new FileImpl(conn, conn.openInputStream(), null)
: new FileImpl(conn, conn.openInputStream(), conn.openOutputStream());
} }
private static void notimplemented() throws IOException { private static void notimplemented() throws IOException {
throw new IOException("not implemented"); throw new IOException("not implemented");
} }
@Override
protected File openProgram(String prog, String mode) throws IOException { protected File openProgram(String prog, String mode) throws IOException {
notimplemented(); notimplemented();
return null; return null;
} }
@Override
protected File tmpFile() throws IOException { protected File tmpFile() throws IOException {
notimplemented(); notimplemented();
return null; return null;
@@ -121,44 +140,61 @@ public class JmeIoLib extends IoLib {
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);
} }
@Override
public String tojstring() { public String tojstring() {
return "file ("+this.hashCode()+")"; return "file (" + this.hashCode() + ")";
} }
@Override
public boolean isstdfile() { public boolean isstdfile() {
return conn == null; return conn == null;
} }
@Override
public void close() throws IOException { public void close() throws IOException {
closed = true; closed = true;
if ( conn != null ) { if (conn != null) {
conn.close(); conn.close();
} }
} }
@Override
public void flush() throws IOException { public void flush() throws IOException {
if ( os != null ) if (os != null)
os.flush(); os.flush();
} }
@Override
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();
} }
@Override
public boolean isclosed() { public boolean isclosed() {
return closed; return closed;
} }
@Override
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,48 +213,54 @@ public class JmeIoLib extends IoLib {
notimplemented(); notimplemented();
return 0; return 0;
} }
@Override
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
@Override
public int remaining() throws IOException { public int remaining() throws IOException {
return -1; return -1;
} }
// peek ahead one character // peek ahead one character
@Override
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
@Override
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;
} }
// return number of bytes read if positive, -1 if eof, throws IOException // return number of bytes read if positive, -1 if eof, throws IOException
@Override
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

@@ -23,8 +23,6 @@ package org.luaj.vm2.lib.jme;
import org.luaj.vm2.Globals; import org.luaj.vm2.Globals;
import org.luaj.vm2.LoadState; import org.luaj.vm2.LoadState;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.compiler.LuaC; import org.luaj.vm2.compiler.LuaC;
import org.luaj.vm2.lib.BaseLib; import org.luaj.vm2.lib.BaseLib;
import org.luaj.vm2.lib.Bit32Lib; import org.luaj.vm2.lib.Bit32Lib;
@@ -37,40 +35,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 *
* <pre>
* {
* &#64;code
* Globals global = JmePlatform.standardGlobals(); * Globals global = JmePlatform.standardGlobals();
* global.get("print").call(LuaValue.valueOf("hello, world")); * global.get("print").call(LuaValue.valueOf("hello, world"));
* } </pre> * }
* </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,9 +101,11 @@ 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
* 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>
* <p> * <p>
* The class ensures that initialization is done in the correct order. * The class ensures that initialization is done in the correct order.
@@ -119,9 +139,11 @@ public class JmePlatform {
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

View File

@@ -0,0 +1,136 @@
package org.luaj.vm2.lib.jme;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.luaj.vm2.LuaValue;
class OsLibTest {
LuaValue jme_lib;
double time;
@BeforeEach
public void setUp() {
jme_lib = JmePlatform.standardGlobals().get("os");
time = 998571302000L/1000.0;
}
void test(String format, String expected) {
String actual = jme_lib.get("date").call(LuaValue.valueOf(format), LuaValue.valueOf(time)).tojstring();
assertEquals(expected, actual);
}
@Test
void testStringDateChars() { test("foo", "foo"); }
@Test
void testStringDate_a() { test("%a", "Thu"); }
@Test
void testStringDate_A() { test("%A", "Thursday"); }
@Test
void testStringDate_b() { test("%b", "Aug"); }
@Test
void testStringDate_B() { test("%B", "August"); }
@Test
void testStringDate_c() { test("%c", "Thu Aug 23 14:55:02 2001"); }
@Test
void testStringDate_d() { test("%d", "23"); }
@Test
void testStringDate_H() { test("%H", "14"); }
@Test
void testStringDate_I() { test("%I", "02"); }
@Test
void testStringDate_j() { test("%j", "235"); }
@Test
void testStringDate_m() { test("%m", "08"); }
@Test
void testStringDate_M() { test("%M", "55"); }
@Test
void testStringDate_p() { test("%p", "PM"); }
@Test
void testStringDate_S() { test("%S", "02"); }
@Test
void testStringDate_U() { test("%U", "33"); }
@Test
void testStringDate_w() { test("%w", "4"); }
@Test
void testStringDate_W() { test("%W", "34"); }
@Test
void testStringDate_x() { test("%x", "08/23/01"); }
@Test
void testStringDate_X() { test("%X", "14:55:02"); }
@Test
void testStringDate_y() { test("%y", "01"); }
@Test
void testStringDate_Y() { test("%Y", "2001"); }
@Test
void testStringDate_Pct() { test("%%", "%"); }
static final double DAY = 24.*3600.;
@Test
void testStringDate_UW_neg4() { time -= 4*DAY; test("%c %U %W", "Sun Aug 19 14:55:02 2001 33 33"); }
@Test
void testStringDate_UW_neg3() { time -= 3*DAY; test("%c %U %W", "Mon Aug 20 14:55:02 2001 33 34"); }
@Test
void testStringDate_UW_neg2() { time -= 2*DAY; test("%c %U %W", "Tue Aug 21 14:55:02 2001 33 34"); }
@Test
void testStringDate_UW_neg1() { time -= DAY; test("%c %U %W", "Wed Aug 22 14:55:02 2001 33 34"); }
@Test
void testStringDate_UW_pos0() { time += 0; test("%c %U %W", "Thu Aug 23 14:55:02 2001 33 34"); }
@Test
void testStringDate_UW_pos1() { time += DAY; test("%c %U %W", "Fri Aug 24 14:55:02 2001 33 34"); }
@Test
void testStringDate_UW_pos2() { time += 2*DAY; test("%c %U %W", "Sat Aug 25 14:55:02 2001 33 34"); }
@Test
void testStringDate_UW_pos3() { time += 3*DAY; test("%c %U %W", "Sun Aug 26 14:55:02 2001 34 34"); }
@Test
void testStringDate_UW_pos4() { time += 4*DAY; test("%c %U %W", "Mon Aug 27 14:55:02 2001 34 35"); }
@Test
void testJseOsGetenvForEnvVariables() {
LuaValue USER = LuaValue.valueOf("USER");
LuaValue jme_user = jme_lib.get("getenv").call(USER);
assertTrue(jme_user.isnil());
System.out.println("User: " + jme_user);
}
void testJseOsGetenvForSystemProperties() {
// System.setProperty("test.key.foo", "test.value.bar");
LuaValue key = LuaValue.valueOf("test.key.foo");
LuaValue value = LuaValue.valueOf("test.value.bar");
LuaValue jme_value = jme_lib.get("getenv").call(key);
assertEquals(value, jme_value);
}
}

View File

BIN
luaj-jse-3.0.2.jar Normal file

Binary file not shown.

77
luaj-jse/pom.xml Normal file
View File

@@ -0,0 +1,77 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.luaj</groupId>
<artifactId>luaj-parent</artifactId>
<version>3.0-SNAPSHOT</version>
</parent>
<artifactId>luaj-jse</artifactId>
<name>luaj-jse</name>
<description>LuaJ for Java SE</description>
<dependencies>
<dependency>
<groupId>org.luaj</groupId>
<artifactId>luaj-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.helger.maven</groupId>
<artifactId>ph-javacc-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-grammar</id>
<phase>generate-sources</phase>
<goals>
<goal>javacc</goal>
</goals>
<configuration>
<jdkVersion>1.8</jdkVersion>
<javadocFriendlyComments>true</javadocFriendlyComments>
<packageName>org.luaj.vm2.parser</packageName>
<sourceDirectory>src/main/javacc</sourceDirectory>
<outputDirectory>${project.build.directory}/generated-sources/javacc</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/javacc</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

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,26 +39,19 @@ 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);
@@ -68,10 +62,10 @@ public class lua {
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;
boolean versioninfo = false; boolean versioninfo = false;
boolean processing = true; boolean processing = true;
boolean nodebug = false; boolean nodebug = false;
@@ -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;
@@ -167,48 +162,48 @@ 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,26 +36,19 @@ 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);
@@ -71,27 +65,27 @@ public class luac {
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;
@@ -130,24 +124,22 @@ 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 );
// process input files // process input files
try { try (OutputStream fos = new FileOutputStream(output)) {
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;
@@ -158,23 +150,22 @@ public class luac {
} }
} }
} }
} finally {
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)
@@ -185,8 +176,8 @@ public class luac {
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

@@ -0,0 +1,270 @@
/*******************************************************************************
* Copyright (c) 2009-2012 Luaj.org. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import org.luaj.vm2.Globals;
import org.luaj.vm2.Lua;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.luajc.LuaJC;
/**
* Compiler for lua files to compile lua sources or lua binaries into java
* classes.
*/
public class luajc {
private static final String version = Lua._VERSION + " Copyright (C) 2012 luaj.org";
private static final String usage = "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"
+ " -d dir destination directory\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() {
System.out.println(usage);
System.exit(-1);
}
private String srcdir = ".";
private String destdir = ".";
private boolean genmain = false;
private boolean recurse = false;
private boolean verbose = false;
private boolean loadclasses = false;
private String encoding = null;
private String pkgprefix = null;
private final List files = new ArrayList();
private final Globals globals;
public static void main(String[] args) throws IOException {
new luajc(args);
}
private luajc(String[] args) throws IOException {
// process args
List seeds = new ArrayList();
// get stateful args
for (int i = 0; i < args.length; i++) {
if (!args[i].startsWith("-")) {
seeds.add(args[i]);
} else {
switch (args[i].charAt(1)) {
case 's':
if (++i >= args.length)
usageExit();
srcdir = args[i];
break;
case 'd':
if (++i >= args.length)
usageExit();
destdir = args[i];
break;
case 'l':
loadclasses = true;
break;
case 'p':
if (++i >= args.length)
usageExit();
pkgprefix = args[i];
break;
case 'm':
genmain = true;
break;
case 'r':
recurse = true;
break;
case 'c':
if (++i >= args.length)
usageExit();
encoding = args[i];
break;
case 'v':
verbose = true;
break;
default:
usageExit();
break;
}
}
}
// echo version
if (verbose) {
System.out.println(version);
System.out.println("srcdir: " + srcdir);
System.out.println("destdir: " + destdir);
System.out.println("files: " + seeds);
System.out.println("recurse: " + recurse);
}
// need at least one seed
if (seeds.size() <= 0) {
System.err.println(usage);
System.exit(-1);
}
// collect up files to process
for (Object seed : seeds)
collectFiles(srcdir + "/" + seed);
// check for at least one file
if (files.size() <= 0) {
System.err.println("no files found in " + seeds);
System.exit(-1);
}
// process input files
globals = JsePlatform.standardGlobals();
for (Object file : files)
processFile((InputFile) file);
}
private void collectFiles(String path) {
File f = new File(path);
if (f.isDirectory() && recurse)
scandir(f, pkgprefix);
else if (f.isFile()) {
File dir = f.getAbsoluteFile().getParentFile();
if (dir != null)
scanfile(dir, f, pkgprefix);
}
}
private void scandir(File dir, String javapackage) {
File[] f = dir.listFiles();
for (File element : f)
scanfile(dir, element, javapackage);
}
private void scanfile(File dir, File f, String javapackage) {
if (f.exists()) {
if (f.isDirectory() && recurse)
scandir(f, javapackage != null? javapackage + "." + f.getName(): f.getName());
else if (f.isFile() && f.getName().endsWith(".lua"))
files.add(new InputFile(dir, f, javapackage));
}
}
private static final class LocalClassLoader extends ClassLoader {
private final Hashtable t;
private LocalClassLoader(Hashtable t) {
this.t = t;
}
@Override
public Class findClass(String classname) throws ClassNotFoundException {
byte[] bytes = (byte[]) t.get(classname);
if (bytes != null)
return defineClass(classname, bytes, 0, bytes.length);
return super.findClass(classname);
}
}
class InputFile {
public String luachunkname;
public String srcfilename;
public File infile;
public File outdir;
public String javapackage;
public InputFile(File dir, File f, String javapackage) {
this.infile = f;
String subdir = javapackage != null? javapackage.replace('.', '/'): null;
String outdirpath = subdir != null? destdir + "/" + subdir: destdir;
this.javapackage = javapackage;
this.srcfilename = (subdir != null? subdir + "/": "")+infile.getName();
this.luachunkname = (subdir != null? subdir + "/": "")
+infile.getName().substring(0, infile.getName().lastIndexOf('.'));
this.infile = f;
this.outdir = new File(outdirpath);
}
}
private void processFile(InputFile inf) {
inf.outdir.mkdirs();
try {
if (verbose)
System.out.println("chunk=" + inf.luachunkname + " srcfile=" + inf.srcfilename);
// create the chunk
FileInputStream fis = new FileInputStream(inf.infile);
final Hashtable t = encoding != null
? LuaJC.instance.compileAll(new InputStreamReader(fis, encoding), inf.luachunkname, inf.srcfilename,
globals, genmain)
: LuaJC.instance.compileAll(fis, inf.luachunkname, inf.srcfilename, globals, genmain);
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
if (loadclasses) {
ClassLoader loader = new LocalClassLoader(t);
for (Enumeration e = t.keys(); e.hasMoreElements();) {
String classname = (String) e.nextElement();
try {
Class c = loader.loadClass(classname);
Object o = c.newInstance();
if (verbose)
System.out.println(" loaded " + classname + " as " + o);
} catch (Exception ex) {
System.out.flush();
System.err.println(" failed to load " + classname + ": " + ex);
System.err.flush();
}
}
}
} catch (Exception e) {
System.err.println(" failed to load " + inf.srcfilename + ": " + e);
e.printStackTrace(System.err);
System.err.flush();
}
}
}

View File

@@ -26,15 +26,16 @@ 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<>();
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);
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }

View File

@@ -28,7 +28,7 @@ public class Chunk extends SyntaxElement {
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,8 +24,7 @@ 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) {
@@ -33,7 +32,7 @@ public class Exp extends SyntaxElement {
} }
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,56 +44,75 @@ 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);
} }
} }
@@ -145,30 +163,40 @@ public class Exp extends SyntaxElement {
} }
abstract public static class PrimaryExp extends Exp { abstract public static class PrimaryExp extends Exp {
@Override
public boolean isvarexp() { public boolean isvarexp() {
return false; return false;
} }
@Override
public boolean isfunccall() { public boolean isfunccall() {
return false; return false;
} }
} }
abstract public static class VarExp extends PrimaryExp { abstract public static class VarExp extends PrimaryExp {
@Override
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);
} }
@Override
public void markHasAssignment() { public void markHasAssignment() {
name.variable.hasassignments = true; name.variable.hasassignments = true;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -176,10 +204,12 @@ public class Exp extends SyntaxElement {
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;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -188,11 +218,13 @@ public class Exp extends SyntaxElement {
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);
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -201,11 +233,13 @@ public class Exp extends SyntaxElement {
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;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -220,14 +254,17 @@ public class Exp extends SyntaxElement {
this.args = args; this.args = args;
} }
@Override
public boolean isfunccall() { public boolean isfunccall() {
return true; return true;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@Override
public boolean isvarargexp() { public boolean isvarargexp() {
return true; return true;
} }
@@ -241,10 +278,12 @@ public class Exp extends SyntaxElement {
this.name = new String(name); this.name = new String(name);
} }
@Override
public boolean isfunccall() { public boolean isfunccall() {
return true; return true;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -252,10 +291,12 @@ 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;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -263,10 +304,12 @@ public class Exp extends SyntaxElement {
public static class VarargsExp extends Exp { public static class VarargsExp extends Exp {
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@Override
public boolean isvarargexp() { public boolean isvarargexp() {
return true; return true;
} }
@@ -275,25 +318,29 @@ public class Exp extends SyntaxElement {
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;
} }
@Override
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;
this.rhs = rhs; this.rhs = rhs;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
@@ -301,10 +348,12 @@ public class Exp extends SyntaxElement {
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;
} }
@Override
public void accept(Visitor visitor) { public void accept(Visitor visitor) {
visitor.visit(this); visitor.visit(this);
} }

View File

@@ -50,13 +50,13 @@ public class FuncArgs extends SyntaxElement {
} }
public FuncArgs(LuaString string) { public FuncArgs(LuaString string) {
this.exps = new ArrayList<Exp>(); this.exps = new ArrayList<>();
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<>();
this.exps.add( table ); this.exps.add(table);
} }
public void accept(Visitor visitor) { public void accept(Visitor visitor) {

View File

@@ -27,9 +27,10 @@ public class FuncBody extends SyntaxElement {
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

@@ -36,13 +36,13 @@ public class FuncName extends SyntaxElement {
// 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<>();
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;
} }

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