From 1907949dd09cba4fafdcb4d370957078d93e6685 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Fri, 8 Apr 2016 17:11:13 +0300 Subject: [PATCH 1/5] add test cases for when objects are constructed with .nulls --- .../tests/testMethodResolutionWithNulls.js | 125 ++++++++++----- test-app/assets/metadata/treeNodeStream.dat | Bin 54096 -> 54096 bytes .../assets/metadata/treeStringsStream.dat | Bin 574821 -> 574838 bytes test-app/assets/metadata/treeValueStream.dat | Bin 650592 -> 650658 bytes test-app/src/com/tns/tests/DummyClass.java | 143 +++++++++++------- 5 files changed, 168 insertions(+), 100 deletions(-) diff --git a/test-app/assets/app/tests/testMethodResolutionWithNulls.js b/test-app/assets/app/tests/testMethodResolutionWithNulls.js index 983ab3a5d..983bdcd31 100644 --- a/test-app/assets/app/tests/testMethodResolutionWithNulls.js +++ b/test-app/assets/app/tests/testMethodResolutionWithNulls.js @@ -1,28 +1,28 @@ describe("Test Method Resolution When Nulls are passed", function () { var dummyClass = new com.tns.tests.DummyClass(); - + var objNull = java.lang.Object.null; var dummyNull = com.tns.tests.DummyClass.null; var staticInterfaceNull = com.tns.tests.DummyClass.MyInterface.null; var publicInterfaceNull = com.tns.tests.MyPublicInterface.null; var fileNull = java.io.File.null; var stringNull = java.lang.String.null; - + it("When_accessing_.null_of_a_node_the_same_instance_will_be_returned", function () { __log("TEST: When_accessing_.null_of_a_node_the_same_instance_will_be_returned"); - + var dummyNull = com.tns.tests.DummyClass.null; var testIterations = 10; - + for (var i = 0; i < testIterations; i++) { var res = dummyNull === com.tns.tests.DummyClass.null; expect(res).toBe(true); } }); - + it("When_call_method_valueOf_of_.null_object_should_return_null", function () { __log("TEST: When_call_method_valueOf_of_.null_object_should_return_null"); - + var res = objNull.valueOf(); expect(res).toBeNull(); res = stringNull.valueOf(); @@ -30,100 +30,141 @@ describe("Test Method Resolution When Nulls are passed", function () { res = dummyNull.valueOf(); expect(res).toBeNull(); }); - + it("When_call_method_methodWithOverloadsWithOneArgument_with_java.lang.Object_argument", function () { __log("TEST: When_call_method_methodWithOverloadsWithOneArgument_with_java.lang.Object_argument"); - + var result = dummyClass.methodWithOverloadsWithOneArgument(objNull); - + expect(result).toContain("Object"); }); - + it("When_call_method_methodWithOverloadsWithOneArgument_with_com.tns.tests.DummyClass_argument", function () { __log("TEST: When_call_method_methodWithOverloadsWithOneArgument_with_com.tns.tests.DummyClass_argument"); - + var result = dummyClass.methodWithOverloadsWithOneArgument(dummyNull); - + expect(result).toContain("DummyClass"); }); - + it("When_call_method_methodWithOverloadsWithOneArgument_with_java.lang.String_argument", function () { __log("TEST: When_call_method_methodWithOverloadsWithOneArgument_with_java.lang.String_argument"); - + var result = dummyClass.methodWithOverloadsWithOneArgument(stringNull); - + expect(result).toContain("String"); }); - + it("When_call_method_methodWithOverloadsWithOneArgument_with_java.io.File_argument", function () { __log("TEST: When_call_method_methodWithOverloadsWithOneArgument_with_java.io.File_argument"); - + var result = dummyClass.methodWithOverloadsWithOneArgument(fileNull); - + expect(result).toContain("File"); }); - + it("When_call_method_methodWithOverloadsWithOneArgument_with_com.tns.tests.DummyClass.MyInterface_argument", function () { __log("TEST: When_call_method_methodWithOverloadsWithOneArgument_with_com.tns.tests.DummyClass.MyInterface_argument"); - + var result = dummyClass.methodWithOverloadsWithOneArgument(staticInterfaceNull); - + expect(result).toContain("MyInterface"); }); - + it("When_call_method_methodWithOverloadsWithOneArgument_with_com.tns.tests.MyPublicInterface_argument", function () { __log("TEST: When_call_method_methodWithOverloadsWithOneArgument_with_com.tns.tests.MyPublicInterface_argument"); - + var result = dummyClass.methodWithOverloadsWithOneArgument(publicInterfaceNull); - + expect(result).toContain("MyPublicInterface"); }); - + it("When_call_method_methodWithOverloadsWithThreeArguments_with_Object_String_Object_arguments", function() { __log("TEST: When_call_method_methodWithOverloadsWithThreeArguments_with_Object_String_Object_arguments"); - + var result = dummyClass.methodWithOverloadsWithThreeArguments(objNull, stringNull, objNull); - + expect(result).toMatch(/\S*.Object and \S*.String and \S*.Object/); }); - + it("When_call_method_methodWithOverloadsWithThreeArguments_with_Object_String_MyInterface_arguments", function() { __log("TEST: When_call_method_methodWithOverloadsWithThreeArguments_with_Object_String_MyInterface_arguments"); - + var result = dummyClass.methodWithOverloadsWithThreeArguments(objNull, stringNull, staticInterfaceNull); - + expect(result).toMatch(/\S*.Object and \S*.String and \S*.MyInterface/); }); - + it("When_call_method_methodWithOverloadsWithThreeArguments_with_Object_String_MyPublicInterface_arguments", function() { __log("TEST: When_call_method_methodWithOverloadsWithThreeArguments_with_Object_String_MyPublicInterface_arguments"); - + var result = dummyClass.methodWithOverloadsWithThreeArguments(objNull, stringNull, publicInterfaceNull); - + expect(result).toMatch(/\S*.Object and \S*.String and \S*.MyPublicInterface/); }); - + it("When_call_method_methodWithOverloadsWithThreeArguments_with_Object_Object_Object_arguments", function() { __log("TEST: When_call_method_methodWithOverloadsWithThreeArguments_with_Object_Object_Object_arguments"); - + var result = dummyClass.methodWithOverloadsWithThreeArguments(objNull, objNull, objNull); - + expect(result).toMatch(/\S*.Object and \S*.Object and \S*.Object/); }); - + it("When_call_method_methodWithOverloadsWithThreeArguments_with_MyInterface_MyInterface_MyPublicInterface_arguments", function() { __log("TEST: When_call_method_methodWithOverloadsWithThreeArguments_with_MyInterface_MyInterface_MyPublicInterface_arguments"); - + var result = dummyClass.methodWithOverloadsWithThreeArguments(staticInterfaceNull, staticInterfaceNull, publicInterfaceNull); - + expect(result).toMatch(/\S*.MyInterface and \S*.MyInterface and \S*.MyPublicInterface/); }); - + it("When_call_method_methodWithOverloadsWithThreeArguments_with_String_Object_Object_arguments", function() { __log("TEST: When_call_method_methodWithOverloadsWithThreeArguments_with_String_Object_Object_arguments"); - + var result = dummyClass.methodWithOverloadsWithThreeArguments(stringNull, objNull, objNull); - + expect(result).toMatch(/\S*.String and \S*.Object and \S*.Object/); }); +}); + +describe("Test Constructor Resolution When Nulls are passed", function () { + var objNull = java.lang.Object.null; + var dummyNull = com.tns.tests.DummyClass.null; + var staticInterfaceNull = com.tns.tests.DummyClass.MyInterface.null; + var publicInterfaceNull = com.tns.tests.MyPublicInterface.null; + var fileNull = java.io.File.null; + var stringNull = java.lang.String.null; + + it("When_call_DummyClass_ctor_with_Object_argument", function () { + __log("TEST: When_call_DummyClass_ctor_with_Object_argument"); + + var result = new com.tns.tests.DummyClass(objNull).arbitraryString; + + expect(result).toContain("Object"); + }); + + it("When_call_DummyClass_ctor_with_DummyClass_argument", function () { + __log("TEST: When_call_DummyClass_ctor_with_DummyClass_argument"); + + var result = new com.tns.tests.DummyClass(dummyNull).arbitraryString; + + expect(result).toContain("DummyClass"); + }); + + it("When_call_DummyClass_ctor_with_com.tns.tests.DummyClass.MyInterface_argument", function () { + __log("TEST: When_call_DummyClass_ctor_with_com.tns.tests.DummyClass.MyInterface_argument"); + + var result = new com.tns.tests.DummyClass(staticInterfaceNull).arbitraryString; + + expect(result).toContain("MyInterface"); + }); + + it("When_call_DummyClass_ctor_with_com.tns.tests.DummyClass_and_Object_arguments", function () { + __log("TEST: When_call_DummyClass_ctor_with_com.tns.tests.DummyClass_and_Object_arguments"); + + var result = new com.tns.tests.DummyClass(dummyNull, objNull).arbitraryString; + + expect(result).toMatch(/\S*.DummyClass and \S*.Object/); + }); }); \ No newline at end of file diff --git a/test-app/assets/metadata/treeNodeStream.dat b/test-app/assets/metadata/treeNodeStream.dat index 22b347b3b91e8db8ed39ba67b2e20d45de06ea3c..7019f2375155d131cfaa9a86efd351491417fb3a 100644 GIT binary patch delta 12295 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 SJ$x2#;~8#7*f}0~=KlZ?U8d~- diff --git a/test-app/assets/metadata/treeStringsStream.dat b/test-app/assets/metadata/treeStringsStream.dat index e758ce8b9659a4be239ddedb038a23e1ed5edee1..614843068b360daea632361491222af0cb2c7a72 100644 GIT binary patch delta 47 zcmaFbrTnc+xuJ!zg{g(Pg{6hHg>4J_{?CH^42eZanI%PuMU}xNMVWc&+xLHFUv?1y Dybu$f delta 29 lcmey?rTnx@xuJ!zg{g(Pg{6hHg>4J_{?FSld}d#E5dgYL4M+e0 diff --git a/test-app/assets/metadata/treeValueStream.dat b/test-app/assets/metadata/treeValueStream.dat index ea3431affcc4686d9eb1f305526696ebd27ad235..9e560c966746207c83f71abe983adb323540f707 100644 GIT binary patch delta 36118 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 delta 36392 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"); Log.d("NativeScript.Java", "inside DummyClass.method2 with ret=" + ret); return ret; } - + public String methodWithoutOverloads(float value) { return "float=" + value; } - + public int triggerEchoInt(MyInterface impl, int i) { int ret = impl.echoInt(i); @@ -302,55 +329,55 @@ public void triggerDoSomething(MyInterface impl) { impl.doSomething(); } - + public String methodWithOverloadsWithOneArgument(Object arg) { return Object.class.getName(); } - + public String methodWithOverloadsWithOneArgument(DummyClass arg) { return DummyClass.class.getName(); } - + public String methodWithOverloadsWithOneArgument(String arg) { return String.class.getName(); } - + public String methodWithOverloadsWithOneArgument(File arg) { return File.class.getName(); } - + public String methodWithOverloadsWithOneArgument(MyInterface arg) { return MyInterface.class.getName(); } - + public String methodWithOverloadsWithOneArgument(MyPublicInterface arg) { return MyPublicInterface.class.getName(); } - + public String methodWithOverloadsWithThreeArguments(Object arg1, String arg2, Object arg3) { return new StringBuilder(Object.class.getName()).append(separator).append(String.class.getName()).append(separator).append(Object.class.getName()).toString(); } - + public String methodWithOverloadsWithThreeArguments(Object arg1, String arg2, MyInterface arg3) { return new StringBuilder(Object.class.getName()).append(separator).append(String.class.getName()).append(separator).append(MyInterface.class.getName()).toString(); } - + public String methodWithOverloadsWithThreeArguments(Object arg1, String arg2, MyPublicInterface arg3) { return new StringBuilder(Object.class.getName()).append(separator).append(String.class.getName()).append(separator).append(MyPublicInterface.class.getName()).toString(); } - + public String methodWithOverloadsWithThreeArguments(Object arg1, Object arg2, Object arg3) { return new StringBuilder(Object.class.getName()).append(separator).append(Object.class.getName()).append(separator).append(Object.class.getName()).toString(); } - + public String methodWithOverloadsWithThreeArguments(MyInterface arg1, MyInterface arg2, MyPublicInterface arg3) { return new StringBuilder(MyInterface.class.getName()).append(separator).append(MyInterface.class.getName()).append(separator).append(MyPublicInterface.class.getName()).toString(); } - + public String methodWithOverloadsWithThreeArguments(String arg1, Object arg2, Object arg3) { return new StringBuilder(String.class.getName()).append(separator).append(Object.class.getName()).append(separator).append(Object.class.getName()).toString(); } - + private final String logTag = "TNS.Java"; private final String separator = " and "; } From b09471347d8e757c9b71e96e5d086c30c190a83d Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 11 Apr 2016 10:06:25 +0300 Subject: [PATCH 2/5] add simple JsArgConverter constructor; add NewObjectA to accept an array of jvalue add working object instantiation in cpp refactor unused and unaccessed code add constructor caching with the updated structure --- src/jni/CallbackHandlers.cpp | 146 ++++++++++------------------ src/jni/CallbackHandlers.h | 9 +- src/jni/JEnv.h | 7 ++ src/jni/JniSignatureParser.cpp | 37 ++++--- src/jni/JsArgConverter.cpp | 70 ++++++++++++- src/jni/JsArgConverter.h | 4 + src/jni/JsArgToArrayConverter.cpp | 6 ++ src/jni/MetadataNode.cpp | 20 +++- src/jni/MetadataNode.h | 20 ++++ src/jni/MethodCache.cpp | 71 +++++++++++++- src/jni/MethodCache.h | 7 ++ src/src/com/tns/MethodResolver.java | 21 +++- src/src/com/tns/Runtime.java | 79 ++++----------- test-app/assets/app/MyActivity.js | 13 ++- test-app/assets/app/tests/tests.js | 2 +- 15 files changed, 319 insertions(+), 193 deletions(-) diff --git a/src/jni/CallbackHandlers.cpp b/src/jni/CallbackHandlers.cpp index 4080279dc..5cbce3dd2 100644 --- a/src/jni/CallbackHandlers.cpp +++ b/src/jni/CallbackHandlers.cpp @@ -39,11 +39,11 @@ void CallbackHandlers::Init(Isolate *isolate, ObjectManager *objectManager) RESOLVE_CLASS_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "resolveClass", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Class;"); assert(RESOLVE_CLASS_METHOD_ID != nullptr); - CREATE_INSTANCE_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "createInstance", "([Ljava/lang/Object;II)Ljava/lang/Object;"); - assert(CREATE_INSTANCE_METHOD_ID != nullptr); + CURRENT_OBJECTID_FIELD_ID = env.GetStaticFieldID(RUNTIME_CLASS, "currentObjectId", "I"); + assert(CURRENT_OBJECTID_FIELD_ID != nullptr); - CACHE_CONSTRUCTOR_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "cacheConstructor", "(Ljava/lang/Class;[Ljava/lang/Object;)I"); - assert(CACHE_CONSTRUCTOR_METHOD_ID != nullptr); + MAKE_INSTANCE_STRONG_ID = env.GetMethodID(RUNTIME_CLASS, "makeInstanceStrong", "(Ljava/lang/Object;I)V"); + assert(MAKE_INSTANCE_STRONG_ID != nullptr); GET_TYPE_METADATA = env.GetStaticMethodID(RUNTIME_CLASS, "getTypeMetadata", "(Ljava/lang/String;I)[Ljava/lang/String;"); assert(GET_TYPE_METADATA != nullptr); @@ -80,7 +80,31 @@ bool CallbackHandlers::RegisterInstance(Isolate *isolate, const Local& j DEBUG_WRITE("RegisterInstance: Linking new instance"); objectManager->Link(jsObject, javaObjectID, nullptr); - jobject instance = CreateJavaInstance(javaObjectID, fullClassName, argWrapper, generatedJavaClass, isInterface); + // resolve constructor + auto mi = MethodCache::ResolveConstructorSignature(argWrapper, fullClassName, generatedJavaClass, isInterface); + + jobject instance; + + env.SetStaticIntField(RUNTIME_CLASS, CURRENT_OBJECTID_FIELD_ID, javaObjectID); + + if(argWrapper.type == ArgType::Interface) + { + instance = env.NewObject(generatedJavaClass, mi.mid); + } + else + { + // resolve arguments before passing them on to the constructor + JsArgConverter argConverter(argWrapper.args, mi.signature, argWrapper.outerThis); + auto ctorArgs = argConverter.ToArgs(); + + instance = env.NewObjectA(generatedJavaClass, mi.mid, ctorArgs); + } + + env.CallVoidMethod(runtime->GetJavaRuntime(), MAKE_INSTANCE_STRONG_ID, instance, javaObjectID); + + env.SetStaticIntField(RUNTIME_CLASS, CURRENT_OBJECTID_FIELD_ID, -1); + + AdjustAmountOfExternalAllocatedMemory(env, isolate); JniLocalRef localInstance(instance); success = !localInstance.IsNull(); @@ -196,8 +220,8 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& } entry->memberId = isStatic ? - env.GetStaticMethodID(clazz, methodName, entry->sig) : - env.GetMethodID(clazz, methodName, entry->sig); + env.GetStaticMethodID(clazz, methodName, entry->sig) : + env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { @@ -209,8 +233,8 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& else { entry->memberId = isStatic ? - env.GetStaticMethodID(clazz, methodName, entry->sig) : - env.GetMethodID(clazz, methodName, entry->sig); + env.GetStaticMethodID(clazz, methodName, entry->sig) : + env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { @@ -302,7 +326,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& switch (retType) { case MethodReturnType::Void: - { + { if (isStatic) { env.CallStaticVoidMethodA(clazz, mid, javaArgs); @@ -318,7 +342,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Boolean: - { + { jboolean result; if (isStatic) { @@ -336,7 +360,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Byte: - { + { jbyte result; if (isStatic) { @@ -354,7 +378,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Char: - { + { jchar result; if (isStatic) { @@ -377,7 +401,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Short: - { + { jshort result; if (isStatic) { @@ -395,7 +419,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Int: - { + { jint result; if (isStatic) { @@ -414,7 +438,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& } case MethodReturnType::Long: - { + { jlong result; if (isStatic) { @@ -433,7 +457,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Float: - { + { jfloat result; if (isStatic) { @@ -451,7 +475,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Double: - { + { jdouble result; if (isStatic) { @@ -469,7 +493,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::String: - { + { jobject result = nullptr; bool exceptionOccurred; @@ -500,7 +524,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } case MethodReturnType::Object: - { + { jobject result = nullptr; bool exceptionOccurred; @@ -548,7 +572,7 @@ void CallbackHandlers::CallJavaMethod(const Local& caller, const string& break; } default: - { + { assert(false); break; } @@ -569,9 +593,9 @@ int64_t CallbackHandlers::AdjustAmountOfExternalAllocatedMemory(JEnv& env, Isola int64_t changeInBytes = env.CallLongMethod(runtime->GetJavaRuntime(), GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID); int64_t adjustedValue = (changeInBytes != 0) - ? isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes) - : - 0; + ? isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes) + : + 0; return adjustedValue; } @@ -584,68 +608,6 @@ Local CallbackHandlers::CreateJSWrapper(Isolate *isolate, jint javaObjec return objectManager->CreateJSWrapper(javaObjectID, typeName); } -jobject CallbackHandlers::CreateJavaInstance(int objectID, const std::string& fullClassName, const ArgsWrapper& argWrapper, jclass javaClass, bool isInterface) -{ - SET_PROFILER_FRAME(); - - jobject instance = nullptr; - DEBUG_WRITE("CreateJavaInstance: %s", fullClassName.c_str()); - - JEnv env; - auto& args = argWrapper.args; - - JsArgToArrayConverter argConverter(args, isInterface, argWrapper.outerThis); - if (argConverter.IsValid()) - { - jobjectArray javaArgs = argConverter.ToJavaArray(); - - int ctorId = GetCachedConstructorId(env, args, fullClassName, javaArgs, javaClass); - - auto runtime = Runtime::GetRuntime(args.GetIsolate()); - - jobject obj = env.CallObjectMethod(runtime->GetJavaRuntime(), - CREATE_INSTANCE_METHOD_ID, - javaArgs, - (jint) objectID, - ctorId); - - instance = obj; - } - else - { - JsArgToArrayConverter::Error err = argConverter.GetError(); - throw NativeScriptException(err.msg); - } - - return instance; -} - -int CallbackHandlers::GetCachedConstructorId(JEnv& env, const FunctionCallbackInfo& args, const string& fullClassName, jobjectArray javaArgs, jclass javaClass) -{ - int ctorId = -1; - string encodedCtorArgs = MethodCache::EncodeSignature(fullClassName, "", args, false); - auto itFound = s_constructorCache.find(encodedCtorArgs); - - if (itFound != s_constructorCache.end()) - { - ctorId = itFound->second; - } - else - { - auto runtime = Runtime::GetRuntime(args.GetIsolate()); - jint id = env.CallIntMethod(runtime->GetJavaRuntime(), CACHE_CONSTRUCTOR_METHOD_ID, javaClass, javaArgs); - - if (env.ExceptionCheck() == JNI_FALSE) - { - ctorId = id; - s_constructorCache.insert(make_pair(encodedCtorArgs, ctorId)); - } - } - - DEBUG_WRITE("GetCachedConstructorId: encodedCtorArgs=%s, ctorId=%d", encodedCtorArgs.c_str(), ctorId); - return ctorId; -} - //delete the returned local reference after use jobjectArray CallbackHandlers::GetMethodOverrides(JEnv& env, const Local& implementationObject) { @@ -702,7 +664,7 @@ void CallbackHandlers::LogMethodCallback(const v8::FunctionCallbackInfo& arr) jclass CallbackHandlers::RUNTIME_CLASS = nullptr; jclass CallbackHandlers::JAVA_LANG_STRING = nullptr; +jfieldID CallbackHandlers::CURRENT_OBJECTID_FIELD_ID = nullptr; jmethodID CallbackHandlers::RESOLVE_CLASS_METHOD_ID = nullptr; -jmethodID CallbackHandlers::CREATE_INSTANCE_METHOD_ID = nullptr; -jmethodID CallbackHandlers::CACHE_CONSTRUCTOR_METHOD_ID = nullptr; +jmethodID CallbackHandlers::MAKE_INSTANCE_STRONG_ID = nullptr; jmethodID CallbackHandlers::GET_TYPE_METADATA = nullptr; jmethodID CallbackHandlers::ENABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; jmethodID CallbackHandlers::DISABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; diff --git a/src/jni/CallbackHandlers.h b/src/jni/CallbackHandlers.h index 0979c2661..aad9493e9 100644 --- a/src/jni/CallbackHandlers.h +++ b/src/jni/CallbackHandlers.h @@ -27,14 +27,13 @@ namespace tns static v8::Local CreateJSWrapper(v8::Isolate *isolate, jint javaObjectID, const std::string& typeName); - static jobject CreateJavaInstance(int objectID, const std::string& fullClassName, const ArgsWrapper& argWrapper, jclass javaClass, bool isInterface); - static bool RegisterInstance(v8::Isolate *isolate, const v8::Local& jsObject, const std::string& fullClassName, const ArgsWrapper& argWrapper, const v8::Local& implementationObject, bool isInterface); + static std::string ResolveConstructor(v8::Isolate *isolate, const ArgsWrapper& argWrapper, const std::string& fullClassName, jclass javaClass, bool isInterface); + static jclass ResolveClass(v8::Isolate *isolate, const std::string& fullClassname, const v8::Local& implementationObject); static std::string ResolveClassName(v8::Isolate *isolate, const std::string& fullClassname, const v8::Local& implementationObject); - // static v8::Local GetArrayElement(v8::Isolate *isolate, const v8::Local& array, uint32_t index, const std::string& arraySignature); @@ -101,9 +100,9 @@ namespace tns static jmethodID RESOLVE_CLASS_METHOD_ID; - static jmethodID CREATE_INSTANCE_METHOD_ID; + static jfieldID CURRENT_OBJECTID_FIELD_ID; - static jmethodID CACHE_CONSTRUCTOR_METHOD_ID; + static jmethodID MAKE_INSTANCE_STRONG_ID; static jmethodID GET_TYPE_METADATA; diff --git a/src/jni/JEnv.h b/src/jni/JEnv.h index 2ad90d7b5..12e258968 100644 --- a/src/jni/JEnv.h +++ b/src/jni/JEnv.h @@ -316,6 +316,13 @@ namespace tns } + jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue *args) + { + jobject jo = m_env->NewObjectA(clazz, methodID, args); + CheckForJavaException(); + return jo; + } + static void Init(JavaVM *jvm); private: diff --git a/src/jni/JniSignatureParser.cpp b/src/jni/JniSignatureParser.cpp index 1bc328b98..e21d88b33 100644 --- a/src/jni/JniSignatureParser.cpp +++ b/src/jni/JniSignatureParser.cpp @@ -6,8 +6,7 @@ using namespace std; using namespace tns; JniSignatureParser::JniSignatureParser(const string& signature) -: - m_signature(signature) +: m_signature(signature) { } @@ -21,16 +20,14 @@ vector JniSignatureParser::Parse() assert(endIdx != string::npos); - string params = m_signature.substr(startIdx + 1, endIdx - startIdx - 1); - - vector < string > tokens = ParseParams(startIdx + 1, endIdx); + vector tokens = ParseParams(startIdx + 1, endIdx); return tokens; } vector JniSignatureParser::ParseParams(int stardIdx, int endIdx) { - vector < string > tokens; + vector tokens; m_pos = stardIdx; @@ -56,13 +53,13 @@ string JniSignatureParser::ReadNextToken(int endIdx) switch (currChar) { case 'Z': - case 'B': - case 'C': - case 'S': - case 'I': - case 'J': - case 'F': - case 'D': + case 'B': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': ++m_pos; token.push_back(currChar); break; @@ -85,13 +82,13 @@ string JniSignatureParser::ReadNextToken(int endIdx) switch (currChar) { case 'Z': - case 'B': - case 'C': - case 'S': - case 'I': - case 'J': - case 'F': - case 'D': + case 'B': + case 'C': + case 'S': + case 'I': + case 'J': + case 'F': + case 'D': endFound = true; break; } diff --git a/src/jni/JsArgConverter.cpp b/src/jni/JsArgConverter.cpp index ccea8c121..f16b66b2b 100644 --- a/src/jni/JsArgConverter.cpp +++ b/src/jni/JsArgConverter.cpp @@ -18,7 +18,7 @@ using namespace std; using namespace tns; JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo& args, bool hasImplementationObject, const string& methodSignature, MetadataEntry *entry) - : m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()), m_tokens(nullptr) +: m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()), m_tokens(nullptr) { m_argsLen = !hasImplementationObject ? args.Length() : args.Length() - 1; @@ -52,6 +52,68 @@ JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo& args, bool } } +JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo& args, const string& methodSignature, const Local& outerThis) +: m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()), m_tokens(nullptr) +{ + auto isInnerClass = !outerThis.IsEmpty(); + if (isInnerClass) + { + m_argsLen = args.Length() + 1; + } + else + { + m_argsLen = args.Length(); + } + + JniSignatureParser parser(m_methodSignature); + m_tokens2 = parser.Parse(); + m_tokens = &m_tokens2; + + for (int i = 0; i < m_argsLen; i++) + { + if (isInnerClass) + { + if (i == 0) + { + m_isValid = ConvertArg(outerThis, i); + } + else + { + m_isValid = ConvertArg(args[i - 1], i); + } + } + else + { + m_isValid = ConvertArg(args[i], i); + } + + if (!m_isValid) + { + break; + } + } +} + +JsArgConverter::JsArgConverter(const v8::FunctionCallbackInfo& args, const string& methodSignature) +: m_isolate(args.GetIsolate()), m_env(JEnv()), m_methodSignature(methodSignature), m_isValid(true), m_error(Error()), m_tokens(nullptr) +{ + m_argsLen = args.Length(); + + JniSignatureParser parser(m_methodSignature); + m_tokens2 = parser.Parse(); + m_tokens = &m_tokens2; + + for (int i = 0; i < m_argsLen; i++) + { + m_isValid = ConvertArg(args[i], i); + + if (!m_isValid) + { + break; + } + } +} + bool JsArgConverter::ConvertArg(const Local& arg, int index) { bool success = false; @@ -204,14 +266,14 @@ bool JsArgConverter::ConvertArg(const Local& arg, int index) case CastType::None: obj = objectManager->GetJavaObjectByJsObject(jsObject); - + castValue = jsObject->GetHiddenValue(ConvertToV8String(V8StringConstants::NULL_NODE_NAME)); if(!castValue.IsEmpty()) { SetConvertedObject(index, nullptr); success = true; - return success; + break; } - + success = !obj.IsNull(); if (success) diff --git a/src/jni/JsArgConverter.h b/src/jni/JsArgConverter.h index bcd9fa2b8..a472d604c 100644 --- a/src/jni/JsArgConverter.h +++ b/src/jni/JsArgConverter.h @@ -14,6 +14,10 @@ namespace tns public: JsArgConverter(const v8::FunctionCallbackInfo& args, bool hasImplementationObject, const std::string& methodSignature, MetadataEntry *entry); + JsArgConverter(const v8::FunctionCallbackInfo& args, const std::string& methodSignature); + + JsArgConverter(const v8::FunctionCallbackInfo& args, const std::string& methodSignature, const v8::Local& outerThis); + ~JsArgConverter(); jvalue* ToArgs(); diff --git a/src/jni/JsArgToArrayConverter.cpp b/src/jni/JsArgToArrayConverter.cpp index a5b52975d..9ff60d065 100644 --- a/src/jni/JsArgToArrayConverter.cpp +++ b/src/jni/JsArgToArrayConverter.cpp @@ -19,6 +19,9 @@ using namespace v8; using namespace std; using namespace tns; +/* + * Converts a single JavaScript (V8) object to its respective Java representation + */ JsArgToArrayConverter::JsArgToArrayConverter(Isolate *isolate, const v8::Local& arg, bool isImplementationObject, int classReturnType) : m_isolate(isolate), m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), m_return_type(classReturnType) { @@ -32,6 +35,9 @@ JsArgToArrayConverter::JsArgToArrayConverter(Isolate *isolate, const v8::Local& args, bool hasImplementationObject, const Local& outerThis) : m_isolate(args.GetIsolate()), m_arr(nullptr), m_argsAsObject(nullptr), m_argsLen(0), m_isValid(false), m_error(Error()), m_return_type(static_cast(Type::Null)) { diff --git a/src/jni/MetadataNode.cpp b/src/jni/MetadataNode.cpp index 96238c925..f77f26cd8 100644 --- a/src/jni/MetadataNode.cpp +++ b/src/jni/MetadataNode.cpp @@ -25,7 +25,7 @@ void MetadataNode::Init(Isolate *isolate) } MetadataNode::MetadataNode(MetadataTreeNode *treeNode) : - m_treeNode(treeNode) + m_treeNode(treeNode) { uint8_t nodeType = s_metadataReader.GetNodeType(treeNode); @@ -487,9 +487,13 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc curPtr += sizeof(uint16_t); string lastMethodName; MethodCallbackData *callbackData = nullptr; + ConstructorCallbackData *ctorCallbackData = nullptr; + for (auto i = 0; i < instanceMethodCout; i++) { auto entry = s_metadataReader.ReadInstanceMethodEntry(&curPtr); + + // attach a function to the prototype of a javascript Object if (entry.name != lastMethodName) { callbackData = new MethodCallbackData(this); @@ -512,6 +516,20 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc prototypeTemplate->Set(funcName, func); lastMethodName = entry.name; } + + // Pete: leaving it here will get all constructors for a node + // Pete: !!!! Is this even necessary?! + if(entry.name.compare("") == 0) + { + if(ctorCallbackData == nullptr) + { + ctorCallbackData = new ConstructorCallbackData(this); + } + + ctorCallbackData->candidates.push_back(entry); + } + // Pete: send ctorCallbackData to all ConstructorCallback so that they can pass it along where necessary + callbackData->candidates.push_back(entry); } diff --git a/src/jni/MetadataNode.h b/src/jni/MetadataNode.h index 78b715cbb..88a910f07 100644 --- a/src/jni/MetadataNode.h +++ b/src/jni/MetadataNode.h @@ -192,6 +192,26 @@ namespace tns bool isSuper; }; + struct ConstructorCallbackData + { + ConstructorCallbackData() + : + node(nullptr), parent(nullptr), isSuper(false) + { + } + + ConstructorCallbackData(MetadataNode *_node) + : + node(_node), parent(nullptr), isSuper(false) + { + } + + std::vector candidates; + MetadataNode *node; + MethodCallbackData *parent; + bool isSuper; + }; + struct ExtendedClassData { ExtendedClassData(MetadataNode *_node, const std::string& _extendedName, const v8::Local& _implementationObject, std::string _fullClassName) diff --git a/src/jni/MethodCache.cpp b/src/jni/MethodCache.cpp index 99224110d..4d8cf8af7 100644 --- a/src/jni/MethodCache.cpp +++ b/src/jni/MethodCache.cpp @@ -25,6 +25,9 @@ void MethodCache::Init() RESOLVE_METHOD_OVERLOAD_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "resolveMethodOverload", "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;"); assert(RESOLVE_METHOD_OVERLOAD_METHOD_ID != nullptr); + + RESOLVE_CONSTRUCTOR_SIGNATURE_ID = env.GetMethodID(RUNTIME_CLASS, "resolveConstructorSignature", "(Ljava/lang/Class;[Ljava/lang/Object;)Ljava/lang/String;"); + assert(RESOLVE_CONSTRUCTOR_SIGNATURE_ID != nullptr); } MethodCache::CacheMethodInfo MethodCache::ResolveMethodSignature(const string& className, const string& methodName, const FunctionCallbackInfo& args, bool isStatic) @@ -53,8 +56,8 @@ MethodCache::CacheMethodInfo MethodCache::ResolveMethodSignature(const string& c mi.retType = MetadataReader::GetReturnType(mi.returnType); mi.isStatic = isStatic; mi.mid = isStatic - ? env.GetStaticMethodID(clazz, methodName, signature) - : + ? env.GetStaticMethodID(clazz, methodName, signature) + : env.GetMethodID(clazz, methodName, signature); s_cache.insert(make_pair(key, mi)); @@ -67,6 +70,41 @@ MethodCache::CacheMethodInfo MethodCache::ResolveMethodSignature(const string& c return mi; } + +MethodCache::CacheMethodInfo MethodCache::ResolveConstructorSignature(const ArgsWrapper& argWrapper, const string& fullClassName, jclass javaClass, bool isInterface) +{ + auto& args = argWrapper.args; + + CacheMethodInfo mi; + + auto key = EncodeSignature(fullClassName, "", args, false); + + auto it = s_cache.find(key); + + if (it == s_cache.end()) + { + auto signature = ResolveConstructor(args, javaClass, isInterface, argWrapper.outerThis); + + DEBUG_WRITE("ResolveConstructorSignature %s='%s'", key.c_str(), signature.c_str()); + + if (!signature.empty()) + { + JEnv env; + mi.clazz = javaClass; + mi.signature = signature; + mi.mid = env.GetMethodID(javaClass, "", signature); + + s_cache.insert(make_pair(key, mi)); + } + } + else + { + mi = (*it).second; + } + + return mi; +} + // Encoded signature .S/I....<...> string MethodCache::EncodeSignature(const string& className, const string& methodName, const FunctionCallbackInfo& args, bool isStatic) { @@ -230,6 +268,35 @@ string MethodCache::ResolveJavaMethod(const FunctionCallbackInfo& args, c return resolvedSignature; } +string MethodCache::ResolveConstructor(const FunctionCallbackInfo& args, jclass javaClass, bool isInterface, Local outerThis) +{ + JEnv env; + string resolvedSignature; + + JsArgToArrayConverter argConverter(args, isInterface, outerThis); + if (argConverter.IsValid()) + { + jobjectArray javaArgs = argConverter.ToJavaArray(); + + auto runtime = Runtime::GetRuntime(args.GetIsolate()); + + jstring signature = (jstring) env.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_CONSTRUCTOR_SIGNATURE_ID, javaClass, javaArgs); + + const char* str = env.GetStringUTFChars(signature, nullptr); + resolvedSignature = string(str); + env.ReleaseStringUTFChars(signature, str); + env.DeleteLocalRef(signature); + } + else + { + JsArgToArrayConverter::Error err = argConverter.GetError(); + throw NativeScriptException(err.msg); + } + + return resolvedSignature; +} + map MethodCache::s_cache; jclass MethodCache::RUNTIME_CLASS = nullptr; jmethodID MethodCache::RESOLVE_METHOD_OVERLOAD_METHOD_ID = nullptr; +jmethodID MethodCache::RESOLVE_CONSTRUCTOR_SIGNATURE_ID = nullptr; diff --git a/src/jni/MethodCache.h b/src/jni/MethodCache.h index eda02d788..ef477b669 100644 --- a/src/jni/MethodCache.h +++ b/src/jni/MethodCache.h @@ -6,6 +6,7 @@ #include "v8.h" #include "JEnv.h" #include "MetadataEntry.h" +#include "ArgsWrapper.h" namespace tns { @@ -32,6 +33,8 @@ namespace tns //static std::string ResolveMethodSignature(const std::string& className, const std::string& methodName, const v8::FunctionCallbackInfo& args); static CacheMethodInfo ResolveMethodSignature(const std::string& className, const std::string& methodName, const v8::FunctionCallbackInfo& args, bool isStatic); + static CacheMethodInfo ResolveConstructorSignature(const ArgsWrapper& argWrapper, const std::string& fullClassName, jclass javaClass, bool isInterface); + static std::string EncodeSignature(const std::string& className, const std::string& methodName, const v8::FunctionCallbackInfo& args, bool isStatic); private: @@ -43,10 +46,14 @@ namespace tns static std::string ResolveJavaMethod(const v8::FunctionCallbackInfo& args, const std::string& className, const std::string& methodName); + static std::string ResolveConstructor(const v8::FunctionCallbackInfo& args, jclass javaClass, bool isInterface, v8::Local outerThis); + static jclass RUNTIME_CLASS; static jmethodID RESOLVE_METHOD_OVERLOAD_METHOD_ID; + static jmethodID RESOLVE_CONSTRUCTOR_SIGNATURE_ID; + //static std::map s_cache; static std::map s_cache; }; diff --git a/src/src/com/tns/MethodResolver.java b/src/src/com/tns/MethodResolver.java index 5359dfb8f..7d1c5ea9a 100644 --- a/src/src/com/tns/MethodResolver.java +++ b/src/src/com/tns/MethodResolver.java @@ -175,6 +175,7 @@ static void tryFindMatches(String methodName, ArrayList> Class argClass = args[i] instanceof NullObject ? ((NullObject)args[i]).getNullObjectClass() : args[i].getClass(); + Tuple res = isAssignableFrom(params[i], argClass); success = res.x.booleanValue(); dist += res.y; @@ -199,6 +200,13 @@ static void tryFindMatches(String methodName, ArrayList> } } + static String resolveConstructorSignature(Class clazz, Object[] args) throws ClassNotFoundException, IOException + { + Constructor ctor = resolveConstructor(clazz, args); + + return ctor != null ? getMethodSignature(null, ctor.getParameterTypes()) : null; + } + static Constructor resolveConstructor(Class clazz, Object[] args) throws ClassNotFoundException, IOException { Constructor[] constructors = clazz.getConstructors(); @@ -239,7 +247,11 @@ static Constructor resolveConstructor(Class clazz, Object[] args) throws C { if (args[i] != null) { - Tuple res = isAssignableFrom(paramTypes[i], args[i].getClass()); + Class argClass = args[i] instanceof NullObject ? + ((NullObject)args[i]).getNullObjectClass() + : args[i].getClass(); + + Tuple res = isAssignableFrom(paramTypes[i], argClass); success = res.x.booleanValue(); dist += res.y; } @@ -270,6 +282,7 @@ static Constructor resolveConstructor(Class clazz, Object[] args) throws C Collections.sort(candidates, distanceComparator); Constructor selectedCtor = candidates.get(0).x; + // Pete: consider dropping this check in favor of performance boolean success = convertConstructorArgs(selectedCtor, args); return success ? selectedCtor : null; @@ -500,11 +513,11 @@ public static boolean convertConstructorArgs(Constructor ctor, Object[] args) for (int i = 0; i < paramTypes.length; i++) { - Class cuurParamType = paramTypes[i]; + Class currParamType = paramTypes[i]; - if (cuurParamType.isPrimitive()) + if (currParamType.isPrimitive()) { - success = convertPrimitiveArg(cuurParamType, args, i); + success = convertPrimitiveArg(currParamType, args, i); } if (!success) diff --git a/src/src/com/tns/Runtime.java b/src/src/com/tns/Runtime.java index 6f4cb0943..b930863d2 100644 --- a/src/src/com/tns/Runtime.java +++ b/src/src/com/tns/Runtime.java @@ -309,67 +309,6 @@ private Class resolveClass(String fullClassName, String[] methodOverrides) th return javaClass; } - @RuntimeCallable - private int cacheConstructor(Class clazz, Object[] args) throws ClassNotFoundException, IOException - { - Constructor ctor = MethodResolver.resolveConstructor(clazz, args); - - // TODO: Lubo: Not thread safe already. - // TODO: Lubo: Does not check for existing items - int ctorId = ctorCache.size(); - - ctorCache.add(ctor); - - return ctorId; - } - - @RuntimeCallable - private Object createInstance(Object[] args, int objectId, int constructorId) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException, IOException - { - Constructor ctor = ctorCache.get(constructorId); - boolean success = MethodResolver.convertConstructorArgs(ctor, args); - - if (!success) - { - StringBuilder builder = new StringBuilder(); - builder.append(constructorId + "("); - if (args != null) - { - for (Object arg : args) - { - if (arg != null) - { - builder.append(arg.toString() + ", "); - } - else - { - builder.append("null, "); - } - } - } - builder.append(")"); - - throw new InstantiationException("MethodResolver didn't resolve any constructor with the specified arguments " + builder.toString()); - } - - Object instance; - try - { - Runtime.currentObjectId = objectId; - instance = ctor.newInstance(args); - - makeInstanceStrong(instance, objectId); - } - finally - { - Runtime.currentObjectId = -1; - } - - adjustAmountOfExternalAllocatedMemory(); - - return instance; - } - @RuntimeCallable private long getChangeInBytesOfUsedMemory() { @@ -836,6 +775,24 @@ static Class getClassForName(String className) throws ClassNotFoundException return clazz; } + @RuntimeCallable + private String resolveConstructorSignature(Class clazz, Object[] args) throws Exception + { + // Pete: cache stuff here, or in the cpp part + + if (logger.isEnabled()) + logger.write("resolveConstructorSignature: Resolving constructor for class " + clazz.getName()); + + String res = MethodResolver.resolveConstructorSignature(clazz, args); + + if (res == null) + { + throw new Exception("Failed resolving constructor on class " + clazz.getName()); + } + + return res; + } + @RuntimeCallable private String resolveMethodOverload(String className, String methodName, Object[] args) throws Exception { diff --git a/test-app/assets/app/MyActivity.js b/test-app/assets/app/MyActivity.js index 8f881a930..04f498c3c 100644 --- a/test-app/assets/app/MyActivity.js +++ b/test-app/assets/app/MyActivity.js @@ -37,17 +37,24 @@ var MyActivity = (function (_super) { this.setContentView(layout); var textView = new android.widget.TextView(this); - textView.setText("Hit that sucker"); + textView.setText("It's a button!"); layout.addView(textView); var button = new android.widget.Button(this); button.setText("Hit me"); layout.addView(button); var counter = 0; + + var Color = android.graphics.Color; + var colors = [Color.BLUE, Color.RED, Color.MAGENTA, Color.YELLOW, Color.parseColor("#FF7F50")]; + var taps = 0; + + var dum = com.tns.tests.DummyClass.null; + button.setOnClickListener(new android.view.View.OnClickListener("AppClickListener", { onClick: function() { - __log("onClick called"); - button.setText("Hit that sucker one more time " + ++counter); + button.setBackgroundColor(colors[taps % colors.length]); + taps++; }})); }; MyActivity = __decorate([ diff --git a/test-app/assets/app/tests/tests.js b/test-app/assets/app/tests/tests.js index 4f855d983..8518db3d5 100644 --- a/test-app/assets/app/tests/tests.js +++ b/test-app/assets/app/tests/tests.js @@ -1205,7 +1205,7 @@ describe("Tests ", function () { try { - var d = new com.tns.tests.DummyClass(new java.lang.Object()); + var d = new com.tns.tests.DummyClass(new java.io.File()); } catch (e) { From b322ff52ec83a5084d490d925f1d95b5be9258d9 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Tue, 12 Apr 2016 13:37:01 +0300 Subject: [PATCH 3/5] remove redundant code in MetadataNode.cpp --- src/jni/MetadataNode.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/jni/MetadataNode.cpp b/src/jni/MetadataNode.cpp index f77f26cd8..8cf266fa3 100644 --- a/src/jni/MetadataNode.cpp +++ b/src/jni/MetadataNode.cpp @@ -487,7 +487,6 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc curPtr += sizeof(uint16_t); string lastMethodName; MethodCallbackData *callbackData = nullptr; - ConstructorCallbackData *ctorCallbackData = nullptr; for (auto i = 0; i < instanceMethodCout; i++) { @@ -517,19 +516,6 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc lastMethodName = entry.name; } - // Pete: leaving it here will get all constructors for a node - // Pete: !!!! Is this even necessary?! - if(entry.name.compare("") == 0) - { - if(ctorCallbackData == nullptr) - { - ctorCallbackData = new ConstructorCallbackData(this); - } - - ctorCallbackData->candidates.push_back(entry); - } - // Pete: send ctorCallbackData to all ConstructorCallback so that they can pass it along where necessary - callbackData->candidates.push_back(entry); } @@ -571,6 +557,7 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc callbackData->candidates.push_back(entry); } + //attach .extend function auto extendFuncName = V8StringConstants::GetExtend(); auto extendFuncTemplate = FunctionTemplate::New(isolate, ExtendCallMethodCallback, External::New(isolate, this)); ctorFunction->Set(extendFuncName, extendFuncTemplate->GetFunction()); From be2a487297d7dd02acb7921c59445b563082ddd3 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Wed, 13 Apr 2016 10:09:58 +0300 Subject: [PATCH 4/5] remove redundant ctor argument conversion in Java --- src/src/com/tns/MethodResolver.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/src/com/tns/MethodResolver.java b/src/src/com/tns/MethodResolver.java index 7d1c5ea9a..eccbc8dc1 100644 --- a/src/src/com/tns/MethodResolver.java +++ b/src/src/com/tns/MethodResolver.java @@ -282,10 +282,7 @@ static Constructor resolveConstructor(Class clazz, Object[] args) throws C Collections.sort(candidates, distanceComparator); Constructor selectedCtor = candidates.get(0).x; - // Pete: consider dropping this check in favor of performance - boolean success = convertConstructorArgs(selectedCtor, args); - - return success ? selectedCtor : null; + return selectedCtor; } return null; From 7c4f2d4ee10d6ba39d6b3e499f4519a159f85a2c Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Wed, 13 Apr 2016 15:43:47 +0300 Subject: [PATCH 5/5] fix runtime currentObjectId scope to properly reset objectId --- src/jni/CallbackHandlers.cpp | 38 +- src/jni/CallbackHandlers.h | 19 + src/src/com/tns/Runtime.java | 4 +- test-app/assets/app/tests/tests.js | 1121 ++++++++++++++-------------- 4 files changed, 613 insertions(+), 569 deletions(-) diff --git a/src/jni/CallbackHandlers.cpp b/src/jni/CallbackHandlers.cpp index 5cbce3dd2..47cd36236 100644 --- a/src/jni/CallbackHandlers.cpp +++ b/src/jni/CallbackHandlers.cpp @@ -39,7 +39,7 @@ void CallbackHandlers::Init(Isolate *isolate, ObjectManager *objectManager) RESOLVE_CLASS_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "resolveClass", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Class;"); assert(RESOLVE_CLASS_METHOD_ID != nullptr); - CURRENT_OBJECTID_FIELD_ID = env.GetStaticFieldID(RUNTIME_CLASS, "currentObjectId", "I"); + CURRENT_OBJECTID_FIELD_ID = env.GetFieldID(RUNTIME_CLASS, "currentObjectId", "I"); assert(CURRENT_OBJECTID_FIELD_ID != nullptr); MAKE_INSTANCE_STRONG_ID = env.GetMethodID(RUNTIME_CLASS, "makeInstanceStrong", "(Ljava/lang/Object;I)V"); @@ -85,25 +85,25 @@ bool CallbackHandlers::RegisterInstance(Isolate *isolate, const Local& j jobject instance; - env.SetStaticIntField(RUNTIME_CLASS, CURRENT_OBJECTID_FIELD_ID, javaObjectID); - - if(argWrapper.type == ArgType::Interface) - { - instance = env.NewObject(generatedJavaClass, mi.mid); - } - else { - // resolve arguments before passing them on to the constructor - JsArgConverter argConverter(argWrapper.args, mi.signature, argWrapper.outerThis); - auto ctorArgs = argConverter.ToArgs(); + JavaObjectIdScope objIdScope(env, CURRENT_OBJECTID_FIELD_ID, runtime->GetJavaRuntime(), javaObjectID); - instance = env.NewObjectA(generatedJavaClass, mi.mid, ctorArgs); + if(argWrapper.type == ArgType::Interface) + { + instance = env.NewObject(generatedJavaClass, mi.mid); + } + else + { + // resolve arguments before passing them on to the constructor + JsArgConverter argConverter(argWrapper.args, mi.signature, argWrapper.outerThis); + auto ctorArgs = argConverter.ToArgs(); + + instance = env.NewObjectA(generatedJavaClass, mi.mid, ctorArgs); + } } env.CallVoidMethod(runtime->GetJavaRuntime(), MAKE_INSTANCE_STRONG_ID, instance, javaObjectID); - env.SetStaticIntField(RUNTIME_CLASS, CURRENT_OBJECTID_FIELD_ID, -1); - AdjustAmountOfExternalAllocatedMemory(env, isolate); JniLocalRef localInstance(instance); @@ -137,14 +137,14 @@ jclass CallbackHandlers::ResolveClass(Isolate *isolate, const string& fullClassn { JEnv env; - //get needed arguments in order to load binding + // get needed arguments in order to load binding JniLocalRef javaFullClassName(env.NewStringUTF(fullClassname.c_str())); jobjectArray methodOverrides = GetMethodOverrides(env, implementationObject); auto runtime = Runtime::GetRuntime(isolate); - //create or load generated binding (java class) + // create or load generated binding (java class) JniLocalRef generatedClass(env.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_CLASS_METHOD_ID, (jstring) javaFullClassName, methodOverrides)); globalRefToGeneratedClass = static_cast(env.NewGlobalRef(generatedClass)); @@ -593,9 +593,9 @@ int64_t CallbackHandlers::AdjustAmountOfExternalAllocatedMemory(JEnv& env, Isola int64_t changeInBytes = env.CallLongMethod(runtime->GetJavaRuntime(), GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID); int64_t adjustedValue = (changeInBytes != 0) - ? isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes) - : - 0; + ? isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes) + : + 0; return adjustedValue; } diff --git a/src/jni/CallbackHandlers.h b/src/jni/CallbackHandlers.h index aad9493e9..534f04dfb 100644 --- a/src/jni/CallbackHandlers.h +++ b/src/jni/CallbackHandlers.h @@ -123,6 +123,25 @@ namespace tns static std::map s_constructorCache; static std::map s_classCache; + + struct JavaObjectIdScope + { + JavaObjectIdScope(JEnv& env, jfieldID fieldId, jobject runtime, int javaObjectId) + : _env(env), _fieldID(fieldId), _runtime(runtime) + { + _env.SetIntField(_runtime, _fieldID, javaObjectId); + } + + ~JavaObjectIdScope() + { + _env.SetIntField(_runtime, _fieldID, -1); + } + + private: + JEnv _env; + jfieldID _fieldID; + jobject _runtime; + }; }; } diff --git a/src/src/com/tns/Runtime.java b/src/src/com/tns/Runtime.java index b930863d2..c526ce6c8 100644 --- a/src/src/com/tns/Runtime.java +++ b/src/src/com/tns/Runtime.java @@ -65,7 +65,7 @@ void passUncaughtExceptionToJs(Throwable ex, String stackTrace) private final java.lang.Runtime dalvikRuntime = java.lang.Runtime.getRuntime(); private final Object keyNotFoundObject = new Object(); - private static int currentObjectId = -1; + private int currentObjectId = -1; private ExtractPolicy extractPolicy; @@ -328,7 +328,7 @@ public static void initInstance(Object instance) { Runtime runtime = Runtime.getCurrentRuntime(); - int objectId = Runtime.currentObjectId; + int objectId = runtime.currentObjectId; if (objectId != -1) { diff --git a/test-app/assets/app/tests/tests.js b/test-app/assets/app/tests/tests.js index 8518db3d5..7768d4fba 100644 --- a/test-app/assets/app/tests/tests.js +++ b/test-app/assets/app/tests/tests.js @@ -1,147 +1,147 @@ describe("Tests ", function () { var objectToString = function(o){ - var str=''; + var str=''; - for(var p in o){ - if(typeof o[p] == 'string'){ - str+= p + ': ' + o[p]+';'; - }else{ - str+= p + ': { ' + objectToString(o[p]) + ' } '; - } - } + for(var p in o){ + if(typeof o[p] == 'string'){ + str+= p + ': ' + o[p]+';'; + }else{ + str+= p + ': { ' + objectToString(o[p]) + ' } '; + } + } - return str; + return str; }; - + var myCustomEquality = function(first, second) { return first == second; }; - + beforeEach(function() { jasmine.addCustomEqualityTester(myCustomEquality); }); - + it("When_extending_a_class_two_times", function () { - + __log("TEST: When_extending_a_class_two_times"); - + __log("TEST: Creating MyButton"); var MyButton = com.tns.tests.Button1.extend("MyButton", { toString : function() { - return "button1"; + return "button1"; } }); - + __log("TEST: Calling MyButton ctor"); var button1 = new MyButton(); __log("TEST: Calling button1 toString"); var button1Label = button1.toString(); button1.setLabel("first button"); - + __log("TEST: Creating MyButton2 class"); var MyButton2 = com.tns.tests.Button1.extend("MyButton", { toString : function() { - return "button2"; - }}); - + return "button2"; + }}); + var button2 = new MyButton2(); button2.setLabel("second button"); var button2Label = button2.toString(); - + __log("but1=" + button1Label + ", but2=" + button2Label); - + var shouldBeTrue = (button1 != button2 && button1Label == "button1" && button2Label == "button2"); - + expect(shouldBeTrue).toBe(true); - + var button1LabelAfterButton2Created = button1.toString(); shouldBeTrue = (button1 != button2 && button1LabelAfterButton2Created == "button1" && button2Label == "button2"); - + expect(shouldBeTrue).toBe(true); }); - + it("When_extending_a_class_two_times_with_no_extend_names", function () { - + __log("TEST: When_extending_a_class_two_times_with_no_extend_names"); - + __log("TEST: Creating MyButton"); var MyButton = com.tns.tests.Button1.extend({ toString : function() { - return "button1"; + return "button1"; } }); - + __log("TEST: Calling MyButton ctor"); var button1 = new MyButton(); __log("TEST: Calling button1 toString"); var button1Label = button1.toString(); button1.setLabel("first button"); - + __log("TEST: Creating MyButton2 class"); var MyButton2 = com.tns.tests.Button1.extend({ toString : function() { - return "button2"; - }}); - + return "button2"; + }}); + var button2 = new MyButton2(); button2.setLabel("second button"); var button2Label = button2.toString(); - + __log("but1=" + button1Label + ", but2=" + button2Label); - + var shouldBeTrue = (button1 != button2 && button1Label == "button1" && button2Label == "button2"); - + expect(shouldBeTrue).toBe(true); - + var button1LabelAfterButton2Created = button1.toString(); shouldBeTrue = (button1 != button2 && button1LabelAfterButton2Created == "button1" && button2Label == "button2"); - + expect(shouldBeTrue).toBe(true); }); - - + + it("When_extending_a_class_with_method_overloads_by_argument_type", function () { - + __log("TEST: Creating MyButton"); var MyButton = com.tns.tests.Button1.extend({ method2 : function(arg1) { - return arg1.toString(); + return arg1.toString(); } }); - + var button1 = new MyButton(); - + var callWithInt = button1.getClass().getMethod("callMethod2WithInt", []); var callWithByte = button1.getClass().getMethod("callMethod2WithByte", []); - - - + + + __log("TEST: Calling MyButton method2 with int"); var intResult = callWithInt.invoke(button1, []); expect(intResult).toBe("1"); //var intResult = button1.callMethod2WithInt(); __log("TEST: intResult = " + intResult); - + __log("TEST: Calling MyButton method2 with byte"); //var byteResult = button1.callMethod2WithByte(); var byteResult = callWithByte.invoke(button1, []); __log("TEST: byteResult = " + byteResult); - + expect(byteResult).toBe("5"); }); - - + + it("When_implementing_an_interface_with_new_the_overrides_should_work", function () { - + __log("TEST: When_implementing_an_interface_with_new__the_overrides_should_work"); - + var MyButton = com.tns.tests.Button1.extend("MyButton60", { toString : function() { - return "button1"; + return "button1"; } }); - + var button1 = new MyButton(); var buttonClicked = false; button1.setOnClickListener(new android.view.View.OnClickListener("MyClickListener", { @@ -150,128 +150,128 @@ describe("Tests ", function () { } })); button1.click(null); - + expect(buttonClicked).toEqual(true); }); - + it("When_calling_instanceof_on_field_result_it_should_work", function () { - + __log("TEST: When_calling_instanceof_on_field_result_it_should_work"); - + var MyButton = com.tns.tests.Button1.extend("MyButton81", { toString : function() { - return "button1"; + return "button1"; }, }); - + var button1 = new MyButton(); var dummyObject = button1.DummyClassAsObjectField; - + var isInstanceOf = dummyObject instanceof com.tns.tests.DummyClass; - + expect(isInstanceOf).toEqual(true); }); - + it("When_calling_instanceof_on_method_result_it_should_work", function () { - + __log("TEST: When_calling_instanceof_on_method_result_it_should_work"); - + var MyButton = com.tns.tests.Button1.extend("MyButton98", { toString : function() { - return "button1"; + return "button1"; }, }); - + var button1 = new MyButton(); var dummy = button1.getDummy(); - + var isInstanceOf = dummy instanceof com.tns.tests.DummyClass; - + expect(isInstanceOf).toEqual(true); }); - + it("When_calling_instanceof_on_method_argument_it_should_work", function () { - + __log("TEST: When_calling_instanceof_on_method_argument_it_should_work"); - + var isInstanceOf; - + var MyButton = com.tns.tests.Button1.extend("MyButton115", { toString : function() { - return "button1"; + return "button1"; }, methodDummyClassAsObjectInArgs: function(object) { isInstanceOf = object instanceof com.tns.tests.DummyClass; } }); - + var button1 = new MyButton(); button1.callMethodDummyClassAsObjectInArgs(); - + expect(isInstanceOf).toEqual(true); }); - + //originally wasn't run it("When_calling_instanceof_on_interface_it_should_work", function () { - + __log("TEST: When_calling_instanceof_on_interface_it_should_work"); - + var interfaceInstance = new android.view.View.OnClickListener("ClickListener", { onClick : function() { buttonClicked = true; } }); - + var secondInterfaceInstance = new android.view.View.OnClickListener("ClickListener", { onClick : function() { buttonClicked = true; } }); - + var thirdInterfaceInstance = new android.view.View.OnClickListener("ClickListener", { onClick : function() { buttonClicked = true; } }); - + //__log("Object get PrototypeOf" + Object.getPrototypeOf(interfaceInstance).toString()); //__log("Object get PrototypeOf" + Object.getPrototypeOf(secondInterfaceInstance).toString()); - + var isInstanceOfOnClickListener = interfaceInstance instanceof android.view.View.OnClickListener; var secondIsInstanceOfOnClickListener = secondInterfaceInstance instanceof android.view.View.OnClickListener; var thirdIsInstanceOfOnClickListener = thirdInterfaceInstance instanceof android.view.View.OnClickListener; - + expect(isInstanceOfOnClickListener).toEqual(true); expect(secondIsInstanceOfOnClickListener).toEqual(true); expect(thirdIsInstanceOfOnClickListener).toEqual(true); }); - + it("When_calling_instanceof_it_should_work", function () { - + __log("TEST: When_calling_instanceof_it_should_work"); - + var MyButton = com.tns.tests.Button1.extend("MyButton148", { toString : function() { - return "button1"; + return "button1"; } }); var button1 = new MyButton(); - + var isInstanceOfMyButton = button1 instanceof MyButton; var isInstanceOfButton1 = button1 instanceof com.tns.tests.Button1; - + expect(isInstanceOfMyButton).toEqual(true); expect(isInstanceOfButton1).toBe(true); }); - + it("When_calling_instance_and_static_member_with_same_name_the_calls_should_succeed", function () { - + __log("TEST: When_calling_instance_and_static_member_with_same_name_the_calls_should_succeed"); var MyButton = com.tns.tests.Button1.extend("MyButton213", { toString : function() { - return "button1"; + return "button1"; } }); @@ -286,83 +286,83 @@ describe("Tests ", function () { catch(e) { exceptionCaught = true; } - + expect(exceptionCaught).toBe(false); }); - + it("When_calling_toString_on_an_java_object_it_should_call_the_java_method", function () { - + __log("TEST: When_calling_toString_on_an_java_object_it_should_call_the_java_method"); var instance = new com.tns.tests.DummyClass(); var s = instance.toString(); - + expect(s.indexOf("com.tns.tests.DummyClass")).not.toEqual(-1); }); - + it("When_calling_toString_on_an_java_object_that_has_overriden_toString_in_js_it_should_call_the_js_method", function () { - + __log("TEST: When_calling_toString_on_an_java_object_that_has_overriden_toString_in_js_it_should_call_the_js_method"); var MyButton = com.tns.tests.Button1.extend("MyButton240", { toString : function() { - return "button1"; + return "button1"; } }); - + var instance = new MyButton(); var s = instance.toString(); - + expect(s).toBe("button1"); }); - + it("When_extending_a_class_two_times_without_second_implementation_object", function () { - + __log("TEST: When_extending_a_class_two_times_without_second_implementation_object"); - + var MyButton = com.tns.tests.Button1.extend("MyButton257", { toString : function() { - return "button1"; + return "button1"; } }); - + var button1 = new MyButton(); var button1Label = button1.toString(); - + var button2 = new com.tns.tests.Button1(); var button2Label = button2.toString(); - + __log("button1Label=" + button1Label + ", button2Label=" + button2Label); var shouldBeTrue = (button1 !== button2 && button1Label !== button2Label); - + expect(shouldBeTrue).toBe(true); - + var button1PostButton2CreationLabel = button1.toString(); - + expect(button1Label).toBe(button1PostButton2CreationLabel); }); - + it("When__calling_super_method_using_the_prototype_property_of_a_function_it_should_call_the_super_method", function () { - + __log("TEST: When__calling_super_method_using_the_prototype_property_of_a_function_it_should_call_the_super_method"); var button1 = new com.tns.tests.Button1(); var prop = com.tns.tests.Button1.prototype.getIMAGE_ID_PROP.call(button1); - + expect(prop).toBe("image id prop"); }); - + it("When__calling_super_method_using_the_prototype_property_of_a_extended_function_it_should_call_the_super_method", function () { - + __log("TEST: When__calling_super_method_using_the_prototype_property_of_a_extended_function_it_should_call_the_super_method"); var MyButton = com.tns.tests.Button1.extend("MyButton289", {}); var button1 = new MyButton(); var prop = com.tns.tests.Button1.prototype.getIMAGE_ID_PROP.call(button1); - + expect(prop).toBe("image id prop"); }); - + it("When__calling_super_method_using_the_prototype_property_of_a_extended_function_it_should_call_the_super_method2", function () { - + __log("TEST: When__calling_super_method_using_the_prototype_property_of_a_extended_function_it_should_call_the_super_method2"); var MyButton = com.tns.tests.Button1.extend("MyButton294", { @@ -370,95 +370,95 @@ describe("Tests ", function () { }); var button1 = new MyButton(); var prop = com.tns.tests.Button1.prototype.getIMAGE_ID_PROP.call(button1); - + expect(prop).toBe("image id prop"); }); - + it("When_extending_a_class_and_calling_super_toString", function () { - + //__log("//TODO: NOT WORKING: super method calls are not working correctly. Tests fails with FAILED: When_extending_a_class_and_calling_super_toString. Actual: com.tns.com.tns.tests.Button1-MyButton305@52854640 Expected: com.tns.tests.Button1@"); //return; - + __log("TEST: When_extending_a_class_and_calling_super_toString"); - + var MyButton = com.tns.tests.Button1.extend("MyButton", { toString : function() { - return this.super.toString() + this.super.echo("success"); + return this.super.toString() + this.super.echo("success"); }, - + echo : function(s) { - return "fail"; + return "fail"; } }); - + var button1 = new MyButton(); var button1Label = button1.toString(); - + expect(button1Label.indexOf("com.tns.tests.Button1_")).not.toEqual(-1); expect(button1Label.indexOf("MyButton")).not.toEqual(-1); expect(button1Label.indexOf("success")).not.toEqual(-1); - + }); - + it("When_extending_a_class_and_calling_super_method_it_should_work", function () { - + __log("TEST: When_extending_a_class_and_calling_super_method_it_should_work"); var MyButton = com.tns.tests.Button1.extend("MyButton318", { toString : function() { - return "toString overriden"; + return "toString overriden"; }, - + getIMAGE_ID_PROP : function() { return this.super.getIMAGE_ID_PROP() + "!"; } }); var button1 = new MyButton(); var button1SuperToString = button1.toString(); - + expect(button1SuperToString).toBe("toString overriden"); - + var IMAGE_ID_PROP_Result = button1.getIMAGE_ID_PROP(); - + expect(IMAGE_ID_PROP_Result).toBe("image id prop!"); }); - + it("When_accessing_static_members_on_an_extended_class", function () { - + __log("TEST: When_accessing_static_members_on_an_extended_class"); - + var MyButton = com.tns.tests.Button1.extend("MyButton341", { hashCode : function() { - return 5454; + return 5454; } }); - + var MyButton2 = com.tns.tests.Button1.extend("MyButton347", { hashCode : function() { - return 1212; + return 1212; } }); - + var setValue = 4; MyButton.setMyStaticIntField(setValue); var readValue = MyButton2.getMyStaticIntField(); - + expect(readValue).toEqual(setValue); - + var readValue = com.tns.tests.Button1.getMyStaticIntField(); - + expect(readValue).toEqual(setValue); }); - + it("When_implementing_an_interface_with_new__the_overrides_should_work", function () { - + __log("TEST: When_implementing_an_interface_with_new__the_overrides_should_work"); - + var MyButton = com.tns.tests.Button1.extend({ toString : function() { - return "button1"; + return "button1"; } }); - + var button1 = new MyButton(); var buttonClicked = false; @@ -472,20 +472,20 @@ describe("Tests ", function () { expect(buttonClicked).toEqual(true); }); - + it("When_a_java_method_returns_object_that_needs_js_instance__it_should_create_the_instance", function () { - + __log("TEST: When_a_java_method_returns_object_that_needs_js_instance__it_should_create_the_instance"); - + var MyButton = com.tns.tests.Button1.extend("MyButton381", { toString : function() { - return "button1"; + return "button1"; } }); - + var button1 = new MyButton(); var dummy = button1.getDummy(); - + var exceptionCaught = false; try { var res = dummy.dummyMethod(123); //this will fail if button2 is not valid proxy object and properly exposed to js @@ -493,166 +493,166 @@ describe("Tests ", function () { catch (e) { exceptionCaught = true; } - + expect(exceptionCaught).toBe(false); }); - + it("When_a_java_method_returns_object_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type", function () { __log("TEST: When_a_java_method_returns_object_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type"); - + var Button = com.tns.tests.Button1.extend("MyButton397", { toString : function() { - return "button1"; + return "button1"; } }); - + var button = new Button(); var object = button.getDummyClassAsObject(); var name = object.getName(); - + expect(name).toEqual("dummy"); }); - + it("When_a_java_field_returns_object_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type", function () { - + __log("TEST: When_a_java_field_returns_object_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type"); - + var Button = com.tns.tests.Button1.extend("MyButton413", { toString : function() { - return "button1"; + return "button1"; } }); - + var button = new Button(); var object = button.DummyClassAsObjectField; var name = object.getName(); - + expect(name).toEqual("dummy"); }); - + it("When_a_java_argument_is_passed_to_js_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type", function () { - + __log("TEST: When_a_java_argument_is_passed_to_js_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type"); - + var name = ""; var Button = com.tns.tests.Button1.extend("MyButton418", { toString : function() { - return "button1"; + return "button1"; }, - + methodDummyClassAsObjectInArgs: function(object) { name = object.getName(); __log("The actual name is " + name); } }); - + var button = new Button(); var object = button.callMethodDummyClassAsObjectInArgs(); - + expect(name).toEqual("dummy"); }); - + it("When_a_java_object_is_returned_from_indexer_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type", function () { - + __log("TEST: When_a_java_object_is_returned_from_indexer_that_needs_js_instance__it_should_create_the_instance_according_to_the_actual_return_type"); - + var Button = com.tns.tests.Button1.extend("MyButton450", { toString : function() { - return "button1"; + return "button1"; } }); - + var button = new Button(); var arrayOfObjects = button.getDummyClassAsObjectArray(); var name = arrayOfObjects[0].getName(); - + expect(name).toEqual("dummy"); }); - + it("When_accessing_a_static_field_on_a_javascript_instance_it_should_work", function () { - + __log("TEST: When_accessing_a_static_field_on_a_javascript_instance_it_should_work"); - + var MyButton = com.tns.tests.Button1.extend("MyButton455", { hashCode : function() { - return 5454; + return 5454; }, - + toString : function() { - return "button1"; + return "button1"; }, - + equals : function() { return true; } }); - + var valueUsingChild = MyButton.STATIC_IMAGE_ID; expect(valueUsingChild).toEqual("static image id"); - + var valueUsingParent = com.tns.tests.Button1.STATIC_IMAGE_ID; - + expect(valueUsingParent).toEqual("static image id"); }); - + it("TestRequireDirName", function () { - + __log("TEST: TestRequireDirName"); - + var dir = __dirname; - + var expectedDirname = "/data/data/com.tns.android_runtime_testapp/files/app/tests"; - + expect(dir).toBe(expectedDirname); }); - + it("TestRequireFileName", function () { - + __log("TEST: TestRequireFileName"); - + var file = __filename; - + var expectedFilename = "/data/data/com.tns.android_runtime_testapp/files/app/tests/tests.js"; - + expect(file).toBe(expectedFilename); - + var file2 = module.filename; expect(file).toBe(file2); }); - + it("TestWorkingWithJavaArrayDoesNotMakeMemoryLeak", function () { - + __log("TEST: TestWorkingWithJavaArrayDoesNotMakeMemoryLeak"); - + var size = 10 * 1024 * 1024; - + for (var i = 0; i < 100; i++) { - + var arr = java.lang.reflect.Array.newInstance(java.lang.Byte.class.getField("TYPE").get(null), size); - + var length = arr.length; - + expect(length).toEqual(size); - + arr[0] = 123; - + var el = arr[0]; - + expect(el).toEqual(123); - + gc(); java.lang.System.gc(); } }); - + it("TestConstructorOverride", function () { - + __log("TEST: TestConstructorOverride"); - + var ctorCalled = false; var isConstructor = false; @@ -663,20 +663,20 @@ describe("Tests ", function () { }, toString : function() { - return "button1"; + return "button1"; } }); - + var btn = new MyButton(); - + expect(ctorCalled).toEqual(true); expect(isConstructor).toEqual(true); }); - + it("TestConstructorOverrideOnTypeWithInitMethod", function () { __log("TEST: TestConstructorOverrideOnTypeWithInitMethod"); - + var isCalled = false; var isConstructor = false; @@ -686,13 +686,13 @@ describe("Tests ", function () { isConstructor = arguments[arguments.length - 1]; } }); - + __log("TEST: TestConstructorOverrideOnTypeWithInitMethod: calling overriden ctor"); var dummy = new MyDummyClassWithInit(); - + expect(isCalled).toEqual(true); expect(isConstructor).toEqual(true); - + __log("TEST: TestConstructorOverrideOnTypeWithInitMethod: calling ctor as regular method"); isCalled = undefined; isConstructor = undefined; @@ -700,17 +700,17 @@ describe("Tests ", function () { expect(isCalled).toEqual(true); expect(isConstructor).toEqual(false); - + }); - + it("TestCreationOfLocationListener", function () { - + __log("TEST: TestCreationOfLocationListener"); - + var onLocationChangedCalled = false; var onProviderDisabledCalled = false; var onProviderEnabledCalled = false; - + var listener = new android.location.LocationListener("LocationListener",{ onLocationChanged: function(location) { onLocationChangedCalled = true; @@ -722,310 +722,310 @@ describe("Tests ", function () { onProviderEnabledCalled = true; } }); - + listener.onLocationChanged(null); - + expect(onLocationChangedCalled).toEqual(true); - + listener.onProviderDisabled(""); - + expect(onProviderDisabledCalled).toEqual(true); - + listener.onProviderEnabled(""); - + expect(onProviderEnabledCalled).toEqual(true); }); - + it("TestInnerClassCreation", function () { - + __log("TEST: TestInnerClassCreation"); - + var MyButton = com.tns.tests.Button1.extend("MyButton726", { toString : function() { - return "button1" - }}); - + return "button1" + }}); + var button1 = new MyButton(); - + var innerButton = new button1.InnerButton(); - + var s = innerButton.getSomeString(); - + expect(s.length).toBeGreaterThan(0); - + var innerButton2 = new new button1.InnerButton().InnerClass2(123) - + var s1 = innerButton2.getSomeString2(); - + expect(s1.length).toBeGreaterThan(0); }); - + it("TestNestedClassCreation", function () { - + __log("TEST: TestNestedClassCreation"); var i = 123; - + var nested = new com.tns.tests.Button1.InnerStaticClass(i); - + var actual_i = nested.getInt(); - + expect(actual_i).toEqual(i); }); - + it("TestCallMethodOnAnObjectReturnedAsObjectWithoutMetadata", function () { - + __log("TEST: TestCallMethodOnAnObjectReturnedAsObjectWithoutMetadata"); - + var dummy = new com.tns.tests.DummyClass(); - + var dummy2 = dummy.getDummyClassAsObject(); - + var name = dummy2.getName(); - + expect(name).toEqual("dummy"); }); - + it("TestGetFieldOnAnObjectReturnedAsObjectWithoutMetadata", function () { - + __log("TEST: TestGetFieldOnAnObjectReturnedAsObjectWithoutMetadata"); - + var dummy = new com.tns.tests.DummyClass(); - + dummy.setDummyField(); - + var dummy2 = dummy.dummyField; - + var name = dummy2.getName(); - + expect(name).toEqual("dummy"); }); - + it("TestSetFloatInstanceField", function () { - + __log("TEST: TestSetFloatInstanceField"); - + var lParams = new android.widget.LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.MATCH_PARENT); lParams.weight = 1; - + var value = lParams.weight; - + expect(value).toEqual(1); }); - + it("TestCallMethodOnAnObjectPassedAsParameterInOverriddenMethodAsAnObjectWithoutMetadata", function () { - + __log("TEST: TestCallMethodOnAnObjectPassedAsParameterInOverriddenMethodAsAnObjectWithoutMetadata"); - + var D = com.tns.tests.DummyClass.DummyDerivedClass.extend("D",{ dummyMethod: function(dummy) { return this.getName(); } }) - + var d = new D(); var name = d.executeCallback(); - + expect(name).toEqual("dummy"); }); - + it("TestAccessArrayElementAsObjectWithoutMetadata", function () { - + __log("TEST: TestAccessArrayElementAsObjectWithoutMetadata"); - + var d = new com.tns.tests.DummyClass(); - + var arr = d.getDummyClassArrayAsObject(); - + var arrLength = arr.length; - + expect(arrLength).toEqual(1); - + var dummy = arr[0]; - + var name = dummy.getName(); - + expect(name).toBe("dummy"); }); - + it("TestCallMethodThatReturnsNull", function () { - + __log("TEST: TestCallMethodThatReturnsNull"); var dummy = new com.tns.tests.DummyClass(); - + var x = dummy.getNull(); - + expect(x).toEqual(null); }); - + it("TestCallMethodThatReturnsNullString", function () { __log("TEST: TestCallMethodThatReturnsNullString"); var dummy = new com.tns.tests.DummyClass(); - + var x = dummy.getNullString(); - + expect(x).toEqual(null); }); - + it("TestAccessNullField", function () { __log("TEST: TestAccessNullField"); var dummy = new com.tns.tests.DummyClass(); - + var x = dummy.nullField - + expect(x).toEqual(null); }); - + it("TestAccessNullArrayElement", function () { __log("TEST: TestAccessNullArrayElement"); var dummy = new com.tns.tests.DummyClass(); - + var arr = dummy.getArrayWithNullElement(); - + __log("arr=" + arr.length) - + var x = arr[0]; - + expect(x).toEqual(null); }); - + it("TEMPLATE", function () { __log("TEST: TestCallMethodWithIntVarArg"); - + var dummy = new com.tns.tests.DummyClass(); - + var s = dummy.concatIntArrayAsString([1, 2, 3, 4]); - + expect(s).toBe("1234"); }); - + it("TestCallMethodWithCharVarArg", function () { - + __log("TEST: TestCallMethodWithCharVarArg"); - + var dummy = new com.tns.tests.DummyClass(); - + var s = dummy.concatCharArrayAsString(['t', 'e', 's', 't']); - + expect(s).toBe("test"); }); - + it("TestCallMethodWithObjectVarArg", function () { - + __log("TEST: TestCallMethodWithObjectVarArg"); - + var dummy = new com.tns.tests.DummyClass(); - + var s = dummy.concatObjectArrayAsString([1, "test", false]); expect(s).toBe("1, test, false"); }); - + it("TestCanInheritFromClassInAndroidSupportLibrary", function () { - + __log("TEST: TestCanInheritFromClassInAndroidSupportLibrary"); - + var MyParcelableCompat = android.support.v4.os.ParcelableCompat.extend("MyParcelableCompat", { toString: function() { return "MyParcelableCompat"; } }); - + var compat = new MyParcelableCompat(); - + var s = compat.toString(); - + expect(s).toBe("MyParcelableCompat"); }); - + it("TestCallMethodWithByteParameter", function () { - + __log("TEST: TestCallMethodWithByteParameter"); - + var b = java.lang.Byte.valueOf(byte(123)); - + var s = "" + b; - + expect(s).toBe("123"); }); - + it("TestCallMethodWithFloatParameter", function () { - + __log("TEST: TestCallMethodWithFloatParameter"); - + var d = new com.tns.tests.DummyClass(); - + var s = d.methodWithoutOverloads(1.23); - + expect(s).toBe("float=1.23"); }); - + it("TestCanCallStaticMethodThroughBaseClass", function () { - + __log("TEST: TestCanCallStaticMethodThroughBaseClass"); - + var name = com.tns.tests.MyClassDerived.getName(); expect(name).toBe("com.tns.tests.MyClassBase"); }); - + it("TestUseFieldThatIsArray", function () { - + __log("TEST: TestUseFieldThatIsArray"); - + var d = new com.tns.tests.DummyClass(); - + var arrInt = d.arrIntField; - + var arrIntLength = arrInt.length; - + expect(arrIntLength).toBe(5); - + var intElement = arrInt[2]; - + expect(intElement).toBe(33); - + var arrString = d.arrStringField; - + var arrStringLength = arrString.length; - + expect(arrIntLength).toBe(5); - + var stringElement = arrString[2]; - + expect(stringElement).toBe("cc"); }); - + it("TestCanAssignArrayToField", function () { - + __log("TEST: TestCanAssignArrayToField"); - + var d = new com.tns.tests.DummyClass(); - + var arr = d.arrIntField2; - + expect(arr).toBe(null); - + d.arrIntField2 = d.arrIntField; - + var arrLength = d.arrIntField2.length; - + expect(arrLength).toBe(5); }); - + it("TestCallMethodThatReturnsLong", function () { - + __log("TEST: TestCallMethodThatReturnsLong"); - + var n = java.lang.Long.parseLong("9007199254740991"); // 9007199254740991 = 2^53-1 expect(n.__proto__.valueOf()).toBe(0); @@ -1034,157 +1034,157 @@ describe("Tests ", function () { expect(n instanceof Number).toBe(false); var n = java.lang.Long.parseLong("9007199254740992"); // 9007199254740992 = 2^53 - + var ctorFuncName = n.__proto__.constructor.name; expect(ctorFuncName).toBe("NativeScriptLongNumber"); expect(isNaN(n.valueOf())).toBe(true); - + var javaValue = n.value; expect(javaValue).toBe("9007199254740992"); - + var typeName = typeof n; expect(typeName).toBe("object"); }); - + it("TestCallMethodWithLongParameter", function () { __log("TEST: TestCallMethodWithLongParameter"); - + var d = new com.tns.tests.DummyClass(); - + var n1 = java.lang.Long.parseLong("9007199254740991"); // 9007199254740991 = 2^53-1 var s1 = d.getLongAsString(n1); expect(s1).toBe("9007199254740991"); - + var n2 = java.lang.Long.parseLong("9007199254740992"); // 9007199254740992 = 2^53 var s2 = d.getLongAsString(n2); expect(s2).toBe("9007199254740992"); - + var n3 = java.lang.Long.parseLong("9007199254740993"); // 9007199254740992 = 2^53+1 var s3 = d.getLongAsString(n3); expect(s3).toBe("9007199254740993"); }); - + it("TestCallMethodWithLongCastArgument", function () { __log("TEST: TestCallMethodWithLongCastArgument"); - + var d = new com.tns.tests.DummyClass(); - + var s1 = d.getLongAsString(long("9007199254740991")); // 9007199254740991 = 2^53-1 expect(s1).toBe("9007199254740991"); - + var s2 = d.getLongAsString(long(9007199254740991)); // 9007199254740991 = 2^53-1 expect(s2).toBe("9007199254740991"); - + var s3 = d.getLongAsString(long("9007199254740992")); // 9007199254740992 = 2^53 expect(s3).toBe("9007199254740992"); - + var s4 = d.getLongAsString(long("9007199254740993")); // 9007199254740992 = 2^53+1 expect(s4).toBe("9007199254740993"); }); - + it("TestCallToStringOfNativeScriptLongObject", function () { - + __log("TEST: TestCallToStringOfNativeScriptLongObject"); - + var n = java.lang.Long.parseLong("9007199254740992"); // 9007199254740992 = 2^53 - + var s = n.toString(); - + expect(s).toBe(n.value); }); - + it("TestCallMethodWithLongParameterWithNumberObject", function () { - + __log("TEST: TestCallMethodWithLongParameterWithNumberObject"); - + var d = new com.tns.tests.DummyClass(); - + var s = d.getLongAsString(new Number("9007199254740991")); // 9007199254740991 = 2^53-1 expect(s).toBe("9007199254740991"); }); - + it("TestCallMethodWithMinAndMaxLongValues", function () { - + __log("TEST: TestCallMethodWithMinAndMaxLongValues"); - + var d = new com.tns.tests.DummyClass(); - + var maxLong = d.getMaxLong(); var sMax = d.getLongAsString(maxLong); expect(sMax).toBe("9223372036854775807"); - + var minLong = d.getMinLong(); var sMin = d.getLongAsString(minLong); expect(sMin).toBe("-9223372036854775808"); }); - + it("TestCallMethodWithByteParameter", function () { - + __log("TEST: TestCallMethodWithByteParameter"); - + var d = new com.tns.tests.DummyClass(); - + var s1 = d.method1(byte(123)); expect(s1).toBe("byte=123"); - + var s2 = d.method1(byte(new Number(123))); expect(s2).toBe("byte=123"); - + var s3 = d.method1(byte("123")); expect(s3).toBe("byte=123"); - + var s4 = d.method1(byte(new String("123"))); expect(s4).toBe("byte=123"); }); - + it("TestCallMethodWithShortParameter", function () { - + __log("TEST: TestCallMethodWithShortParameter"); - + var d = new com.tns.tests.DummyClass(); - + var s1 = d.method1(short(12345)); expect(s1).toBe("short=12345"); - + var s2 = d.method1(short(new Number(12345))); expect(s2).toBe("short=12345"); - + var s3 = d.method1(short("12345")); expect(s3).toBe("short=12345"); - + var s4 = d.method1(short(new String("12345"))); expect(s4).toBe("short=12345"); }); - + it("TestCallMethodWithBooleanParameter", function () { - + __log("TEST: TestCallMethodWithBooleanParameter"); - + var d = new com.tns.tests.DummyClass(); - + var s1 = d.method1(true); expect(s1).toBe("boolean=true"); - + var s2 = d.method1(false); expect(s2).toBe("boolean=false"); - + var s3 = d.method1(new Boolean(true)); expect(s3).toBe("boolean=true"); - + var s4 = d.method1(new Boolean(false)); expect(s4).toBe("boolean=false"); }); - + it("TestThrowJavaScriptExceptionWhenCannotResolveJavaMethod", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenCannotResolveJavaMethod"); - + var exceptionCaught = false; - + var d = new com.tns.tests.DummyClass(); - + try { var s = d.method1(new java.lang.Object()); @@ -1193,16 +1193,16 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenCannotResolveJavaConstructor", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenCannotResolveJavaConstructor"); - + var exceptionCaught = false; - + try { var d = new com.tns.tests.DummyClass(new java.io.File()); @@ -1211,21 +1211,21 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenSetArrayRefElementWithNakedJavaScriptObject", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenSetArrayRefElementWithNakedJavaScriptObject"); - + var arr = java.lang.reflect.Array.newInstance(java.lang.Object.class, 10); - + var o = new java.lang.Object(); arr[0] = o; - + var exceptionCaught = false; - + try { arr[0] = {}; @@ -1234,25 +1234,25 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); - + var isOldElement = o.equals(arr[0]); - + expect(isOldElement).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenSetArrayRefElementWithJavaScriptPrimitive", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenSetArrayRefElementWithJavaScriptPrimitive"); - + var arr = java.lang.reflect.Array.newInstance(java.lang.Object.class, 10); - + var o = new java.lang.Object(); arr[0] = o; - + var exceptionCaught = false; - + try { arr[0] = 123; @@ -1261,18 +1261,43 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); - + var isOldElement = o.equals(arr[0]); - + expect(isOldElement).toBe(true); }); - - it("TestThrowJavaScriptExceptionWhenCreateJavaObjectWithNakedJavaScriptObject", function () { + + it("TestRuntimeCurrentObjectIdIsResetProperlyWhenAnExceptionIsThrownWhileConstructingObject", function() { + __log("TEST: TestRuntimeCurrentObjectIdIsResetProperlyWhenAnExceptionIsThrownWhileConstructingObject"); + + var exceptionCaught = false; - __log("TEST: TestThrowJavaScriptExceptionWhenCreateJavaObjectWithNakedJavaScriptObject"); + try { + new java.io.FileInputStream("asdasd"); + } catch (e) { + exceptionCaught = true; + } + + var MyButton = com.tns.tests.Button1.extend("btn1303", { + echo : function(s) { + return "!!!" + s; + } + }); + + var btn = MyButton.class.getConstructors()[0].newInstance(null); + var s = btn.triggerEcho("12345"); + var expected = "!!!12345"; + expect(exceptionCaught).toBe(true); + expect(s).toContain(expected); + }); + + it("TestThrowJavaScriptExceptionWhenCreateJavaObjectWithNakedJavaScriptObject", function () { + + __log("TEST: TestThrowJavaScriptExceptionWhenCreateJavaObjectWithNakedJavaScriptObject"); + var exceptionCaught = false; try @@ -1286,13 +1311,13 @@ describe("Tests ", function () { expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenCallJavaMethodWithNakedJavaScriptObject", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenCallJavaMethodWithNakedJavaScriptObject"); - + var exceptionCaught = false; - + var d = new com.tns.tests.DummyClass(); try @@ -1303,16 +1328,16 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenCallJavaMethodWithJavaScriptPrimitiveWhenJavaRefIsExpected", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenCallJavaMethodWithJavaScriptPrimitiveWhenJavaRefIsExpected"); - + var exceptionCaught = false; - + var d = new com.tns.tests.DummyClass(); try @@ -1326,26 +1351,26 @@ describe("Tests ", function () { expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenOverideMethodImplementationIsDeleted", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenOverideMethodImplementationIsDeleted"); - + var exceptionCaught = false; - + var impl = { - echo : function(s) { - return "!!!" + s; - } + echo : function(s) { + return "!!!" + s; + } }; - + var MyButton = com.tns.tests.Button1.extend("btn1303", impl); var btn = new MyButton(); - + var echo = com.tns.tests.Button1.prototype.echo; delete com.tns.tests.Button1.prototype.echo; delete impl.echo; - + try { __log("btn=" + btn.triggerEcho("12345")); @@ -1356,7 +1381,7 @@ describe("Tests ", function () { } expect(exceptionCaught).toBe(true); - + exceptionCaught = false; try @@ -1369,30 +1394,30 @@ describe("Tests ", function () { } expect(exceptionCaught).toBe(true); - + com.tns.tests.Button1.prototype.echo = echo; }); - + it("TestThrowJavaScriptExceptionWhenOverideMethodImplementationIsOverwritten", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenOverideMethodImplementationIsOverwritten"); - + var exceptionCaught = false; - + var impl = { - echo : function(s) { - return "!!!" + s; - } + echo : function(s) { + return "!!!" + s; + } }; - + var MyButton = com.tns.tests.Button1.extend("btn1344", impl); var btn = new MyButton(); - + impl.echo = "" - - try + + try { - __log("btn=" + btn.triggerEcho("123")); + __log("btn=" + btn.triggerEcho("123")); } catch (e) { @@ -1400,7 +1425,7 @@ describe("Tests ", function () { } expect(exceptionCaught).toBe(true); - + exceptionCaught = false; try @@ -1414,26 +1439,26 @@ describe("Tests ", function () { expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenPartiallyImplementedInterfaceIsUsed", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenPartiallyImplementedInterfaceIsUsed"); - + var methodCalled = false; var exceptionCaught = false; - + var d = new com.tns.tests.DummyClass(); - + var impl1 = new com.tns.tests.DummyClass.MyInterface("impl1_1393", { echoInt: function(i) { methodCalled = true; return i; } }); - + var i = d.triggerEchoInt(impl1, 123); - + expect(methodCalled).toBe(true); - + expect(i).toBe(123); - + try { d.triggerDoSomething(impl1); @@ -1442,20 +1467,20 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); - + methodCalled = false; exceptionCaught = false; - + var impl2 = new com.tns.tests.DummyClass.MyInterface("impl2_1417",{ doSomething: function() { methodCalled = true; } }); - + d.triggerDoSomething(impl2); - + expect(methodCalled).toBe(true); - + try { d.triggerEchoInt(impl2, 123); @@ -1464,21 +1489,21 @@ describe("Tests ", function () { { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenImplementationObjectIsUsedToExtendMoreThanOneClass", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenImplementationObjectIsUsedToExtendMoreThanOneClass"); - + var implObj = {} - + var exceptionCaught = false; - + var Button1 = com.tns.tests.Button1.extend("Button1", implObj); - - try + + try { var D = com.tns.tests.DummyClass.DummyDerivedClass.extend("D1440", implObj); } @@ -1487,18 +1512,18 @@ describe("Tests ", function () { __log("TEST: TestThrowJavaScriptExceptionWhenImplementationObjectIsUsedToExtendMoreThanOneClass exception:" + e); exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenPassBooleanArgumentWhereNotExpected", function () { - + __log("TEST: TestThrowJavaScriptExceptionWhenPassBooleanArgumentWhereNotExpected"); - + var d = new com.tns.tests.DummyClass(); - + var exceptionCaught = false; - + try { d.setName(false); @@ -1508,9 +1533,9 @@ describe("Tests ", function () { __log("e=" + e); exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); - + exceptionCaught = false; try @@ -1522,18 +1547,18 @@ describe("Tests ", function () { __log("e=" + e); exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestThrowJavaScriptExceptionWhenPassNumberArgumentWhereNotExpected", function () { __log("TEST: TestThrowJavaScriptExceptionWhenPassNumberArgumentWhereNotExpected"); - + var d = new com.tns.tests.DummyClass(); - + var exceptionCaught = false; - + try { d.setName(1); @@ -1543,12 +1568,12 @@ describe("Tests ", function () { __log("e=" + e); exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); - + it("TestCallProctedMethodDefinedAsAbstractAndThenOverwritten", function () { - + __log("TEST: TestCallProctedMethodDefinedAsAbstractAndThenOverwritten"); var C = com.tns.tests.AbsClassImpl.extend("C1520", { @@ -1558,65 +1583,65 @@ describe("Tests ", function () { } }); var c = new C(); - + var s = c.echo("test"); - + expect(s).toBe("test!"); }); - + it("TestCharSequenceReturnValueIsTreatedAsStringWhenItIsString", function () { - + __log("TEST: TestCharSequenceReturnValueIsTreatedAsStringWhenItIsString"); var d = new com.tns.tests.DummyClass(); - + var s = d.getNameAsCharSequence(); - + expect(s).toBe("dummy"); }); - + it("TestObjectReturnValueIsTreatedAsStringWhenItIsString", function () { - + __log("TEST: TestObjectReturnValueIsTreatedAsStringWhenItIsString"); var d = new com.tns.tests.DummyClass(); - + var s = d.getNameAsCharSequence(); - + expect(s).toBe("dummy"); expect(true).toEqual(true); }); - + it("TestCanFindImplementationObjectWhenCreateExtendedObjectFromJava", function () { __log("TEST: TestCanFindImplementationObjectWhenCreateExtendedObjectFromJava"); var O = java.lang.Object.extend("O1560", {}); - + var ctor = (new O()).getClass().getConstructors()[0]; - + var o = ctor.newInstance(null); expect(o).not.toBe(null); }); - + it("TestCanCallMethodThatReturnsArrayOfInterfaces", function () { __log("TEST: TestCanCallMethodThatReturnsArrayOfInterfaces"); var arr = java.lang.reflect.Array.newInstance(android.view.View.OnClickListener.class, 1); - + expect(arr).not.toBe(null); - + var listener = new android.view.View.OnClickListener("listener1580", {}); - + arr[0] = listener; expect(arr[0]).not.toBe(null); }); - + it("TestCanParseSignatureWithTypesThatContainsCapitalLettersForPrimitiveTypes", function () { - + __log("TEST: TestCanParseSignatureWithTypesThatContainsCapitalLettersForPrimitiveTypes"); var formats = java.lang.reflect.Array.newInstance(java.text.NumberFormat.class, 2); @@ -1626,29 +1651,29 @@ describe("Tests ", function () { mf.setFormats(formats); var arr = mf.parse("123, 4567"); var len = arr.length; - + expect(len).toBe(2); }); it("TestCanCallToStringOnClassProxy", function () { - + __log("TEST: TestCanCallToStringOnClassProxy"); var view = android.view.View; var s = view.toString(); - + expect(s.length).toBeGreaterThan(0); }); - + it("When_accessing_class_property_on_a_extended_class_it_should_return_the_extended_class", function () { - + __log("TEST: When_accessing_class_property_on_a_extended_class_it_should_return_the_extended_class"); - + var MyButton = com.tns.tests.Button1.extend("MyButton1615", { toString : function() { - return "button1" - }}); - + return "button1" + }}); + var clazz1 = MyButton.class; var name1 = clazz1.getName(); expect(name1.indexOf("MyButton1615")).not.toEqual(-1); @@ -1658,25 +1683,25 @@ describe("Tests ", function () { var name2 = clazz2.getName(); expect(name2.indexOf("MyButton1615")).not.toEqual(-1); }); - + it("When_calling_non_existent_ctor_it_should_fail", function () { - + __log("TEST: When_calling_non_existent_ctor_it_should_fail: Start"); - + try { var textView = android.widget.TextView; var MyTextView = textView.extend({ - + }); - + var my = new MyTextView(); } catch(e) { exceptionCaught = true; } - + expect(exceptionCaught).toBe(true); }); }); \ No newline at end of file