From ea5ec9fad41bc3d3ef0ce9c6d4650df318206621 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 27 Jun 2016 10:24:50 +0300 Subject: [PATCH 1/9] runtime binding generator can now generate classes implementing multiple interfaces --- .../src/main/java/com/tns/bindings/Dump.java | 69 +- .../java/com/tns/bindings/ProxyGenerator.java | 7 +- .../src/main/java/com/tns/ClassResolver.java | 4 +- runtime/src/main/java/com/tns/DexFactory.java | 21 +- runtime/src/main/java/com/tns/Runtime.java | 5 +- runtime/src/main/jni/CallbackHandlers.cpp | 1532 ++++++++--------- runtime/src/main/jni/CallbackHandlers.h | 4 + runtime/src/main/jni/MetadataNode.cpp | 10 + runtime/src/main/jni/MetadataNode.h | 2 + .../app/src/main/assets/app/MyActivity.js | 3 +- test-app/app/src/main/assets/app/MyApp.js | 2 + 11 files changed, 855 insertions(+), 804 deletions(-) diff --git a/binding-generator/src/main/java/com/tns/bindings/Dump.java b/binding-generator/src/main/java/com/tns/bindings/Dump.java index bdf00017b..1a94bc18b 100644 --- a/binding-generator/src/main/java/com/tns/bindings/Dump.java +++ b/binding-generator/src/main/java/com/tns/bindings/Dump.java @@ -199,34 +199,36 @@ public void generateProxy(ApplicationWriter aw, String proxyName, ClassDescripto String methodOverride = methodOverrides[i]; methodOverridesSet.add(methodOverride); } - generateProxy(aw, proxyName, classTo, methodOverridesSet); - } - public void generateProxy(ApplicationWriter aw, ClassDescriptor classTo, String[] methodOverrides, int ignored) - { - HashSet methodOverridesSet = new HashSet(); + generateProxy(aw, proxyName, classTo, methodOverridesSet, null); + } + + public void generateProxy(ApplicationWriter aw, ClassDescriptor classTo, String[] methodOverrides, int ignored) + { + HashSet methodOverridesSet = new HashSet(); + for (int i = 0; i < methodOverrides.length; i++) { String methodOverride = methodOverrides[i]; methodOverridesSet.add(methodOverride); } - generateProxy(aw, "0", classTo, methodOverridesSet); + + generateProxy(aw, "0", classTo, methodOverridesSet, null); } public void generateProxy(ApplicationWriter aw, String proxyName, ClassDescriptor classTo) { - generateProxy(aw, proxyName, classTo, null); + generateProxy(aw, proxyName, classTo, null, null); } public void generateProxy(ApplicationWriter aw, ClassDescriptor classTo) { - generateProxy(aw, "0", classTo, null); - } + generateProxy(aw, "0", classTo, null, null); + } - public void generateProxy(ApplicationWriter aw, String proxyName, ClassDescriptor classTo, HashSet methodOverrides) + public void generateProxy(ApplicationWriter aw, String proxyName, ClassDescriptor classTo, HashSet methodOverrides, HashSet implementedInterfaces) { String classSignature = getAsmDescriptor(classTo); - //String methodSignature = org.objectweb.asm.Type.getMethodDescriptor(Object.class.getMethods()[0]); String tnsClassSignature = LCOM_TNS + classSignature.substring(1, classSignature.length() - 1).replace("$", "_"); @@ -237,8 +239,8 @@ public void generateProxy(ApplicationWriter aw, String proxyName, ClassDescripto tnsClassSignature += ";"; - ClassVisitor cv = generateClass(aw, classTo, classSignature, tnsClassSignature); - MethodDescriptor[] methods = getSupportedMethods(classTo, methodOverrides); + ClassVisitor cv = generateClass(aw, classTo, classSignature, tnsClassSignature, implementedInterfaces); + MethodDescriptor[] methods = getSupportedMethods(classTo, methodOverrides, implementedInterfaces); methods = groupMethodsByNameAndSignature(methods); @@ -332,12 +334,17 @@ private void collectInterfaceMethods(ClassDescriptor clazz, HashSet meth } } - private MethodDescriptor[] getSupportedMethods(ClassDescriptor clazz, HashSet methodOverrides) + private MethodDescriptor[] getSupportedMethods(ClassDescriptor clazz, HashSet methodOverrides, HashSet interfacesToImplement) { ArrayList result = new ArrayList(); collectInterfaceMethods(clazz, methodOverrides, result); + for (ClassDescriptor iface : interfacesToImplement) + { + collectInterfaceMethods(iface, methodOverrides, result); + } + if (!clazz.isInterface()) { HashMap finalMethods = new HashMap(); @@ -601,13 +608,13 @@ private void generateHashCodeSuper(ClassVisitor cv) private void generateMethod(ClassVisitor cv, ClassDescriptor classTo, MethodDescriptor method, int methodNumber, String classSignature, String tnsClassSignature, int fieldBit) { - if (ProxyGenerator.IsLogEnabled) Log.d("Generator", "generatingMethod " + method.getName()); - + if (ProxyGenerator.IsLogEnabled) { + Log.d("Generator", "generatingMethod " + method.getName()); + } + //TODO: handle checked exceptions String methodDexSignature = getDexMethodDescriptor(method); String[] exceptions = new String[0]; - - MethodVisitor mv; int methodModifiers = getDexModifiers(method); @@ -620,6 +627,7 @@ private void generateMethod(ClassVisitor cv, ClassDescriptor classTo, MethodDesc { generateInitializedBlock(mv, thisRegister, classSignature, tnsClassSignature); } + generateCallOverrideBlock(mv, method, thisRegister, classSignature, tnsClassSignature, methodDexSignature, fieldBit); mv.visitEnd(); @@ -919,25 +927,38 @@ private void generateInitializedField(ClassVisitor cv) static final String[] classImplentedInterfaces = new String[] { "Lcom/tns/NativeScriptHashCodeProvider;" }; static final String[] interfaceImplementedInterfaces = new String[] { "Lcom/tns/NativeScriptHashCodeProvider;", "" }; - private ClassVisitor generateClass(ApplicationWriter aw, ClassDescriptor classTo, String classSignature, String tnsClassSignature) + private ClassVisitor generateClass(ApplicationWriter aw, ClassDescriptor classTo, String classSignature, String tnsClassSignature, HashSet implementedInterfaces) { ClassVisitor cv; int classModifiers = getDexModifiers(classTo); - String[] implentedInterfaces = classImplentedInterfaces; + ArrayList interfacesToImplement = new ArrayList(Arrays.asList(classImplentedInterfaces)); + if (classTo.isInterface()) { interfaceImplementedInterfaces[1] = classSignature; //new String[] { "Lcom/tns/NativeScriptHashCodeProvider;", classSignature }; - implentedInterfaces = interfaceImplementedInterfaces; + for(String interfaceToImpl : interfaceImplementedInterfaces) { + if(!interfacesToImplement.contains(interfaceToImpl)) { + interfacesToImplement.add(interfaceToImpl); + } + } + classSignature = objectClass; } else { - implentedInterfaces = classImplentedInterfaces; + if(implementedInterfaces != null) { + for(ClassDescriptor interfaceToImpl : implementedInterfaces) { + interfacesToImplement.add(getAsmDescriptor(interfaceToImpl)); + } + } } - cv = aw.visitClass(classModifiers, tnsClassSignature, null, classSignature, implentedInterfaces); - cv.visit(0, classModifiers, tnsClassSignature, null, classSignature, implentedInterfaces); + String[] interfacesToImplementArr = new String[interfacesToImplement.size()]; + interfacesToImplementArr = interfacesToImplement.toArray(interfacesToImplementArr); + + cv = aw.visitClass(classModifiers, tnsClassSignature, null, classSignature, interfacesToImplementArr); + cv.visit(0, classModifiers, tnsClassSignature, null, classSignature, interfacesToImplementArr); cv.visitSource(classTo.getName() + ".java", null); return cv; } diff --git a/binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java b/binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java index fd19e4df7..aa3551681 100644 --- a/binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java +++ b/binding-generator/src/main/java/com/tns/bindings/ProxyGenerator.java @@ -44,15 +44,16 @@ public String generateProxy(String proxyName, ClassDescriptor classToProxy, Stri methodOverridesSet.add(methodOverride); } } - return generateProxy(proxyName, classToProxy, methodOverridesSet, isInterface); + + return generateProxy(proxyName, classToProxy, methodOverridesSet, null, isInterface); } - public String generateProxy(String proxyName, ClassDescriptor classToProxy, HashSet methodOverrides, boolean isInterface) throws IOException + public String generateProxy(String proxyName, ClassDescriptor classToProxy, HashSet methodOverrides, HashSet implementedInterfaces, boolean isInterface) throws IOException { ApplicationWriter aw = new ApplicationWriter(); aw.visit(); - dump.generateProxy(aw, proxyName, classToProxy, methodOverrides); + dump.generateProxy(aw, proxyName, classToProxy, methodOverrides, implementedInterfaces); aw.visitEnd(); byte[] generatedBytes = aw.toByteArray(); diff --git a/runtime/src/main/java/com/tns/ClassResolver.java b/runtime/src/main/java/com/tns/ClassResolver.java index 43125ed51..211e1d902 100644 --- a/runtime/src/main/java/com/tns/ClassResolver.java +++ b/runtime/src/main/java/com/tns/ClassResolver.java @@ -11,7 +11,7 @@ public ClassResolver(Runtime runtime) this.runtime = runtime; } - public Class resolveClass(String fullClassName, DexFactory dexFactory, String[] methodOverrides, boolean isInterface) throws ClassNotFoundException, IOException + public Class resolveClass(String fullClassName, DexFactory dexFactory, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException { String cannonicalClassName = fullClassName.replace('/', '.'); String name = null; @@ -41,7 +41,7 @@ public Class resolveClass(String fullClassName, DexFactory dexFactory, String } } - clazz = dexFactory.resolveClass(name, className, methodOverrides, isInterface); + clazz = dexFactory.resolveClass(name, className, methodOverrides, implementedInterfaces, isInterface); } if (clazz == null) diff --git a/runtime/src/main/java/com/tns/DexFactory.java b/runtime/src/main/java/com/tns/DexFactory.java index d9d243710..7b8cc3b8e 100644 --- a/runtime/src/main/java/com/tns/DexFactory.java +++ b/runtime/src/main/java/com/tns/DexFactory.java @@ -16,6 +16,7 @@ import java.util.zip.ZipOutputStream; import com.tns.bindings.ProxyGenerator; +import com.tns.bindings.desc.ClassDescriptor; import com.tns.bindings.desc.reflection.ClassInfo; import dalvik.system.DexClassLoader; @@ -63,7 +64,8 @@ public DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, Strin static long totalMultiDexTime = 0; static long totalLoadDexTime = 0; - public Class resolveClass(String name, String className, String[] methodOverrides, boolean isInterface) throws ClassNotFoundException, IOException + // TODO: Pete: new param - implementedInterfaces + public Class resolveClass(String name, String className, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException { String fullClassName = className.replace("$", "_"); @@ -115,7 +117,7 @@ public Class resolveClass(String name, String className, String[] methodOverr logger.write("generating proxy in place"); } - dexFilePath = this.generateDex(name, classToProxy, methodOverrides, isInterface); + dexFilePath = this.generateDex(name, classToProxy, methodOverrides, implementedInterfaces, isInterface); dexFile = new File(dexFilePath); long stopGenTime = System.nanoTime(); totalGenTime += stopGenTime - startGenTime; @@ -253,22 +255,33 @@ private File getDexFile(String className) throws InvalidClassException return null; } - private String generateDex(String proxyName, String className, String[] methodOverrides, boolean isInterface) throws ClassNotFoundException, IOException + private String generateDex(String proxyName, String className, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException { Class classToProxy = Class.forName(className); HashSet methodOverridesSet = null; + HashSet implementedInterfacesSet = new HashSet(); + if (methodOverrides != null) { methodOverridesSet = new HashSet(); for (int i = 0; i < methodOverrides.length; i++) { String methodOverride = methodOverrides[i]; + + if (implementedInterfaces.length > 0) { + for(int j = 0; j < implementedInterfaces.length; j++) { + if(!implementedInterfaces[j].isEmpty()) { + implementedInterfacesSet.add(new ClassInfo(Class.forName(implementedInterfaces[j]))); + } + } + } + methodOverridesSet.add(methodOverride); } } - return proxyGenerator.generateProxy(proxyName, new ClassInfo(classToProxy) , methodOverridesSet, isInterface); + return proxyGenerator.generateProxy(proxyName, new ClassInfo(classToProxy) , methodOverridesSet, implementedInterfacesSet, isInterface); } private void updateDexThumbAndPurgeCache() diff --git a/runtime/src/main/java/com/tns/Runtime.java b/runtime/src/main/java/com/tns/Runtime.java index e1263ea40..758739eb9 100644 --- a/runtime/src/main/java/com/tns/Runtime.java +++ b/runtime/src/main/java/com/tns/Runtime.java @@ -304,10 +304,11 @@ public void run() return result; } + // TODO: Pete: new param - implementedInterfaces @RuntimeCallable - private Class resolveClass(String fullClassName, String[] methodOverrides, boolean isInterface) throws ClassNotFoundException, IOException + private Class resolveClass(String fullClassName, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException { - Class javaClass = classResolver.resolveClass(fullClassName, dexFactory, methodOverrides, isInterface); + Class javaClass = classResolver.resolveClass(fullClassName, dexFactory, methodOverrides, implementedInterfaces, isInterface); return javaClass; } diff --git a/runtime/src/main/jni/CallbackHandlers.cpp b/runtime/src/main/jni/CallbackHandlers.cpp index acbd96dcb..61e62fa7f 100644 --- a/runtime/src/main/jni/CallbackHandlers.cpp +++ b/runtime/src/main/jni/CallbackHandlers.cpp @@ -21,137 +21,153 @@ #include "JsDebugger.h" #include "SimpleProfiler.h" #include "Runtime.h" +#include "include/v8.h" using namespace v8; using namespace std; using namespace tns; -void CallbackHandlers::Init(Isolate *isolate, ObjectManager *objectManager) -{ - JEnv env; +void CallbackHandlers::Init(Isolate *isolate, ObjectManager *objectManager) { + JEnv env; - JAVA_LANG_STRING = env.FindClass("java/lang/String"); - assert(JAVA_LANG_STRING != nullptr); + JAVA_LANG_STRING = env.FindClass("java/lang/String"); + assert(JAVA_LANG_STRING != nullptr); - RUNTIME_CLASS = env.FindClass("com/tns/Runtime"); - assert(RUNTIME_CLASS != nullptr); + RUNTIME_CLASS = env.FindClass("com/tns/Runtime"); + assert(RUNTIME_CLASS != nullptr); - RESOLVE_CLASS_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "resolveClass", "(Ljava/lang/String;[Ljava/lang/String;Z)Ljava/lang/Class;"); - assert(RESOLVE_CLASS_METHOD_ID != nullptr); + RESOLVE_CLASS_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "resolveClass", + "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Z)Ljava/lang/Class;"); + assert(RESOLVE_CLASS_METHOD_ID != nullptr); - CURRENT_OBJECTID_FIELD_ID = env.GetFieldID(RUNTIME_CLASS, "currentObjectId", "I"); - assert(CURRENT_OBJECTID_FIELD_ID != nullptr); + 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"); - assert(MAKE_INSTANCE_STRONG_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); + GET_TYPE_METADATA = env.GetStaticMethodID(RUNTIME_CLASS, "getTypeMetadata", + "(Ljava/lang/String;I)[Ljava/lang/String;"); + assert(GET_TYPE_METADATA != nullptr); - ENABLE_VERBOSE_LOGGING_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "enableVerboseLogging", "()V"); - assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); + ENABLE_VERBOSE_LOGGING_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "enableVerboseLogging", + "()V"); + assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); - DISABLE_VERBOSE_LOGGING_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "disableVerboseLogging", "()V"); - assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); + DISABLE_VERBOSE_LOGGING_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "disableVerboseLogging", + "()V"); + assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); - GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, "getChangeInBytesOfUsedMemory", "()J"); - assert(GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID != nullptr); + GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID = env.GetMethodID(RUNTIME_CLASS, + "getChangeInBytesOfUsedMemory", + "()J"); + assert(GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID != nullptr); - MetadataNode::Init(isolate); + MetadataNode::Init(isolate); - MethodCache::Init(); + MethodCache::Init(); } -bool CallbackHandlers::RegisterInstance(Isolate *isolate, const Local& jsObject, const std::string& fullClassName, const ArgsWrapper& argWrapper, const Local& implementationObject, bool isInterface) -{ - bool success; +bool CallbackHandlers::RegisterInstance(Isolate *isolate, const Local &jsObject, + const std::string &fullClassName, + const ArgsWrapper &argWrapper, + const Local &implementationObject, + bool isInterface) { + bool success; - DEBUG_WRITE("RegisterInstance called for '%s'", fullClassName.c_str()); + DEBUG_WRITE("RegisterInstance called for '%s'", fullClassName.c_str()); - auto runtime = Runtime::GetRuntime(isolate); - auto objectManager = runtime->GetObjectManager(); + auto runtime = Runtime::GetRuntime(isolate); + auto objectManager = runtime->GetObjectManager(); - JEnv env; + JEnv env; - jclass generatedJavaClass = ResolveClass(isolate, fullClassName, implementationObject, isInterface); + jclass generatedJavaClass = ResolveClass(isolate, fullClassName, implementationObject, + isInterface); - int javaObjectID = objectManager->GenerateNewObjectID(); + int javaObjectID = objectManager->GenerateNewObjectID(); - DEBUG_WRITE("RegisterInstance: Linking new instance"); - objectManager->Link(jsObject, javaObjectID, nullptr); + DEBUG_WRITE("RegisterInstance: Linking new instance"); + objectManager->Link(jsObject, javaObjectID, nullptr); - // resolve constructor - auto mi = MethodCache::ResolveConstructorSignature(argWrapper, fullClassName, generatedJavaClass, isInterface); + // resolve constructor + auto mi = MethodCache::ResolveConstructorSignature(argWrapper, fullClassName, + generatedJavaClass, isInterface); - jobject instance; + jobject instance; - { - JavaObjectIdScope objIdScope(env, CURRENT_OBJECTID_FIELD_ID, runtime->GetJavaRuntime(), javaObjectID); + { + 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(); + 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); - } - } + instance = env.NewObjectA(generatedJavaClass, mi.mid, ctorArgs); + } + } - env.CallVoidMethod(runtime->GetJavaRuntime(), MAKE_INSTANCE_STRONG_ID, instance, javaObjectID); + env.CallVoidMethod(runtime->GetJavaRuntime(), MAKE_INSTANCE_STRONG_ID, instance, javaObjectID); - AdjustAmountOfExternalAllocatedMemory(env, isolate); + AdjustAmountOfExternalAllocatedMemory(env, isolate); - JniLocalRef localInstance(instance); - success = !localInstance.IsNull(); + JniLocalRef localInstance(instance); + success = !localInstance.IsNull(); - if (success) - { - DEBUG_WRITE("RegisterInstance: Updating linked instance with its real class"); - jclass instanceClass = env.FindClass(fullClassName); - objectManager->SetJavaClass(jsObject, instanceClass); - } - else - { - DEBUG_WRITE("RegisterInstance failed with null new instance"); - } + if (success) { + DEBUG_WRITE("RegisterInstance: Updating linked instance with its real class"); + jclass instanceClass = env.FindClass(fullClassName); + objectManager->SetJavaClass(jsObject, instanceClass); + } + else { + DEBUG_WRITE("RegisterInstance failed with null new instance"); + } - return success; + return success; } -jclass CallbackHandlers::ResolveClass(Isolate *isolate, const string& fullClassname, const Local& implementationObject, bool isInterface) -{ - auto itFound = s_classCache.find(fullClassname); +jclass CallbackHandlers::ResolveClass(Isolate *isolate, const string &fullClassname, + const Local &implementationObject, bool isInterface) { + auto itFound = s_classCache.find(fullClassname); + + jclass globalRefToGeneratedClass; - jclass globalRefToGeneratedClass; + if (itFound != s_classCache.end()) { + globalRefToGeneratedClass = itFound->second; + } + else { + JEnv env; - if (itFound != s_classCache.end()) - { - globalRefToGeneratedClass = itFound->second; - } - else - { - JEnv env; + // get needed arguments in order to load binding + JniLocalRef javaFullClassName(env.NewStringUTF(fullClassname.c_str())); - // get needed arguments in order to load binding - JniLocalRef javaFullClassName(env.NewStringUTF(fullClassname.c_str())); + // TODO: Pete: Split into getMethodOverrides and getImplementedInterfaces + // TODO: Pete: ~~1~~ + jobjectArray methodOverrides = GetMethodOverrides(env, implementationObject); - jobjectArray methodOverrides = GetMethodOverrides(env, implementationObject); + // TODO: Pete: ~~1~~ - change 1 + jobjectArray implementedInterfaces = GetImplementedInterfaces(env, implementationObject); - auto runtime = Runtime::GetRuntime(isolate); + auto runtime = Runtime::GetRuntime(isolate); - // create or load generated binding (java class) - JniLocalRef generatedClass(env.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_CLASS_METHOD_ID, (jstring) javaFullClassName, methodOverrides, isInterface)); - globalRefToGeneratedClass = static_cast(env.NewGlobalRef(generatedClass)); + // create or load generated binding (java class) + // TODO: Pete: pass them interface names as param to ResolveClass in Runtime class + // TODO: Pete: ~~2~~ - change 1 - pass implementedInterfaces to Runtime + JniLocalRef generatedClass( + env.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_CLASS_METHOD_ID, + (jstring) javaFullClassName, methodOverrides, implementedInterfaces, isInterface)); + globalRefToGeneratedClass = static_cast(env.NewGlobalRef(generatedClass)); - s_classCache.insert(make_pair(fullClassname, globalRefToGeneratedClass)); - } + s_classCache.insert(make_pair(fullClassname, globalRefToGeneratedClass)); + } - return globalRefToGeneratedClass; + return globalRefToGeneratedClass; } // Called by ExtendCallMethodCallback when extending a class @@ -164,740 +180,720 @@ string CallbackHandlers::ResolveClassName(Isolate *isolate, const string& fullCl return className; } -Local CallbackHandlers::GetArrayElement(Isolate *isolate, const Local& array, uint32_t index, const string& arraySignature) -{ - return arrayElementAccessor.GetArrayElement(isolate, array, index, arraySignature); +Local CallbackHandlers::GetArrayElement(Isolate *isolate, const Local &array, + uint32_t index, const string &arraySignature) { + return arrayElementAccessor.GetArrayElement(isolate, array, index, arraySignature); } -void CallbackHandlers::SetArrayElement(Isolate *isolate, const Local& array, uint32_t index, const string& arraySignature, Local& value) -{ - JEnv env; +void CallbackHandlers::SetArrayElement(Isolate *isolate, const Local &array, uint32_t index, + const string &arraySignature, Local &value) { + JEnv env; - arrayElementAccessor.SetArrayElement(isolate, array, index, arraySignature, value); + arrayElementAccessor.SetArrayElement(isolate, array, index, arraySignature, value); } -Local CallbackHandlers::GetJavaField(Isolate *isolate, const Local& caller, FieldCallbackData *fieldData) -{ - return fieldAccessor.GetJavaField(isolate, caller, fieldData); +Local CallbackHandlers::GetJavaField(Isolate *isolate, const Local &caller, + FieldCallbackData *fieldData) { + return fieldAccessor.GetJavaField(isolate, caller, fieldData); } -void CallbackHandlers::SetJavaField(Isolate *isolate, const Local& target, const Local& value, FieldCallbackData *fieldData) -{ - fieldAccessor.SetJavaField(isolate, target, value, fieldData); +void CallbackHandlers::SetJavaField(Isolate *isolate, const Local &target, + const Local &value, FieldCallbackData *fieldData) { + fieldAccessor.SetJavaField(isolate, target, value, fieldData); } -void CallbackHandlers::CallJavaMethod(const Local& caller, const string& className, const string& methodName, MetadataEntry *entry, bool isStatic, bool isSuper, const v8::FunctionCallbackInfo& args) -{ - SET_PROFILER_FRAME(); - - JEnv env; - - jclass clazz; - jmethodID mid; - string *sig = nullptr; - string *returnType = nullptr; - auto retType = MethodReturnType::Unknown; - MethodCache::CacheMethodInfo mi; - - if ((entry != nullptr) && entry->isResolved) - { - isStatic = entry->isStatic; - - if (entry->memberId == nullptr) - { - clazz = env.FindClass(className); - if (clazz == nullptr) - { - MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); - const string callerClassName = callerNode->GetName(); - - DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", className.c_str(), methodName.c_str(), callerClassName.c_str()); - clazz = env.FindClass(callerClassName); - if (clazz == nullptr) - { - //todo: plamen5kov: throw exception here - DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); - return; - } - - entry->memberId = isStatic ? - env.GetStaticMethodID(clazz, methodName, entry->sig) : - env.GetMethodID(clazz, methodName, entry->sig); - - if (entry->memberId == nullptr) - { - //todo: plamen5kov: throw exception here - DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); - return; - } - } - else - { - entry->memberId = isStatic ? - env.GetStaticMethodID(clazz, methodName, entry->sig) : - env.GetMethodID(clazz, methodName, entry->sig); - - if (entry->memberId == nullptr) - { - //todo: plamen5kov: throw exception here - DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); - return; - } - } - entry->clazz = clazz; - } - - mid = reinterpret_cast(entry->memberId); - clazz = entry->clazz; - sig = &entry->sig; - returnType = &entry->returnType; - retType = entry->retType; - } - else - { - DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); - - clazz = env.FindClass(className); - if (clazz != nullptr) - { - mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); - if (mi.mid == nullptr) - { - DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", className.c_str(), methodName.c_str(), isStatic, isSuper); - return; - } - } - else - { - MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); - const string callerClassName = callerNode->GetName(); - DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", callerClassName.c_str(), methodName.c_str(), className.c_str()); - mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); - if (mi.mid == nullptr) - { - DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", className.c_str(), methodName.c_str(), isStatic, isSuper, callerClassName.c_str()); - return; - } - } - - clazz = mi.clazz; - mid = mi.mid; - sig = &mi.signature; - returnType = &mi.returnType; - retType = mi.retType; - } - - if (!isStatic) - { - DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), isSuper); - } - else - { - DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), methodName.c_str()); - } - - JsArgConverter argConverter(args, false, *sig, entry); - - if (!argConverter.IsValid()) - { - JsArgConverter::Error err = argConverter.GetError(); - throw NativeScriptException(err.msg); - } - - auto isolate = args.GetIsolate(); - - JniLocalRef callerJavaObject; - - jvalue* javaArgs = argConverter.ToArgs(); +void CallbackHandlers::CallJavaMethod(const Local &caller, const string &className, + const string &methodName, MetadataEntry *entry, bool isStatic, + bool isSuper, + const v8::FunctionCallbackInfo &args) { + SET_PROFILER_FRAME(); + + JEnv env; + + jclass clazz; + jmethodID mid; + string *sig = nullptr; + string *returnType = nullptr; + auto retType = MethodReturnType::Unknown; + MethodCache::CacheMethodInfo mi; + + if ((entry != nullptr) && entry->isResolved) { + isStatic = entry->isStatic; + + if (entry->memberId == nullptr) { + clazz = env.FindClass(className); + if (clazz == nullptr) { + MetadataNode *callerNode = MetadataNode::GetNodeFromHandle(caller); + const string callerClassName = callerNode->GetName(); + + DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", + className.c_str(), methodName.c_str(), callerClassName.c_str()); + clazz = env.FindClass(callerClassName); + if (clazz == nullptr) { + //todo: plamen5kov: throw exception here + DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); + return; + } + + entry->memberId = isStatic ? + env.GetStaticMethodID(clazz, methodName, entry->sig) : + env.GetMethodID(clazz, methodName, entry->sig); + + if (entry->memberId == nullptr) { + //todo: plamen5kov: throw exception here + DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", + methodName.c_str(), callerClassName.c_str()); + return; + } + } + else { + entry->memberId = isStatic ? + env.GetStaticMethodID(clazz, methodName, entry->sig) : + env.GetMethodID(clazz, methodName, entry->sig); + + if (entry->memberId == nullptr) { + //todo: plamen5kov: throw exception here + DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), + className.c_str()); + return; + } + } + entry->clazz = clazz; + } + + mid = reinterpret_cast(entry->memberId); + clazz = entry->clazz; + sig = &entry->sig; + returnType = &entry->returnType; + retType = entry->retType; + } + else { + DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); + + clazz = env.FindClass(className); + if (clazz != nullptr) { + mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); + if (mi.mid == nullptr) { + DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", + className.c_str(), methodName.c_str(), isStatic, isSuper); + return; + } + } + else { + MetadataNode *callerNode = MetadataNode::GetNodeFromHandle(caller); + const string callerClassName = callerNode->GetName(); + DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", + callerClassName.c_str(), methodName.c_str(), className.c_str()); + mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); + if (mi.mid == nullptr) { + DEBUG_WRITE( + "Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", + className.c_str(), methodName.c_str(), isStatic, isSuper, + callerClassName.c_str()); + return; + } + } + + clazz = mi.clazz; + mid = mi.mid; + sig = &mi.signature; + returnType = &mi.returnType; + retType = mi.retType; + } + + if (!isStatic) { + DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), + methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), + isSuper); + } + else { + DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), + methodName.c_str()); + } + + JsArgConverter argConverter(args, false, *sig, entry); + + if (!argConverter.IsValid()) { + JsArgConverter::Error err = argConverter.GetError(); + throw NativeScriptException(err.msg); + } + + auto isolate = args.GetIsolate(); + + JniLocalRef callerJavaObject; + + jvalue *javaArgs = argConverter.ToArgs(); + + auto runtime = Runtime::GetRuntime(isolate); + auto objectManager = runtime->GetObjectManager(); + + if (!isStatic) { + callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); + if (callerJavaObject.IsNull()) { + stringstream ss; + ss << "No java object found on which to call \"" << methodName << + "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; + throw NativeScriptException(ss.str()); + } + } + + switch (retType) { + case MethodReturnType::Void: { + if (isStatic) { + env.CallStaticVoidMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + env.CallVoidMethodA(callerJavaObject, mid, javaArgs); + } + break; + } + case MethodReturnType::Boolean: { + jboolean result; + if (isStatic) { + result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); + } + args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); + break; + } + case MethodReturnType::Byte: { + jbyte result; + if (isStatic) { + result = env.CallStaticByteMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); + } + args.GetReturnValue().Set(result); + break; + } + case MethodReturnType::Char: { + jchar result; + if (isStatic) { + result = env.CallStaticCharMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); + } + + JniLocalRef str(env.NewString(&result, 1)); + jboolean bol = true; + const char *resP = env.GetStringUTFChars(str, &bol); + args.GetReturnValue().Set(ConvertToV8String(resP, 1)); + env.ReleaseStringUTFChars(str, resP); + break; + } + case MethodReturnType::Short: { + jshort result; + if (isStatic) { + result = env.CallStaticShortMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); + } + args.GetReturnValue().Set(result); + break; + } + case MethodReturnType::Int: { + jint result; + if (isStatic) { + result = env.CallStaticIntMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); + } + args.GetReturnValue().Set(result); + break; + + } + case MethodReturnType::Long: { + jlong result; + if (isStatic) { + result = env.CallStaticLongMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); + } + auto jsLong = ArgConverter::ConvertFromJavaLong(isolate, result); + args.GetReturnValue().Set(jsLong); + break; + } + case MethodReturnType::Float: { + jfloat result; + if (isStatic) { + result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); + } + args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. + break; + } + case MethodReturnType::Double: { + jdouble result; + if (isStatic) { + result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); + } + args.GetReturnValue().Set(result); + break; + } + case MethodReturnType::String: { + jobject result = nullptr; + bool exceptionOccurred; + + if (isStatic) { + result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); + } + + if (result != nullptr) { + auto objectResult = ArgConverter::jstringToV8String(static_cast(result)); + args.GetReturnValue().Set(objectResult); + env.DeleteLocalRef(result); + } + else { + args.GetReturnValue().Set(Null(isolate)); + } + + break; + } + case MethodReturnType::Object: { + jobject result = nullptr; + bool exceptionOccurred; + + if (isStatic) { + result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); + } + else if (isSuper) { + result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); + } + else { + result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); + } + + if (result != nullptr) { + auto isString = env.IsInstanceOf(result, JAVA_LANG_STRING); + + Local objectResult; + if (isString) { + objectResult = ArgConverter::jstringToV8String((jstring) result); + } + else { + jint javaObjectID = objectManager->GetOrCreateObjectId(result); + objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (objectResult.IsEmpty()) { + objectResult = objectManager->CreateJSWrapper(javaObjectID, *returnType, + result); + } + } + + args.GetReturnValue().Set(objectResult); + env.DeleteLocalRef(result); + } + else { + args.GetReturnValue().Set(Null(isolate)); + } + + break; + } + default: { + assert(false); + break; + } + } + + static uint32_t adjustMemCount = 0; + + if ((++adjustMemCount % 2) == 0) { + AdjustAmountOfExternalAllocatedMemory(env, isolate); + } +} - auto runtime = Runtime::GetRuntime(isolate); - auto objectManager = runtime->GetObjectManager(); +int64_t CallbackHandlers::AdjustAmountOfExternalAllocatedMemory(JEnv &env, Isolate *isolate) { + auto runtime = Runtime::GetRuntime(isolate); - if (!isStatic) - { - callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); - if (callerJavaObject.IsNull()) - { - stringstream ss; - ss << "No java object found on which to call \"" << methodName << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; - throw NativeScriptException(ss.str()); - } - } - - switch (retType) - { - case MethodReturnType::Void: - { - if (isStatic) - { - env.CallStaticVoidMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - env.CallVoidMethodA(callerJavaObject, mid, javaArgs); - } - break; - } - case MethodReturnType::Boolean: - { - jboolean result; - if (isStatic) - { - result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); - } - args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); - break; - } - case MethodReturnType::Byte: - { - jbyte result; - if (isStatic) - { - result = env.CallStaticByteMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); - } - args.GetReturnValue().Set(result); - break; - } - case MethodReturnType::Char: - { - jchar result; - if (isStatic) - { - result = env.CallStaticCharMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); - } - - JniLocalRef str(env.NewString(&result, 1)); - jboolean bol = true; - const char* resP = env.GetStringUTFChars(str, &bol); - args.GetReturnValue().Set(ConvertToV8String(resP, 1)); - env.ReleaseStringUTFChars(str, resP); - break; - } - case MethodReturnType::Short: - { - jshort result; - if (isStatic) - { - result = env.CallStaticShortMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); - } - args.GetReturnValue().Set(result); - break; - } - case MethodReturnType::Int: - { - jint result; - if (isStatic) - { - result = env.CallStaticIntMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); - } - args.GetReturnValue().Set(result); - break; - - } - case MethodReturnType::Long: - { - jlong result; - if (isStatic) - { - result = env.CallStaticLongMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); - } - auto jsLong = ArgConverter::ConvertFromJavaLong(isolate, result); - args.GetReturnValue().Set(jsLong); - break; - } - case MethodReturnType::Float: - { - jfloat result; - if (isStatic) - { - result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); - } - args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. - break; - } - case MethodReturnType::Double: - { - jdouble result; - if (isStatic) - { - result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); - } - args.GetReturnValue().Set(result); - break; - } - case MethodReturnType::String: - { - jobject result = nullptr; - bool exceptionOccurred; - - if (isStatic) - { - result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); - } - - if (result != nullptr) - { - auto objectResult = ArgConverter::jstringToV8String(static_cast(result)); - args.GetReturnValue().Set(objectResult); - env.DeleteLocalRef(result); - } - else - { - args.GetReturnValue().Set(Null(isolate)); - } - - break; - } - case MethodReturnType::Object: - { - jobject result = nullptr; - bool exceptionOccurred; - - if (isStatic) - { - result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); - } - else if (isSuper) - { - result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); - } - else - { - result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); - } - - if (result != nullptr) - { - auto isString = env.IsInstanceOf(result, JAVA_LANG_STRING); - - Local objectResult; - if (isString) - { - objectResult = ArgConverter::jstringToV8String((jstring) result); - } - else - { - jint javaObjectID = objectManager->GetOrCreateObjectId(result); - objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); - - if (objectResult.IsEmpty()) - { - objectResult = objectManager->CreateJSWrapper(javaObjectID, *returnType, result); - } - } - - args.GetReturnValue().Set(objectResult); - env.DeleteLocalRef(result); - } - else - { - args.GetReturnValue().Set(Null(isolate)); - } - - break; - } - default: - { - assert(false); - break; - } - } - - static uint32_t adjustMemCount = 0; - - if ((++adjustMemCount % 2) == 0) - { - AdjustAmountOfExternalAllocatedMemory(env, isolate); - } + 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; + + return adjustedValue; } -int64_t CallbackHandlers::AdjustAmountOfExternalAllocatedMemory(JEnv& env, Isolate *isolate) -{ - auto runtime = Runtime::GetRuntime(isolate); +Local CallbackHandlers::CreateJSWrapper(Isolate *isolate, jint javaObjectID, + const string &typeName) { + auto runtime = Runtime::GetRuntime(isolate); + auto objectManager = runtime->GetObjectManager(); + + return objectManager->CreateJSWrapper(javaObjectID, typeName); +} + +// TODO: Pete: ~~1~~ - change 2 +jobjectArray CallbackHandlers::GetImplementedInterfaces(JEnv &env, const Local &implementationObject) { + if (implementationObject.IsEmpty()) { + return JavaObjectArrayCache::GetJavaStringArray(0); + } + + vector interfacesToImplement; + auto propNames = implementationObject->GetOwnPropertyNames(); + for (int i = 0; i < propNames->Length(); i++) { + auto name = propNames->Get(i).As(); + auto prop = implementationObject->Get(name); + + bool arrFound = !prop.IsEmpty() && prop->IsArray(); + + if (arrFound) { + v8::String::Utf8Value propName(name); + // convert it to string + std::string arrNameC = std::string(*propName); + if (arrNameC == "interfaces") { + auto interfacesArr = prop->ToObject(); + + auto isolate = Isolate::GetCurrent(); + int length = interfacesArr->Get(v8::String::NewFromUtf8(isolate, "length"))->ToObject()->Uint32Value(); + + if(length > 0) { + for (int i = 0; i < length; i++) { + auto element = interfacesArr->Get(i); + + if(element->IsFunction()) { + auto node = MetadataNode::GetTypeMetadataName(isolate, element); + + replaceAll(node, std::string("/"), std::string(".")); - int64_t changeInBytes = env.CallLongMethod(runtime->GetJavaRuntime(), GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID); + jstring value = env.NewStringUTF(node.c_str()); + interfacesToImplement.push_back(value); + } + } + } + } + } + } - int64_t adjustedValue = (changeInBytes != 0) - ? isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes) - : - 0; + int interfacesCount = interfacesToImplement.size(); - return adjustedValue; + jobjectArray implementedInterfaces = JavaObjectArrayCache::GetJavaStringArray(interfacesCount); + for (int i = 0; i < interfacesCount; i++) { + env.SetObjectArrayElement(implementedInterfaces, i, interfacesToImplement[i]); + } + + for (int i = 0; i < interfacesCount; i++) { + env.DeleteLocalRef(interfacesToImplement[i]); + } + + return implementedInterfaces; } -Local CallbackHandlers::CreateJSWrapper(Isolate *isolate, jint javaObjectID, const string& typeName) -{ - auto runtime = Runtime::GetRuntime(isolate); - auto objectManager = runtime->GetObjectManager(); +jobjectArray CallbackHandlers::GetMethodOverrides(JEnv &env, const Local &implementationObject) { + if (implementationObject.IsEmpty()) { + return JavaObjectArrayCache::GetJavaStringArray(0); + } + + vector methodNames; + auto propNames = implementationObject->GetOwnPropertyNames(); + for (int i = 0; i < propNames->Length(); i++) { + auto name = propNames->Get(i).As(); + auto method = implementationObject->Get(name); + + bool methodFound = !method.IsEmpty() && method->IsFunction(); - return objectManager->CreateJSWrapper(javaObjectID, typeName); + if (methodFound) { + String::Utf8Value stringValue(name); + jstring value = env.NewStringUTF(*stringValue); + methodNames.push_back(value); + } + } + + int methodCount = methodNames.size(); + + jobjectArray methodOverrides = JavaObjectArrayCache::GetJavaStringArray(methodCount); + for (int i = 0; i < methodCount; i++) { + env.SetObjectArrayElement(methodOverrides, i, methodNames[i]); + } + + for (int i = 0; i < methodCount; i++) { + env.DeleteLocalRef(methodNames[i]); + } + + return methodOverrides; } -//delete the returned local reference after use -jobjectArray CallbackHandlers::GetMethodOverrides(JEnv& env, const Local& implementationObject) -{ - if (implementationObject.IsEmpty()) - { - return JavaObjectArrayCache::GetJavaStringArray(0); - } - - vector < jstring > methodNames; - auto propNames = implementationObject->GetOwnPropertyNames(); - for (int i = 0; i < propNames->Length(); i++) - { - auto name = propNames->Get(i).As(); - auto method = implementationObject->Get(name); - - bool methodFound = !method.IsEmpty() && method->IsFunction(); - - if (methodFound) - { - String::Utf8Value stringValue(name); - jstring value = env.NewStringUTF(*stringValue); - methodNames.push_back(value); - } - } - - int methodCount = methodNames.size(); - - jobjectArray methodOverrides = JavaObjectArrayCache::GetJavaStringArray(methodCount); - for (int i = 0; i < methodCount; i++) - { - env.SetObjectArrayElement(methodOverrides, i, methodNames[i]); - } - - for (int i = 0; i < methodCount; i++) - { - env.DeleteLocalRef(methodNames[i]); - } - - return methodOverrides; +// TODO: Pete: extract in some utils class +void CallbackHandlers::replaceAll(std::string& str, const std::string& from, const std::string& to) { + if(from.empty()) + return; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } } -void CallbackHandlers::LogMethodCallback(const v8::FunctionCallbackInfo& args) -{ - try - { - if ((args.Length() > 0) && args[0]->IsString()) - { - String::Utf8Value message(args[0]->ToString()); - DEBUG_WRITE("%s", *message); - } - } - catch (NativeScriptException& e) - { - e.ReThrowToV8(); - } - catch (std::exception e) { - stringstream ss; - ss << "Error: c++ exception: " << e.what() << endl; - NativeScriptException nsEx(ss.str()); - nsEx.ReThrowToV8(); - } - catch (...) { - NativeScriptException nsEx(std::string("Error: c++ exception!")); - nsEx.ReThrowToV8(); - } +void CallbackHandlers::LogMethodCallback(const v8::FunctionCallbackInfo &args) { + try { + if ((args.Length() > 0) && args[0]->IsString()) { + String::Utf8Value message(args[0]->ToString()); + DEBUG_WRITE("%s", *message); + } + } + catch (NativeScriptException &e) { + e.ReThrowToV8(); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToV8(); + } } -void CallbackHandlers::DumpReferenceTablesMethodCallback(const v8::FunctionCallbackInfo& args) -{ - DumpReferenceTablesMethod(); +void CallbackHandlers::DumpReferenceTablesMethodCallback( + const v8::FunctionCallbackInfo &args) { + DumpReferenceTablesMethod(); } -void CallbackHandlers::DumpReferenceTablesMethod() -{ - try - { - JEnv env; - jclass vmDbgClass = env.FindClass("dalvik/system/VMDebug"); - if (vmDbgClass != nullptr) - { - jmethodID mid = env.GetStaticMethodID(vmDbgClass, "dumpReferenceTables", "()V"); - if (mid != 0) - { - env.CallStaticVoidMethod(vmDbgClass, mid); - } - } - } - catch (NativeScriptException& e) - { - e.ReThrowToV8(); - } - catch (std::exception e) { - stringstream ss; - ss << "Error: c++ exception: " << e.what() << endl; - NativeScriptException nsEx(ss.str()); - nsEx.ReThrowToV8(); - } - catch (...) { - NativeScriptException nsEx(std::string("Error: c++ exception!")); - nsEx.ReThrowToV8(); - } +void CallbackHandlers::DumpReferenceTablesMethod() { + try { + JEnv env; + jclass vmDbgClass = env.FindClass("dalvik/system/VMDebug"); + if (vmDbgClass != nullptr) { + jmethodID mid = env.GetStaticMethodID(vmDbgClass, "dumpReferenceTables", "()V"); + if (mid != 0) { + env.CallStaticVoidMethod(vmDbgClass, mid); + } + } + } + catch (NativeScriptException &e) { + e.ReThrowToV8(); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToV8(); + } } -void CallbackHandlers::EnableVerboseLoggingMethodCallback(const v8::FunctionCallbackInfo& args) -{ - try - { - auto isolate = args.GetIsolate(); - auto runtume = Runtime::GetRuntime(isolate); - tns::LogEnabled = true; - JEnv env; - env.CallVoidMethod(runtume->GetJavaRuntime(), ENABLE_VERBOSE_LOGGING_METHOD_ID); - } - catch (NativeScriptException& e) - { - e.ReThrowToV8(); - } - catch (std::exception e) { - stringstream ss; - ss << "Error: c++ exception: " << e.what() << endl; - NativeScriptException nsEx(ss.str()); - nsEx.ReThrowToV8(); - } - catch (...) { - NativeScriptException nsEx(std::string("Error: c++ exception!")); - nsEx.ReThrowToV8(); - } +void CallbackHandlers::EnableVerboseLoggingMethodCallback( + const v8::FunctionCallbackInfo &args) { + try { + auto isolate = args.GetIsolate(); + auto runtume = Runtime::GetRuntime(isolate); + tns::LogEnabled = true; + JEnv env; + env.CallVoidMethod(runtume->GetJavaRuntime(), ENABLE_VERBOSE_LOGGING_METHOD_ID); + } + catch (NativeScriptException &e) { + e.ReThrowToV8(); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToV8(); + } } -void CallbackHandlers::DisableVerboseLoggingMethodCallback(const v8::FunctionCallbackInfo& args) -{ - try - { - auto isolate = args.GetIsolate(); - auto runtume = Runtime::GetRuntime(isolate); - tns::LogEnabled = false; - JEnv env; - env.CallVoidMethod(runtume->GetJavaRuntime(), DISABLE_VERBOSE_LOGGING_METHOD_ID); - } - catch (NativeScriptException& e) - { - e.ReThrowToV8(); - } - catch (std::exception e) { - stringstream ss; - ss << "Error: c++ exception: " << e.what() << endl; - NativeScriptException nsEx(ss.str()); - nsEx.ReThrowToV8(); - } - catch (...) { - NativeScriptException nsEx(std::string("Error: c++ exception!")); - nsEx.ReThrowToV8(); - } +void CallbackHandlers::DisableVerboseLoggingMethodCallback( + const v8::FunctionCallbackInfo &args) { + try { + auto isolate = args.GetIsolate(); + auto runtume = Runtime::GetRuntime(isolate); + tns::LogEnabled = false; + JEnv env; + env.CallVoidMethod(runtume->GetJavaRuntime(), DISABLE_VERBOSE_LOGGING_METHOD_ID); + } + catch (NativeScriptException &e) { + e.ReThrowToV8(); + } + catch (std::exception e) { + stringstream ss; + ss << "Error: c++ exception: " << e.what() << endl; + NativeScriptException nsEx(ss.str()); + nsEx.ReThrowToV8(); + } + catch (...) { + NativeScriptException nsEx(std::string("Error: c++ exception!")); + nsEx.ReThrowToV8(); + } } -void CallbackHandlers::ExitMethodCallback(const v8::FunctionCallbackInfo& args) -{ - auto msg = ConvertToString(args[0].As()); - DEBUG_WRITE_FATAL("FORCE EXIT: %s", msg.c_str()); - exit(-1); +void CallbackHandlers::ExitMethodCallback(const v8::FunctionCallbackInfo &args) { + auto msg = ConvertToString(args[0].As()); + DEBUG_WRITE_FATAL("FORCE EXIT: %s", msg.c_str()); + exit(-1); } -void CallbackHandlers::CreateGlobalCastFunctions(const Local& globalTemplate) -{ - castFunctions.CreateGlobalCastFunctions(globalTemplate); +void CallbackHandlers::CreateGlobalCastFunctions(const Local &globalTemplate) { + castFunctions.CreateGlobalCastFunctions(globalTemplate); } -vector CallbackHandlers::GetTypeMetadata(const string& name, int index) -{ - JEnv env; +vector CallbackHandlers::GetTypeMetadata(const string &name, int index) { + JEnv env; - string canonicalName = Util::ConvertFromJniToCanonicalName(name); + string canonicalName = Util::ConvertFromJniToCanonicalName(name); - JniLocalRef className(env.NewStringUTF(canonicalName.c_str())); - jint idx = index; + JniLocalRef className(env.NewStringUTF(canonicalName.c_str())); + jint idx = index; - JniLocalRef pubApi(env.CallStaticObjectMethod(RUNTIME_CLASS, GET_TYPE_METADATA, (jstring) className, idx)); + JniLocalRef pubApi( + env.CallStaticObjectMethod(RUNTIME_CLASS, GET_TYPE_METADATA, (jstring) className, idx)); - jsize length = env.GetArrayLength(pubApi); + jsize length = env.GetArrayLength(pubApi); - assert(length > 0); + assert(length > 0); - vector result; + vector result; - for (jsize i = 0; i < length; i++) - { - JniLocalRef s(env.GetObjectArrayElement(pubApi, i)); - const char *pc = env.GetStringUTFChars(s, nullptr); - result.push_back(string(pc)); - env.ReleaseStringUTFChars(s, pc); - } + for (jsize i = 0; i < length; i++) { + JniLocalRef s(env.GetObjectArrayElement(pubApi, i)); + const char *pc = env.GetStringUTFChars(s, nullptr); + result.push_back(string(pc)); + env.ReleaseStringUTFChars(s, pc); + } - return result; + return result; } -Local CallbackHandlers::CallJSMethod(Isolate *isolate, JNIEnv *_env, const Local& jsObject, const string& methodName, jobjectArray args) -{ - SET_PROFILER_FRAME(); - - JEnv env(_env); - Local result; - - auto method = jsObject->Get(ConvertToV8String(methodName)); - - if (method.IsEmpty() || method->IsUndefined()) - { - stringstream ss; - ss << "Cannot find method '" << methodName << "' implementation"; - throw NativeScriptException(ss.str()); - } - else if (!method->IsFunction()) - { - stringstream ss; - ss << "Property '" << methodName << "' is not a function"; - throw NativeScriptException(ss.str()); - } - else - { - EscapableHandleScope handleScope(isolate); - - auto jsMethod = method.As(); - auto jsArgs = ArgConverter::ConvertJavaArgsToJsArgs(isolate, args); - int argc = jsArgs->Length(); - - Local arguments[argc]; - for (int i = 0; i < argc; i++) - { - arguments[i] = jsArgs->Get(i); - } - - DEBUG_WRITE("implementationObject->GetIdentityHash()=%d", jsObject->GetIdentityHash()); - - TryCatch tc; - Local jsResult; - { - SET_PROFILER_FRAME(); - jsResult = jsMethod->Call(jsObject, argc, argc == 0 ? nullptr : arguments); - } - - //TODO: if javaResult is a pure js object create a java object that represents this object in java land - - if (tc.HasCaught()) - { - stringstream ss; - ss << "Calling js method " << methodName << " failed"; - string exceptionMessage = ss.str(); - throw NativeScriptException(tc, ss.str()); - } - - result = handleScope.Escape(jsResult); - } - - return result; +Local CallbackHandlers::CallJSMethod(Isolate *isolate, JNIEnv *_env, + const Local &jsObject, const string &methodName, + jobjectArray args) { + SET_PROFILER_FRAME(); + + JEnv env(_env); + Local result; + + auto method = jsObject->Get(ConvertToV8String(methodName)); + + if (method.IsEmpty() || method->IsUndefined()) { + stringstream ss; + ss << "Cannot find method '" << methodName << "' implementation"; + throw NativeScriptException(ss.str()); + } + else if (!method->IsFunction()) { + stringstream ss; + ss << "Property '" << methodName << "' is not a function"; + throw NativeScriptException(ss.str()); + } + else { + EscapableHandleScope handleScope(isolate); + + auto jsMethod = method.As(); + auto jsArgs = ArgConverter::ConvertJavaArgsToJsArgs(isolate, args); + int argc = jsArgs->Length(); + + Local arguments[argc]; + for (int i = 0; i < argc; i++) { + arguments[i] = jsArgs->Get(i); + } + + DEBUG_WRITE("implementationObject->GetIdentityHash()=%d", jsObject->GetIdentityHash()); + + TryCatch tc; + Local jsResult; + { + SET_PROFILER_FRAME(); + jsResult = jsMethod->Call(jsObject, argc, argc == 0 ? nullptr : arguments); + } + + //TODO: if javaResult is a pure js object create a java object that represents this object in java land + + if (tc.HasCaught()) { + stringstream ss; + ss << "Calling js method " << methodName << " failed"; + string exceptionMessage = ss.str(); + throw NativeScriptException(tc, ss.str()); + } + + result = handleScope.Escape(jsResult); + } + + return result; } -Local CallbackHandlers::FindClass(Isolate *isolate, const string& className) -{ - Local clazz; - JEnv env; - - jclass c = env.FindClass(className); - if (env.ExceptionCheck() == JNI_FALSE) - { - auto runtime = Runtime::GetRuntime(isolate); - auto objectManager = runtime->GetObjectManager(); - - jint javaObjectID = objectManager->GetOrCreateObjectId(c); - clazz = objectManager->GetJsObjectByJavaObject(javaObjectID); - - if (clazz.IsEmpty()) - { - clazz = objectManager->CreateJSWrapper(javaObjectID, "Ljava/lang/Class;", c); - } - } - return clazz; +Local CallbackHandlers::FindClass(Isolate *isolate, const string &className) { + Local clazz; + JEnv env; + + jclass c = env.FindClass(className); + if (env.ExceptionCheck() == JNI_FALSE) { + auto runtime = Runtime::GetRuntime(isolate); + auto objectManager = runtime->GetObjectManager(); + + jint javaObjectID = objectManager->GetOrCreateObjectId(c); + clazz = objectManager->GetJsObjectByJavaObject(javaObjectID); + + if (clazz.IsEmpty()) { + clazz = objectManager->CreateJSWrapper(javaObjectID, "Ljava/lang/Class;", c); + } + } + return clazz; } -int CallbackHandlers::GetArrayLength(Isolate *isolate, const Local& arr) -{ - auto runtime = Runtime::GetRuntime(isolate); - auto objectManager = runtime->GetObjectManager(); +int CallbackHandlers::GetArrayLength(Isolate *isolate, const Local &arr) { + auto runtime = Runtime::GetRuntime(isolate); + auto objectManager = runtime->GetObjectManager(); - JEnv env; + JEnv env; - auto javaArr = objectManager->GetJavaObjectByJsObject(arr); + auto javaArr = objectManager->GetJavaObjectByJsObject(arr); - auto length = env.GetArrayLength(javaArr); + auto length = env.GetArrayLength(javaArr); - return length; + return length; } jclass CallbackHandlers::RUNTIME_CLASS = nullptr; @@ -909,9 +905,9 @@ jmethodID CallbackHandlers::GET_TYPE_METADATA = nullptr; jmethodID CallbackHandlers::ENABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; jmethodID CallbackHandlers::DISABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; jmethodID CallbackHandlers::GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID = nullptr; -MetadataTreeNode* CallbackHandlers::metadataRoot = nullptr; +MetadataTreeNode *CallbackHandlers::metadataRoot = nullptr; NumericCasts CallbackHandlers::castFunctions; ArrayElementAccessor CallbackHandlers::arrayElementAccessor; FieldAccessor CallbackHandlers::fieldAccessor; map CallbackHandlers::s_constructorCache; -map CallbackHandlers::s_classCache; +map CallbackHandlers::s_classCache; diff --git a/runtime/src/main/jni/CallbackHandlers.h b/runtime/src/main/jni/CallbackHandlers.h index 2c90c3ab0..604654503 100644 --- a/runtime/src/main/jni/CallbackHandlers.h +++ b/runtime/src/main/jni/CallbackHandlers.h @@ -74,6 +74,8 @@ namespace tns static jobjectArray GetMethodOverrides(JEnv& env, const v8::Local& implementationObject); + static jobjectArray GetImplementedInterfaces(JEnv &env, const v8::Local &implementationObject); + static std::string LogExceptionStackTrace(const v8::TryCatch& tryCatch); static void EnableVerboseLoggingMethodCallback(const v8::FunctionCallbackInfo& args); @@ -87,6 +89,8 @@ namespace tns { } + static void replaceAll(std::string& str, const std::string& from, const std::string& to); + static int GetCachedConstructorId(JEnv& env, const v8::FunctionCallbackInfo& args, const std::string& fullClassName, jobjectArray javaArgs, jclass javaClass); static int64_t AdjustAmountOfExternalAllocatedMemory(JEnv& env, v8::Isolate *isolate); diff --git a/runtime/src/main/jni/MetadataNode.cpp b/runtime/src/main/jni/MetadataNode.cpp index cef5b32d7..349ad4eae 100644 --- a/runtime/src/main/jni/MetadataNode.cpp +++ b/runtime/src/main/jni/MetadataNode.cpp @@ -133,6 +133,13 @@ string MetadataNode::GetName() return m_name; } +string MetadataNode::GetTypeMetadataName(Isolate *isolate, Local& value) +{ + auto data = GetTypeMetadata(isolate, value.As()); + + return data->name; +} + Local MetadataNode::CreateWrapper(Isolate *isolate) { EscapableHandleScope handle_scope(isolate); @@ -1338,6 +1345,7 @@ void MetadataNode::PackageGetterCallback(Local property, const PropertyCal } } +// TODO: Pete: Why do we check for validity AND!!! assign objects from outside the method? bool MetadataNode::ValidateExtendArguments(const FunctionCallbackInfo& info, string& extendLocation, v8::Local& extendName, Local& implementationObject) { bool extendLocationFound = GetExtendLocation(extendLocation); @@ -1472,6 +1480,7 @@ void MetadataNode::ExtendCallMethodCallback(const v8::FunctionCallbackInfo()); hasDot = strName.find('.') != string::npos; } + if (hasDot) { extendName = info[0].As(); @@ -1484,6 +1493,7 @@ void MetadataNode::ExtendCallMethodCallback(const v8::FunctionCallbackInfo(info.Data().As()->Value()); DEBUG_WRITE("ExtendsCallMethodHandler: called with %s", ConvertToString(extendName).c_str()); diff --git a/runtime/src/main/jni/MetadataNode.h b/runtime/src/main/jni/MetadataNode.h index 935e30321..3e15af8cf 100644 --- a/runtime/src/main/jni/MetadataNode.h +++ b/runtime/src/main/jni/MetadataNode.h @@ -57,6 +57,8 @@ namespace tns static MetadataNode* GetOrCreate(const std::string& className); + static std::string GetTypeMetadataName(v8::Isolate *isolate, v8::Local& value); + private: struct MethodCallbackData; diff --git a/test-app/app/src/main/assets/app/MyActivity.js b/test-app/app/src/main/assets/app/MyActivity.js index 7cca82bc0..a7fffd576 100644 --- a/test-app/app/src/main/assets/app/MyActivity.js +++ b/test-app/app/src/main/assets/app/MyActivity.js @@ -21,6 +21,7 @@ } } */ +__enableVerboseLogging(); var MyActivity = (function (_super) { __extends(MyActivity, _super); function MyActivity() { @@ -28,7 +29,7 @@ var MyActivity = (function (_super) { } MyActivity.prototype.onCreate = function (bundle) { _super.prototype.onCreate.call(this, bundle); - + require("./tests/testsWithContext").run(this); execute(); //run jasmine diff --git a/test-app/app/src/main/assets/app/MyApp.js b/test-app/app/src/main/assets/app/MyApp.js index 4de284a15..930aba4ef 100644 --- a/test-app/app/src/main/assets/app/MyApp.js +++ b/test-app/app/src/main/assets/app/MyApp.js @@ -5,4 +5,6 @@ var MyApp = android.app.Application.extend("com.tns.NativeScriptApplication", { __log("Hello MyApp::onCreate()"); } + + }); \ No newline at end of file From 78f142bf1eb7dc979d157111664239f8ace15592 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 27 Jun 2016 13:57:05 +0300 Subject: [PATCH 2/9] small refactor job --- .../src/main/java/com/tns/bindings/Dump.java | 1 + .../src/main/java/com/tns/ClassResolver.java | 3 ++- runtime/src/main/java/com/tns/DexFactory.java | 3 ++- runtime/src/main/jni/CallbackHandlers.cpp | 17 +++++++---------- runtime/src/main/jni/MetadataNode.cpp | 1 - test-app/app/src/main/assets/app/MyActivity.js | 11 +++++------ test-app/app/src/main/assets/app/MyApp.js | 2 -- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/binding-generator/src/main/java/com/tns/bindings/Dump.java b/binding-generator/src/main/java/com/tns/bindings/Dump.java index 1a94bc18b..0f38730e9 100644 --- a/binding-generator/src/main/java/com/tns/bindings/Dump.java +++ b/binding-generator/src/main/java/com/tns/bindings/Dump.java @@ -927,6 +927,7 @@ private void generateInitializedField(ClassVisitor cv) static final String[] classImplentedInterfaces = new String[] { "Lcom/tns/NativeScriptHashCodeProvider;" }; static final String[] interfaceImplementedInterfaces = new String[] { "Lcom/tns/NativeScriptHashCodeProvider;", "" }; + private ClassVisitor generateClass(ApplicationWriter aw, ClassDescriptor classTo, String classSignature, String tnsClassSignature, HashSet implementedInterfaces) { ClassVisitor cv; diff --git a/runtime/src/main/java/com/tns/ClassResolver.java b/runtime/src/main/java/com/tns/ClassResolver.java index 211e1d902..c916eed5d 100644 --- a/runtime/src/main/java/com/tns/ClassResolver.java +++ b/runtime/src/main/java/com/tns/ClassResolver.java @@ -35,7 +35,8 @@ public Class resolveClass(String fullClassName, DexFactory dexFactory, String if(isInterface) { name = ""; - } else + } + else { name = "0"; } diff --git a/runtime/src/main/java/com/tns/DexFactory.java b/runtime/src/main/java/com/tns/DexFactory.java index 7b8cc3b8e..6e29b4e34 100644 --- a/runtime/src/main/java/com/tns/DexFactory.java +++ b/runtime/src/main/java/com/tns/DexFactory.java @@ -64,7 +64,6 @@ public DexFactory(Logger logger, ClassLoader classLoader, File dexBaseDir, Strin static long totalMultiDexTime = 0; static long totalLoadDexTime = 0; - // TODO: Pete: new param - implementedInterfaces public Class resolveClass(String name, String className, String[] methodOverrides, String[] implementedInterfaces, boolean isInterface) throws ClassNotFoundException, IOException { String fullClassName = className.replace("$", "_"); @@ -189,6 +188,7 @@ public Class findClass(String className) throws ClassNotFoundException { return existingClass; } + return classLoader.loadClass(canonicalName); } @@ -252,6 +252,7 @@ private File getDexFile(String className) throws InvalidClassException { logger.write("Looking for proxy file: " + dexFilePath + " Result: NOT Found. Proxy Gen needed. ClassName: " + className); } + return null; } diff --git a/runtime/src/main/jni/CallbackHandlers.cpp b/runtime/src/main/jni/CallbackHandlers.cpp index 61e62fa7f..6fdab08d3 100644 --- a/runtime/src/main/jni/CallbackHandlers.cpp +++ b/runtime/src/main/jni/CallbackHandlers.cpp @@ -147,21 +147,20 @@ jclass CallbackHandlers::ResolveClass(Isolate *isolate, const string &fullClassn // get needed arguments in order to load binding JniLocalRef javaFullClassName(env.NewStringUTF(fullClassname.c_str())); - // TODO: Pete: Split into getMethodOverrides and getImplementedInterfaces - // TODO: Pete: ~~1~~ jobjectArray methodOverrides = GetMethodOverrides(env, implementationObject); - // TODO: Pete: ~~1~~ - change 1 jobjectArray implementedInterfaces = GetImplementedInterfaces(env, implementationObject); auto runtime = Runtime::GetRuntime(isolate); // create or load generated binding (java class) - // TODO: Pete: pass them interface names as param to ResolveClass in Runtime class - // TODO: Pete: ~~2~~ - change 1 - pass implementedInterfaces to Runtime - JniLocalRef generatedClass( - env.CallObjectMethod(runtime->GetJavaRuntime(), RESOLVE_CLASS_METHOD_ID, - (jstring) javaFullClassName, methodOverrides, implementedInterfaces, isInterface)); + JniLocalRef generatedClass(env.CallObjectMethod(runtime->GetJavaRuntime(), + RESOLVE_CLASS_METHOD_ID, + (jstring) javaFullClassName, + methodOverrides, + implementedInterfaces, + isInterface)); + globalRefToGeneratedClass = static_cast(env.NewGlobalRef(generatedClass)); s_classCache.insert(make_pair(fullClassname, globalRefToGeneratedClass)); @@ -569,7 +568,6 @@ Local CallbackHandlers::CreateJSWrapper(Isolate *isolate, jint javaObjec return objectManager->CreateJSWrapper(javaObjectID, typeName); } -// TODO: Pete: ~~1~~ - change 2 jobjectArray CallbackHandlers::GetImplementedInterfaces(JEnv &env, const Local &implementationObject) { if (implementationObject.IsEmpty()) { return JavaObjectArrayCache::GetJavaStringArray(0); @@ -659,7 +657,6 @@ jobjectArray CallbackHandlers::GetMethodOverrides(JEnv &env, const Local return methodOverrides; } -// TODO: Pete: extract in some utils class void CallbackHandlers::replaceAll(std::string& str, const std::string& from, const std::string& to) { if(from.empty()) return; diff --git a/runtime/src/main/jni/MetadataNode.cpp b/runtime/src/main/jni/MetadataNode.cpp index 349ad4eae..edebd1840 100644 --- a/runtime/src/main/jni/MetadataNode.cpp +++ b/runtime/src/main/jni/MetadataNode.cpp @@ -1345,7 +1345,6 @@ void MetadataNode::PackageGetterCallback(Local property, const PropertyCal } } -// TODO: Pete: Why do we check for validity AND!!! assign objects from outside the method? bool MetadataNode::ValidateExtendArguments(const FunctionCallbackInfo& info, string& extendLocation, v8::Local& extendName, Local& implementationObject) { bool extendLocationFound = GetExtendLocation(extendLocation); diff --git a/test-app/app/src/main/assets/app/MyActivity.js b/test-app/app/src/main/assets/app/MyActivity.js index a7fffd576..e88343a3c 100644 --- a/test-app/app/src/main/assets/app/MyActivity.js +++ b/test-app/app/src/main/assets/app/MyActivity.js @@ -21,7 +21,6 @@ } } */ -__enableVerboseLogging(); var MyActivity = (function (_super) { __extends(MyActivity, _super); function MyActivity() { @@ -36,26 +35,26 @@ var MyActivity = (function (_super) { var layout = new android.widget.LinearLayout(this); layout.setOrientation(1); this.setContentView(layout); - + var textView = new android.widget.TextView(this); 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() { button.setBackgroundColor(colors[taps % colors.length]); - + taps++; }})); }; diff --git a/test-app/app/src/main/assets/app/MyApp.js b/test-app/app/src/main/assets/app/MyApp.js index 930aba4ef..4de284a15 100644 --- a/test-app/app/src/main/assets/app/MyApp.js +++ b/test-app/app/src/main/assets/app/MyApp.js @@ -5,6 +5,4 @@ var MyApp = android.app.Application.extend("com.tns.NativeScriptApplication", { __log("Hello MyApp::onCreate()"); } - - }); \ No newline at end of file From 2f95f2bf811cf36fcf5a640e7234f09fc9a95240 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 27 Jun 2016 15:31:23 +0300 Subject: [PATCH 3/9] fix indentation --- binding-generator/src/main/java/com/tns/bindings/Dump.java | 2 +- runtime/src/main/java/com/tns/ClassResolver.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/binding-generator/src/main/java/com/tns/bindings/Dump.java b/binding-generator/src/main/java/com/tns/bindings/Dump.java index 0f38730e9..849b6af70 100644 --- a/binding-generator/src/main/java/com/tns/bindings/Dump.java +++ b/binding-generator/src/main/java/com/tns/bindings/Dump.java @@ -611,7 +611,7 @@ private void generateMethod(ClassVisitor cv, ClassDescriptor classTo, MethodDesc if (ProxyGenerator.IsLogEnabled) { Log.d("Generator", "generatingMethod " + method.getName()); } - + //TODO: handle checked exceptions String methodDexSignature = getDexMethodDescriptor(method); String[] exceptions = new String[0]; diff --git a/runtime/src/main/java/com/tns/ClassResolver.java b/runtime/src/main/java/com/tns/ClassResolver.java index c916eed5d..6cce0514f 100644 --- a/runtime/src/main/java/com/tns/ClassResolver.java +++ b/runtime/src/main/java/com/tns/ClassResolver.java @@ -33,11 +33,11 @@ public Class resolveClass(String fullClassName, DexFactory dexFactory, String if (name == null || name == "") { if(isInterface) - { + { name = ""; } else - { + { name = "0"; } } From 20f0fdbf6b097dcb6c40dcf8e5acd333753a6781 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Mon, 27 Jun 2016 16:23:46 +0300 Subject: [PATCH 4/9] move replaceAll string util method to Util --- runtime/src/main/jni/CallbackHandlers.cpp | 12 +----------- runtime/src/main/jni/CallbackHandlers.h | 2 -- runtime/src/main/jni/Util.cpp | 10 ++++++++++ runtime/src/main/jni/Util.h | 2 ++ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/runtime/src/main/jni/CallbackHandlers.cpp b/runtime/src/main/jni/CallbackHandlers.cpp index 6fdab08d3..6c810730e 100644 --- a/runtime/src/main/jni/CallbackHandlers.cpp +++ b/runtime/src/main/jni/CallbackHandlers.cpp @@ -598,7 +598,7 @@ jobjectArray CallbackHandlers::GetImplementedInterfaces(JEnv &env, const LocalIsFunction()) { auto node = MetadataNode::GetTypeMetadataName(isolate, element); - replaceAll(node, std::string("/"), std::string(".")); + Util::ReplaceAll(node, std::string("/"), std::string(".")); jstring value = env.NewStringUTF(node.c_str()); interfacesToImplement.push_back(value); @@ -657,16 +657,6 @@ jobjectArray CallbackHandlers::GetMethodOverrides(JEnv &env, const Local return methodOverrides; } -void CallbackHandlers::replaceAll(std::string& str, const std::string& from, const std::string& to) { - if(from.empty()) - return; - size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' - } -} - void CallbackHandlers::LogMethodCallback(const v8::FunctionCallbackInfo &args) { try { if ((args.Length() > 0) && args[0]->IsString()) { diff --git a/runtime/src/main/jni/CallbackHandlers.h b/runtime/src/main/jni/CallbackHandlers.h index 604654503..f59f376d1 100644 --- a/runtime/src/main/jni/CallbackHandlers.h +++ b/runtime/src/main/jni/CallbackHandlers.h @@ -89,8 +89,6 @@ namespace tns { } - static void replaceAll(std::string& str, const std::string& from, const std::string& to); - static int GetCachedConstructorId(JEnv& env, const v8::FunctionCallbackInfo& args, const std::string& fullClassName, jobjectArray javaArgs, jclass javaClass); static int64_t AdjustAmountOfExternalAllocatedMemory(JEnv& env, v8::Isolate *isolate); diff --git a/runtime/src/main/jni/Util.cpp b/runtime/src/main/jni/Util.cpp index 56551d8b7..57de40597 100644 --- a/runtime/src/main/jni/Util.cpp +++ b/runtime/src/main/jni/Util.cpp @@ -109,3 +109,13 @@ string Util::ConvertFromCanonicalToJniName(const std::string& name) replace(converted.begin(), converted.end(), '.', '/'); return converted; } + +void Util::ReplaceAll(std::string& str, const std::string& from, const std::string& to) { + if(from.empty()) + return; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } +} \ No newline at end of file diff --git a/runtime/src/main/jni/Util.h b/runtime/src/main/jni/Util.h index f36979589..a6beee287 100644 --- a/runtime/src/main/jni/Util.h +++ b/runtime/src/main/jni/Util.h @@ -19,6 +19,8 @@ namespace tns static std::string ConvertFromJniToCanonicalName(const std::string& name); static std::string ConvertFromCanonicalToJniName(const std::string& name); + + static void ReplaceAll(std::string& str, const std::string& from, const std::string& to); }; } From 4a0ec6599343a100cb2cb4ad7f18f6a12e54fed5 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Fri, 8 Jul 2016 11:13:31 +0300 Subject: [PATCH 5/9] add Interfaces decorator syntax support in ts_helpers --- test-app/app/src/main/assets/internal/ts_helpers.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test-app/app/src/main/assets/internal/ts_helpers.js b/test-app/app/src/main/assets/internal/ts_helpers.js index 526a9a65a..2175f563f 100644 --- a/test-app/app/src/main/assets/internal/ts_helpers.js +++ b/test-app/app/src/main/assets/internal/ts_helpers.js @@ -107,9 +107,18 @@ }; } + function Interfaces(interfacesArr) { + return function (target) { + if(interfacesArr instanceof Array) { + // attach interfaces: [] to the object + target.prototype.interfaces = interfacesArr; + } + } + } global.__native = __native; global.__extends = __extends; global.__decorate = __decorate; global.JavaProxy = JavaProxy; + global.Interfaces = Interfaces; })() \ No newline at end of file From aaf3dd0c3e5f0660c0fddcabf7395e3ec50d703e Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Fri, 8 Jul 2016 14:42:56 +0300 Subject: [PATCH 6/9] add runtime binding generator for the new feature --- .../tests/testsForRuntimeBindingGenerator.js | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) diff --git a/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js b/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js index fe39f2723..eb95d9608 100644 --- a/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js +++ b/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js @@ -71,4 +71,208 @@ describe("Tests for runtime binding generator", function () { expect(true).toBe(false); } }); + + it("When_generating_a_class_that_implements_interfaces_javascript", function() { + + __log("TEST: When_generating_a_class_that_implements_interfaces_javascript"); + + var MyNormalObject = java.lang.Object.extend({ + greet: function(greetings) { + return "Hello, " + greetings; + }, + unpack: function() { + + }, + properties: function() { + + }, + toString: function() { + + }, + formatTo: function() { + + }, + update: function() { + + }, + addPropertyChangeListener: function() { + + }, + pack: function() { + + }, + removePropertyChangeListener: function() { + + }, + interfaces: [java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer] + }) + + var myNormalObject = new MyNormalObject(); + + var isInstanceOf = myNormalObject instanceof java.lang.Object; + + expect(isInstanceOf).toBe(true); + + var clazz = myNormalObject.getClass(); + + var interfaces = clazz.getInterfaces(); + + var expectedInterfaces = ["java.util.jar.Pack200$Packer", "java.util.Formattable", "java.util.Observer", "java.util.jar.Pack200$Unpacker", "com.tns.NativeScriptHashCodeProvider"]; + + expect(interfaces.length).toBe(5); + + for(var i = 0; i < interfaces.length; i++) { + var interfaceName = interfaces[i].getName().toString(); + + __log(interfaceName); + + if(interfaceName.length > 0) { + expect(expectedInterfaces.indexOf(interfaceName)).not.toBe(-1); + } + } + }); + + it("When_generating_a_class_that_implements_interfaces_typescript", function() { + + __log("TEST: When_generating_a_class_that_implements_interfaces_typescript"); + + var MyObject = (function (_super) { + __extends(MyObject, _super); + function MyObject() { + _super.apply(this, arguments); + return global.__native(this); + } + MyObject.prototype.greet = function (greeting) { + return "Hello, " + greeting; + }; + MyObject.prototype.unpack = function () { + }; + MyObject.prototype.properties = function () { + }; + MyObject.prototype.toString = function () { + }; + MyObject.prototype.formatTo = function () { + }; + MyObject.prototype.update = function () { + }; + MyObject.prototype.addPropertyChangeListener = function () { + }; + MyObject.prototype.pack = function () { + }; + MyObject.prototype.removePropertyChangeListener = function () { + }; + MyObject = __decorate([ + Interfaces([java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer]) + ], MyObject); + return MyObject; + }(java.lang.Object)); + + var myObject = new MyObject(); + + var isInstanceOf = myObject instanceof java.lang.Object; + + expect(isInstanceOf).toBe(true); + + var clazz = myObject.getClass(); + + var interfaces = clazz.getInterfaces(); + + var expectedInterfaces = ["java.util.jar.Pack200$Packer", "java.util.Formattable", "java.util.Observer", "java.util.jar.Pack200$Unpacker", "com.tns.NativeScriptHashCodeProvider"]; + + expect(interfaces.length).toBe(5); + + for(var i = 0; i < interfaces.length; i++) { + var interfaceName = interfaces[i].getName().toString(); + + __log(interfaceName); + + if(interfaceName.length > 0) { + expect(expectedInterfaces.indexOf(interfaceName)).not.toBe(-1); + } + } + }); + + it("Test_correct_behavior_when_two_or_more_classes_implement_the_same_interfaces", function() { + + __log("TEST: Test_correct_behavior_when_two_or_more_classes_implement_the_same_interfaces"); + + var resultFromMyObject1UnpackCall = "", + resultFromMyObject2UnpackCall = ""; + + var MyObject1 = (function (_super) { + __extends(MyObject1, _super); + function MyObject1() { + _super.apply(this, arguments); + return global.__native(this); + } + MyObject1.prototype.greet = function (greeting) { + return "Hello, " + greeting; + }; + MyObject1.prototype.unpack = function () { + resultFromMyObject1UnpackCall = "Object1"; + __log("From MyObject1"); + }; + MyObject1.prototype.properties = function () { + }; + MyObject1.prototype.toString = function () { + }; + MyObject1.prototype.formatTo = function () { + }; + MyObject1.prototype.update = function () { + }; + MyObject1.prototype.addPropertyChangeListener = function () { + }; + MyObject1.prototype.pack = function () { + }; + MyObject1.prototype.removePropertyChangeListener = function () { + }; + MyObject1 = __decorate([ + Interfaces([java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer]) + ], MyObject1); + return MyObject1; + }(java.lang.Object)); + + var MyObject2 = (function (_super) { + __extends(MyObject2, _super); + function MyObject2() { + _super.apply(this, arguments); + return global.__native(this); + } + MyObject2.prototype.greet = function (greeting) { + return "Hello, " + greeting; + }; + MyObject2.prototype.unpack = function () { + resultFromMyObject2UnpackCall = "Object2"; + __log("From MyObject2"); + }; + MyObject2.prototype.properties = function () { + }; + MyObject2.prototype.toString = function () { + }; + MyObject2.prototype.formatTo = function () { + }; + MyObject2.prototype.update = function () { + }; + MyObject2.prototype.addPropertyChangeListener = function () { + }; + MyObject2.prototype.pack = function () { + }; + MyObject2.prototype.removePropertyChangeListener = function () { + }; + MyObject2 = __decorate([ + Interfaces([java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer]) + ], MyObject2); + return MyObject2; + }(java.lang.Object)); + + + var myObj1 = new MyObject1(); + myObj1.unpack(); + + var myObj2 = new MyObject2(); + myObj2.unpack(); + + expect(resultFromMyObject1UnpackCall).toBe("Object1"); + expect(resultFromMyObject2UnpackCall).toBe("Object2"); + }); }); \ No newline at end of file From 17d3e8a7c3cda45f7fb56cc1ffc2225a77cd858a Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Fri, 8 Jul 2016 15:07:07 +0300 Subject: [PATCH 7/9] fix failing test --- .../tests/testsForRuntimeBindingGenerator.js | 116 +++--------------- 1 file changed, 16 insertions(+), 100 deletions(-) diff --git a/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js b/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js index eb95d9608..44af5db95 100644 --- a/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js +++ b/test-app/app/src/main/assets/app/tests/testsForRuntimeBindingGenerator.js @@ -136,38 +136,38 @@ describe("Tests for runtime binding generator", function () { __log("TEST: When_generating_a_class_that_implements_interfaces_typescript"); - var MyObject = (function (_super) { - __extends(MyObject, _super); - function MyObject() { + var MyTSTranspiledObjectThatExtendsInterfaces = (function (_super) { + __extends(MyTSTranspiledObjectThatExtendsInterfaces, _super); + function MyTSTranspiledObjectThatExtendsInterfaces() { _super.apply(this, arguments); return global.__native(this); } - MyObject.prototype.greet = function (greeting) { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.greet = function (greeting) { return "Hello, " + greeting; }; - MyObject.prototype.unpack = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.unpack = function () { }; - MyObject.prototype.properties = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.properties = function () { }; - MyObject.prototype.toString = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.toString = function () { }; - MyObject.prototype.formatTo = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.formatTo = function () { }; - MyObject.prototype.update = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.update = function () { }; - MyObject.prototype.addPropertyChangeListener = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.addPropertyChangeListener = function () { }; - MyObject.prototype.pack = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.pack = function () { }; - MyObject.prototype.removePropertyChangeListener = function () { + MyTSTranspiledObjectThatExtendsInterfaces.prototype.removePropertyChangeListener = function () { }; - MyObject = __decorate([ + MyTSTranspiledObjectThatExtendsInterfaces = __decorate([ Interfaces([java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer]) - ], MyObject); - return MyObject; + ], MyTSTranspiledObjectThatExtendsInterfaces); + return MyTSTranspiledObjectThatExtendsInterfaces; }(java.lang.Object)); - var myObject = new MyObject(); + var myObject = new MyTSTranspiledObjectThatExtendsInterfaces(); var isInstanceOf = myObject instanceof java.lang.Object; @@ -190,89 +190,5 @@ describe("Tests for runtime binding generator", function () { expect(expectedInterfaces.indexOf(interfaceName)).not.toBe(-1); } } - }); - - it("Test_correct_behavior_when_two_or_more_classes_implement_the_same_interfaces", function() { - - __log("TEST: Test_correct_behavior_when_two_or_more_classes_implement_the_same_interfaces"); - - var resultFromMyObject1UnpackCall = "", - resultFromMyObject2UnpackCall = ""; - - var MyObject1 = (function (_super) { - __extends(MyObject1, _super); - function MyObject1() { - _super.apply(this, arguments); - return global.__native(this); - } - MyObject1.prototype.greet = function (greeting) { - return "Hello, " + greeting; - }; - MyObject1.prototype.unpack = function () { - resultFromMyObject1UnpackCall = "Object1"; - __log("From MyObject1"); - }; - MyObject1.prototype.properties = function () { - }; - MyObject1.prototype.toString = function () { - }; - MyObject1.prototype.formatTo = function () { - }; - MyObject1.prototype.update = function () { - }; - MyObject1.prototype.addPropertyChangeListener = function () { - }; - MyObject1.prototype.pack = function () { - }; - MyObject1.prototype.removePropertyChangeListener = function () { - }; - MyObject1 = __decorate([ - Interfaces([java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer]) - ], MyObject1); - return MyObject1; - }(java.lang.Object)); - - var MyObject2 = (function (_super) { - __extends(MyObject2, _super); - function MyObject2() { - _super.apply(this, arguments); - return global.__native(this); - } - MyObject2.prototype.greet = function (greeting) { - return "Hello, " + greeting; - }; - MyObject2.prototype.unpack = function () { - resultFromMyObject2UnpackCall = "Object2"; - __log("From MyObject2"); - }; - MyObject2.prototype.properties = function () { - }; - MyObject2.prototype.toString = function () { - }; - MyObject2.prototype.formatTo = function () { - }; - MyObject2.prototype.update = function () { - }; - MyObject2.prototype.addPropertyChangeListener = function () { - }; - MyObject2.prototype.pack = function () { - }; - MyObject2.prototype.removePropertyChangeListener = function () { - }; - MyObject2 = __decorate([ - Interfaces([java.util.jar.Pack200.Unpacker, java.util.Formattable, java.util.Observer, java.util.jar.Pack200.Packer]) - ], MyObject2); - return MyObject2; - }(java.lang.Object)); - - - var myObj1 = new MyObject1(); - myObj1.unpack(); - - var myObj2 = new MyObject2(); - myObj2.unpack(); - - expect(resultFromMyObject1UnpackCall).toBe("Object1"); - expect(resultFromMyObject2UnpackCall).toBe("Object2"); }); }); \ No newline at end of file From ead0e8b7f340c43eb6b5a9cd0c84e34f40025411 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Fri, 8 Jul 2016 15:08:18 +0300 Subject: [PATCH 8/9] add updated ts_helpers to project-template --- .../src/main/assets/internal/ts_helpers.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build-artifacts/project-template-gradle/src/main/assets/internal/ts_helpers.js b/build-artifacts/project-template-gradle/src/main/assets/internal/ts_helpers.js index 47dfbb123..2175f563f 100644 --- a/build-artifacts/project-template-gradle/src/main/assets/internal/ts_helpers.js +++ b/build-artifacts/project-template-gradle/src/main/assets/internal/ts_helpers.js @@ -90,6 +90,7 @@ __extends_ts(Child, Parent); + if (Parent.extend) { Child.__isPrototypeImplementationObject = true; Child.__proto__ = Parent; @@ -105,9 +106,19 @@ return extended; }; } + + function Interfaces(interfacesArr) { + return function (target) { + if(interfacesArr instanceof Array) { + // attach interfaces: [] to the object + target.prototype.interfaces = interfacesArr; + } + } + } global.__native = __native; global.__extends = __extends; global.__decorate = __decorate; global.JavaProxy = JavaProxy; + global.Interfaces = Interfaces; })() \ No newline at end of file From 1be4f57831981b8c203ec1c1b60152d20ff79a87 Mon Sep 17 00:00:00 2001 From: Peter Kanev Date: Thu, 21 Jul 2016 13:55:40 +0300 Subject: [PATCH 9/9] fix bug --- runtime/src/main/java/com/tns/DexFactory.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/src/main/java/com/tns/DexFactory.java b/runtime/src/main/java/com/tns/DexFactory.java index 6e29b4e34..9a53a6abb 100644 --- a/runtime/src/main/java/com/tns/DexFactory.java +++ b/runtime/src/main/java/com/tns/DexFactory.java @@ -270,18 +270,18 @@ private String generateDex(String proxyName, String className, String[] methodOv { String methodOverride = methodOverrides[i]; - if (implementedInterfaces.length > 0) { - for(int j = 0; j < implementedInterfaces.length; j++) { - if(!implementedInterfaces[j].isEmpty()) { - implementedInterfacesSet.add(new ClassInfo(Class.forName(implementedInterfaces[j]))); - } - } - } - methodOverridesSet.add(methodOverride); } } + if (implementedInterfaces.length > 0) { + for(int j = 0; j < implementedInterfaces.length; j++) { + if(!implementedInterfaces[j].isEmpty()) { + implementedInterfacesSet.add(new ClassInfo(Class.forName(implementedInterfaces[j]))); + } + } + } + return proxyGenerator.generateProxy(proxyName, new ClassInfo(classToProxy) , methodOverridesSet, implementedInterfacesSet, isInterface); }