diff --git a/src/jni/CallbackHandlers.cpp b/src/jni/CallbackHandlers.cpp index 4080279dc..47cd36236 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.GetFieldID(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; + + { + JavaObjectIdScope objIdScope(env, CURRENT_OBJECTID_FIELD_ID, runtime->GetJavaRuntime(), 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); + + AdjustAmountOfExternalAllocatedMemory(env, isolate); JniLocalRef localInstance(instance); success = !localInstance.IsNull(); @@ -113,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)); @@ -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..534f04dfb 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; @@ -124,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/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..8cf266fa3 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,12 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc curPtr += sizeof(uint16_t); string lastMethodName; MethodCallbackData *callbackData = 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 +515,7 @@ Local MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc prototypeTemplate->Set(funcName, func); lastMethodName = entry.name; } + callbackData->candidates.push_back(entry); } @@ -553,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()); 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..eccbc8dc1 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,9 +282,7 @@ static Constructor resolveConstructor(Class clazz, Object[] args) throws C Collections.sort(candidates, distanceComparator); Constructor selectedCtor = candidates.get(0).x; - boolean success = convertConstructorArgs(selectedCtor, args); - - return success ? selectedCtor : null; + return selectedCtor; } return null; @@ -500,11 +510,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..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; @@ -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() { @@ -389,7 +328,7 @@ public static void initInstance(Object instance) { Runtime runtime = Runtime.getCurrentRuntime(); - int objectId = Runtime.currentObjectId; + int objectId = runtime.currentObjectId; if (objectId != -1) { @@ -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/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/app/tests/tests.js b/test-app/assets/app/tests/tests.js index 4f855d983..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,39 +1193,39 @@ 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.lang.Object()); + var d = new com.tns.tests.DummyClass(new java.io.File()); } catch (e) { 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 diff --git a/test-app/assets/metadata/treeNodeStream.dat b/test-app/assets/metadata/treeNodeStream.dat index 22b347b3b..7019f2375 100644 Binary files a/test-app/assets/metadata/treeNodeStream.dat and b/test-app/assets/metadata/treeNodeStream.dat differ diff --git a/test-app/assets/metadata/treeStringsStream.dat b/test-app/assets/metadata/treeStringsStream.dat index e758ce8b9..614843068 100644 Binary files a/test-app/assets/metadata/treeStringsStream.dat and b/test-app/assets/metadata/treeStringsStream.dat differ diff --git a/test-app/assets/metadata/treeValueStream.dat b/test-app/assets/metadata/treeValueStream.dat index ea3431aff..9e560c966 100644 Binary files a/test-app/assets/metadata/treeValueStream.dat and b/test-app/assets/metadata/treeValueStream.dat differ diff --git a/test-app/src/com/tns/tests/DummyClass.java b/test-app/src/com/tns/tests/DummyClass.java index 32607abc9..958e936a1 100644 --- a/test-app/src/com/tns/tests/DummyClass.java +++ b/test-app/src/com/tns/tests/DummyClass.java @@ -9,7 +9,7 @@ public class DummyClass private static class DummyClassImpl extends DummyClass { } - + public static class DummyDerivedClass extends DummyClass { @Override @@ -18,26 +18,27 @@ public String dummyMethod(DummyClass dummy) return super.dummyMethod(dummy); } } - + public static interface MyInterface { int echoInt(int i); - + void doSomething(); } public String nameField; - + public String arbitraryString; + public DummyClass() { nameField = "dummy"; - + arrIntField = new int[] { 11, 22, 33, 44, 55 }; arrIntField2 = null; arrStringField = new String[] { "aa", "bb", "cc", "dd", "ee" }; arrLongField = new long[] { (long)1 << 0, (long)1 << 10, (long)1 << 20, (long)1 << 30, (long)1 << 40, (long)1 << 50, (long)1 << 60 }; } - + public DummyClass(boolean throwException) throws Exception { if (throwException) @@ -52,37 +53,63 @@ public DummyClass(boolean throwException) throws Exception } } } - + public DummyClass(String name) { this.nameField = name; + this.arbitraryString = String.class.getName(); + } + + public DummyClass(Object obj) + { + this.arbitraryString = Object.class.getName(); + } + + public DummyClass(DummyClass obj) + { + this.arbitraryString = DummyClass.class.getName(); + } + + public DummyClass(MyInterface obj) + { + this.arbitraryString = MyInterface.class.getName(); + } + + public DummyClass(Object obj, DummyClass obj2) + { + this.arbitraryString = new StringBuilder(Object.class.getName()).append(separator).append(DummyClass.class.getName()).toString(); } - + + public DummyClass(DummyClass obj, Object obj2) + { + this.arbitraryString = new StringBuilder(DummyClass.class.getName()).append(separator).append(Object.class.getName()).toString(); + } + public int dummyMethod(int value) { return value; } - + public String executeCallback() { return dummyMethod(new DummyClassImpl()); } - + public String dummyMethod(DummyClass dummy) { return dummy.nameField; } - + public String getName() { return nameField; } - + public Object getNameAsObject() { return nameField; } - + public CharSequence getNameAsCharSequence() { return nameField; @@ -92,7 +119,7 @@ public void setName(String value) { nameField = value; } - + public String getname2() { return nameField; @@ -107,21 +134,21 @@ public Object getDummyClassAsObject() { return new DummyClassImpl(); } - + public Object getDummyClassArrayAsObject() { DummyClassImpl[] arr = new DummyClassImpl[1]; arr[0] = new DummyClassImpl(); return arr; } - + public void setDummyField() { dummyField = new DummyClassImpl(); } - + public Object dummyField; - + public void methodThatThrowsException() throws Exception { try @@ -133,35 +160,35 @@ public void methodThatThrowsException() throws Exception throw new Exception("This is wrapped exception throwns on purpose", e); } } - + private void someMethodThatThrowsException() throws Exception { throw new Exception("This exception is thrown on purpose"); } - + public Object nullField = null; - + public Object getNull() { return null; } - + public String getNullString() { return null; } - + public Object[] getArrayWithNullElement() { Object[] arr = new Object[1]; arr[0] = null; return arr; } - + public String concatIntArrayAsString(int... params) { String s = ""; - + if (params != null) { for (int i=0; i"); 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 "; }