diff options
-rw-r--r-- | compiler/driver/compiler_driver.cc | 12 | ||||
-rw-r--r-- | runtime/interpreter/unstarted_runtime_test.cc | 43 | ||||
-rw-r--r-- | runtime/runtime.cc | 19 | ||||
-rw-r--r-- | runtime/runtime.h | 9 | ||||
-rw-r--r-- | runtime/transaction.cc | 5 | ||||
-rw-r--r-- | runtime/transaction.h | 3 | ||||
-rw-r--r-- | runtime/transaction_test.cc | 59 | ||||
-rw-r--r-- | test/660-clinit/expected.txt | 9 | ||||
-rw-r--r-- | test/660-clinit/info.txt | 1 | ||||
-rw-r--r-- | test/660-clinit/src/Main.java | 115 |
10 files changed, 200 insertions, 75 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index cf04e41d5c..dd58bc7d13 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2331,10 +2331,8 @@ class InitializeClassVisitor : public CompilationVisitor { // a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity // checks in Thread::AssertThreadSuspensionIsAllowable. Runtime* const runtime = Runtime::Current(); - Transaction transaction; - // Run the class initializer in transaction mode. - runtime->EnterTransactionMode(&transaction); + runtime->EnterTransactionMode(klass.Get()); bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true, true); // TODO we detach transaction from runtime to indicate we quit the transactional @@ -2343,7 +2341,11 @@ class InitializeClassVisitor : public CompilationVisitor { { ScopedAssertNoThreadSuspension ants("Transaction end"); - runtime->ExitTransactionMode(); + + if (success) { + runtime->ExitTransactionMode(); + DCHECK(!runtime->IsActiveTransaction()); + } if (!success) { CHECK(soa.Self()->IsExceptionPending()); @@ -2357,7 +2359,7 @@ class InitializeClassVisitor : public CompilationVisitor { *file_log << exception->Dump() << "\n"; } soa.Self()->ClearException(); - transaction.Rollback(); + runtime->RollbackAndExitTransactionMode(); CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored"; } else if (is_boot_image) { // For boot image, we want to put the updated status in the oat class since we can't diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 3461a6503a..87fa8beb01 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -780,44 +780,40 @@ TEST_F(UnstartedRuntimeTest, ToLowerUpper) { { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToLowerCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToUpperCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } } for (uint64_t i = 256; i <= std::numeric_limits<uint32_t>::max(); i <<= 1) { { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToLowerCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } { JValue result; tmp->SetVReg(0, static_cast<int32_t>(i)); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedCharacterToUpperCase(self, tmp, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); } } @@ -996,12 +992,11 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0); shadow_frame->SetLink(caller_frame); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedThreadLocalGet(self, shadow_frame, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); self->ClearException(); ShadowFrame::DeleteDeoptimizedFrame(caller_frame); @@ -1066,12 +1061,11 @@ TEST_F(UnstartedRuntimeTest, ThreadCurrentThread) { PrepareForAborts(); { - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); UnstartedThreadCurrentThread(self, shadow_frame, &result, 0); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(self->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); self->ClearException(); } @@ -1138,28 +1132,27 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest { mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name); CHECK(name_string != nullptr); - Transaction transaction; if (in_transaction) { - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); } CHECK(!self->IsExceptionPending()); runner(self, shadow_frame, name_string, &result); - if (in_transaction) { - Runtime::Current()->ExitTransactionMode(); - } - if (should_succeed) { CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump(); CHECK(result.GetL() != nullptr) << name; } else { CHECK(self->IsExceptionPending()) << name; if (in_transaction) { - ASSERT_TRUE(transaction.IsAborted()); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); } self->ClearException(); } + + if (in_transaction) { + Runtime::Current()->ExitTransactionMode(); + } } ShadowFrame::DeleteDeoptimizedFrame(shadow_frame); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index ebee5ea187..2366e10fae 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -2065,19 +2065,30 @@ void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, } // Transaction support. -void Runtime::EnterTransactionMode(Transaction* transaction) { +void Runtime::EnterTransactionMode() { DCHECK(IsAotCompiler()); - DCHECK(transaction != nullptr); DCHECK(!IsActiveTransaction()); - preinitialization_transaction_ = transaction; + preinitialization_transaction_ = std::make_unique<Transaction>(); +} + +void Runtime::EnterTransactionMode(mirror::Class* root) { + DCHECK(IsAotCompiler()); + preinitialization_transaction_ = std::make_unique<Transaction>(root); } void Runtime::ExitTransactionMode() { DCHECK(IsAotCompiler()); - DCHECK(IsActiveTransaction()); preinitialization_transaction_ = nullptr; } +void Runtime::RollbackAndExitTransactionMode() { + DCHECK(IsAotCompiler()); + DCHECK(IsActiveTransaction()); + std::unique_ptr<Transaction> rollback_transaction_= std::move(preinitialization_transaction_); + ExitTransactionMode(); + rollback_transaction_->Rollback(); +} + bool Runtime::IsTransactionAborted() const { if (!IsActiveTransaction()) { return false; diff --git a/runtime/runtime.h b/runtime/runtime.h index af9d215454..9424596c8a 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -24,6 +24,7 @@ #include <set> #include <string> #include <utility> +#include <memory> #include <vector> #include "arch/instruction_set.h" @@ -457,8 +458,12 @@ class Runtime { bool IsActiveTransaction() const { return preinitialization_transaction_ != nullptr; } - void EnterTransactionMode(Transaction* transaction); + void EnterTransactionMode(); + void EnterTransactionMode(mirror::Class* root); void ExitTransactionMode(); + // Transaction rollback and exit transaction are always done together, it's convenience to + // do them in one function. + void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_); bool IsTransactionAborted() const; void AbortTransactionAndThrowAbortError(Thread* self, const std::string& abort_message) @@ -842,7 +847,7 @@ class Runtime { bool dump_gc_performance_on_shutdown_; // Transaction used for pre-initializing classes at compilation time. - Transaction* preinitialization_transaction_; + std::unique_ptr<Transaction> preinitialization_transaction_; // If kNone, verification is disabled. kEnable by default. verifier::VerifyMode verify_; diff --git a/runtime/transaction.cc b/runtime/transaction.cc index 907d37ef31..359030e6d6 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -38,6 +38,10 @@ Transaction::Transaction() CHECK(Runtime::Current()->IsAotCompiler()); } +Transaction::Transaction(mirror::Class* root) : Transaction() { + root_ = root; +} + Transaction::~Transaction() { if (kEnableTransactionStats) { MutexLock mu(Thread::Current(), log_lock_); @@ -270,6 +274,7 @@ void Transaction::UndoResolveStringModifications() { void Transaction::VisitRoots(RootVisitor* visitor) { MutexLock mu(Thread::Current(), log_lock_); + visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&root_), RootInfo(kRootUnknown)); VisitObjectLogs(visitor); VisitArrayLogs(visitor); VisitInternStringLogs(visitor); diff --git a/runtime/transaction.h b/runtime/transaction.h index 747c2d0f38..22518f6c7e 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -32,6 +32,7 @@ namespace art { namespace mirror { class Array; +class Class; class DexCache; class Object; class String; @@ -44,6 +45,7 @@ class Transaction FINAL { static constexpr const char* kAbortExceptionSignature = "Ldalvik/system/TransactionAbortError;"; Transaction(); + explicit Transaction(mirror::Class* root); ~Transaction(); void Abort(const std::string& abort_message) @@ -288,6 +290,7 @@ class Transaction FINAL { std::list<ResolveStringLog> resolve_string_logs_ GUARDED_BY(log_lock_); bool aborted_ GUARDED_BY(log_lock_); std::string abort_message_ GUARDED_BY(log_lock_); + mirror::Class* root_ GUARDED_BY(log_lock_); DISALLOW_COPY_AND_ASSIGN(Transaction); }; diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index 920629276a..e52dd08540 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -69,14 +69,12 @@ class TransactionTest : public CommonRuntimeTest { mirror::Class::Status old_status = h_klass->GetStatus(); LockWord old_lock_word = h_klass->GetLockWord(false); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); - Runtime::Current()->ExitTransactionMode(); + ASSERT_TRUE(Runtime::Current()->IsTransactionAborted()); ASSERT_FALSE(success); ASSERT_TRUE(h_klass->IsErroneous()); ASSERT_TRUE(soa.Self()->IsExceptionPending()); - ASSERT_TRUE(transaction.IsAborted()); // Check class's monitor get back to its original state without rolling back changes. LockWord new_lock_word = h_klass->GetLockWord(false); @@ -84,7 +82,7 @@ class TransactionTest : public CommonRuntimeTest { // Check class status is rolled back properly. soa.Self()->ClearException(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); ASSERT_EQ(old_status, h_klass->GetStatus()); } }; @@ -97,15 +95,12 @@ TEST_F(TransactionTest, Object_class) { hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); ASSERT_TRUE(h_klass != nullptr); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); - Runtime::Current()->ExitTransactionMode(); - // Rolling back transaction's changes must not clear the Object::class field. - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); EXPECT_EQ(h_obj->GetClass(), h_klass.Get()); } @@ -124,15 +119,13 @@ TEST_F(TransactionTest, Object_monitor) { h_obj->MonitorEnter(soa.Self()); LockWord old_lock_word = h_obj->GetLockWord(false); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); // Unlock object's monitor inside the transaction. h_obj->MonitorExit(soa.Self()); LockWord new_lock_word = h_obj->GetLockWord(false); - Runtime::Current()->ExitTransactionMode(); - // Rolling back transaction's changes must not change monitor's state. - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); + LockWord aborted_lock_word = h_obj->GetLockWord(false); EXPECT_FALSE(LockWord::Equal<false>(old_lock_word, new_lock_word)); EXPECT_TRUE(LockWord::Equal<false>(aborted_lock_word, new_lock_word)); @@ -148,8 +141,7 @@ TEST_F(TransactionTest, Array_length) { constexpr int32_t kArraySize = 2; - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); // Allocate an array during transaction. Handle<mirror::Array> h_obj( @@ -159,10 +151,9 @@ TEST_F(TransactionTest, Array_length) { Runtime::Current()->GetHeap()->GetCurrentAllocator()))); ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); - Runtime::Current()->ExitTransactionMode(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Rolling back transaction's changes must not reset array's length. - transaction.Rollback(); EXPECT_EQ(h_obj->GetLength(), kArraySize); } @@ -238,8 +229,7 @@ TEST_F(TransactionTest, StaticFieldsTest) { ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); booleanField->SetBoolean<true>(h_klass.Get(), true); byteField->SetByte<true>(h_klass.Get(), 1); charField->SetChar<true>(h_klass.Get(), 1u); @@ -249,8 +239,7 @@ TEST_F(TransactionTest, StaticFieldsTest) { floatField->SetFloat<true>(h_klass.Get(), 1.0); doubleField->SetDouble<true>(h_klass.Get(), 1.0); objectField->SetObject<true>(h_klass.Get(), h_obj.Get()); - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanField->GetBoolean(h_klass.Get()), false); @@ -340,8 +329,7 @@ TEST_F(TransactionTest, InstanceFieldsTest) { ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); booleanField->SetBoolean<true>(h_instance.Get(), true); byteField->SetByte<true>(h_instance.Get(), 1); charField->SetChar<true>(h_instance.Get(), 1u); @@ -351,8 +339,7 @@ TEST_F(TransactionTest, InstanceFieldsTest) { floatField->SetFloat<true>(h_instance.Get(), 1.0); doubleField->SetDouble<true>(h_instance.Get(), 1.0); objectField->SetObject<true>(h_instance.Get(), h_obj.Get()); - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanField->GetBoolean(h_instance.Get()), false); @@ -457,8 +444,7 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) { ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); booleanArray->SetWithoutChecks<true>(0, true); byteArray->SetWithoutChecks<true>(0, 1); charArray->SetWithoutChecks<true>(0, 1u); @@ -468,8 +454,7 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) { floatArray->SetWithoutChecks<true>(0, 1.0); doubleArray->SetWithoutChecks<true>(0, 1.0); objectArray->SetWithoutChecks<true>(0, h_obj.Get()); - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check values have properly been restored to their original (default) value. EXPECT_EQ(booleanArray->GetWithoutChecks(0), false); @@ -511,8 +496,7 @@ TEST_F(TransactionTest, ResolveString) { EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); // Do the transaction, then roll back. - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(success); ASSERT_TRUE(h_klass->IsInitialized()); @@ -523,8 +507,7 @@ TEST_F(TransactionTest, ResolveString) { EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString); EXPECT_EQ(s, h_dex_cache->GetResolvedString(string_idx)); } - Runtime::Current()->ExitTransactionMode(); - transaction.Rollback(); + Runtime::Current()->RollbackAndExitTransactionMode(); // Check that the string did not stay resolved. EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); @@ -547,8 +530,7 @@ TEST_F(TransactionTest, EmptyClass) { class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(success); @@ -571,8 +553,7 @@ TEST_F(TransactionTest, StaticFieldClass) { class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); - Transaction transaction; - Runtime::Current()->EnterTransactionMode(&transaction); + Runtime::Current()->EnterTransactionMode(); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); Runtime::Current()->ExitTransactionMode(); ASSERT_TRUE(success); diff --git a/test/660-clinit/expected.txt b/test/660-clinit/expected.txt new file mode 100644 index 0000000000..e103a2c6a5 --- /dev/null +++ b/test/660-clinit/expected.txt @@ -0,0 +1,9 @@ +JNI_OnLoad called +X: 4950 +Y: 5730 +str: Hello World! +ooo: OoooooO +Z: 11206655 +A: 100 +AA: 100 +a != 101 diff --git a/test/660-clinit/info.txt b/test/660-clinit/info.txt new file mode 100644 index 0000000000..da0193de0b --- /dev/null +++ b/test/660-clinit/info.txt @@ -0,0 +1 @@ +Tests that class initializers are executed correctly. diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java new file mode 100644 index 0000000000..f5476925a0 --- /dev/null +++ b/test/660-clinit/src/Main.java @@ -0,0 +1,115 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.*; + +public class Main { + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + if (!checkAppImageLoaded()) { + System.out.println("AppImage not loaded."); + } + + expectNotPreInit(Day.class); + expectNotPreInit(ClInit.class); + + ClInit c = new ClInit(); + int aa = c.a; + + System.out.println("X: " + c.getX()); + System.out.println("Y: " + c.getY()); + System.out.println("str: " + c.str); + System.out.println("ooo: " + c.ooo); + System.out.println("Z: " + c.getZ()); + System.out.println("A: " + c.getA()); + System.out.println("AA: " + aa); + + if (c.a != 101) { + System.out.println("a != 101"); + } + + return; + } + + static void expectPreInit(Class<?> klass) { + if (checkInitialized(klass) == false) { + System.out.println(klass.getName() + " should be initialized!"); + } + } + + static void expectNotPreInit(Class<?> klass) { + if (checkInitialized(klass) == true) { + System.out.println(klass.getName() + " should not be initialized!"); + } + } + + public static native boolean checkAppImageLoaded(); + public static native boolean checkAppImageContains(Class<?> klass); + public static native boolean checkInitialized(Class<?> klass); +} + +enum Day { + SUNDAY, MONDAY, TUESDAY, WEDNESDAY, + THURSDAY, FRIDAY, SATURDAY +} + +class ClInit { + + static String ooo = "OoooooO"; + static String str; + static int z; + static int x, y; + public static volatile int a = 100; + + static { + StringBuilder sb = new StringBuilder(); + sb.append("Hello "); + sb.append("World!"); + str = sb.toString(); + + z = 0xFF; + z += 0xFF00; + z += 0xAA0000; + + for(int i = 0; i < 100; i++) { + x += i; + } + + y = x; + for(int i = 0; i < 40; i++) { + y += i; + } + } + + int getX() { + return x; + } + + int getZ() { + return z; + } + + int getY() { + return y; + } + + int getA() { + return a; + } +} + |