diff --git a/TotalCrossSDK/src/main/java/totalcross/json/JSONFactory.java b/TotalCrossSDK/src/main/java/totalcross/json/JSONFactory.java
index 3d62f5a914..ca817cb505 100644
--- a/TotalCrossSDK/src/main/java/totalcross/json/JSONFactory.java
+++ b/TotalCrossSDK/src/main/java/totalcross/json/JSONFactory.java
@@ -10,48 +10,55 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
/**
- The JSONFactory class helps converting json objects into Java objects, using reflection.
-
- Some examples:
-
-
- class Car
- {
- private int id;
- private String description;
-
- public int getId()
- {
- return id;
- }
- public void setId(int id)
- {
- this.id = id;
- }
- public String getDescription()
- {
- return description;
- }
- public void setDescription(String description)
- {
- this.description = description;
- }
- }
-
-
- You can retrieve a new Car object using:
-
- Car cc = JSONFactory.parse("{\"carro\":{\"id\":-1,\"descricao\":\"GOL\"}}", Carro.class);
-
-
- You may also retrieve a list or an array. See the JSONSample in the TotalCrossAPI.
+ * The JSONFactory class helps converting json objects into Java objects, using
+ * reflection.
+ *
+ * Some examples:
+ *
+ *
+ * class Car {
+ * private int id;
+ * private String description;
+ *
+ * public int getId() {
+ * return id;
+ * }
+ *
+ * public void setId(int id) {
+ * this.id = id;
+ * }
+ *
+ * public String getDescription() {
+ * return description;
+ * }
+ *
+ * public void setDescription(String description) {
+ * this.description = description;
+ * }
+ * }
+ *
+ *
+ * You can retrieve a new Car object using:
+ *
+ *
+ * Car cc = JSONFactory.parse("{\"carro\":{\"id\":-1,\"descricao\":\"GOL\"}}", Carro.class);
+ *
+ *
+ * You may also retrieve a list or an array. See the JSONSample in the
+ * TotalCrossAPI.
*/
public class JSONFactory {
- public static List asList(String json, Class classOfT) throws InstantiationException,
- IllegalAccessException, IllegalArgumentException, InvocationTargetException, JSONException, ArrayIndexOutOfBoundsException, NoSuchMethodException, SecurityException {
+ private static Map, Map> classes = new HashMap<>();
+
+ public static List asList(String json, Class classOfT)
+ throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
+ JSONException, ArrayIndexOutOfBoundsException, NoSuchMethodException, SecurityException {
List list = new ArrayList();
try {
JSONArray jsonArray = new JSONArray(json);
@@ -88,13 +95,15 @@ public static T parse(String json, Class classOfT) throws InstantiationEx
return parse(new JSONObject(json), classOfT);
}
- public static T parse(JSONArray jsonArray, Class classOfT) throws InstantiationException,
- IllegalAccessException, IllegalArgumentException, InvocationTargetException, JSONException, ArrayIndexOutOfBoundsException, NoSuchMethodException, SecurityException {
- return parse(null, jsonArray, classOfT);
+ public static T parse(JSONArray jsonArray, Class classOfT)
+ throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
+ JSONException, ArrayIndexOutOfBoundsException, NoSuchMethodException, SecurityException {
+ return parse(null, jsonArray, classOfT);
}
-
- private static T parse(Object outerObject, JSONArray jsonArray, Class classOfT) throws InstantiationException,
- IllegalAccessException, IllegalArgumentException, InvocationTargetException, JSONException, ArrayIndexOutOfBoundsException, NoSuchMethodException, SecurityException {
+
+ private static T parse(Object outerObject, JSONArray jsonArray, Class classOfT)
+ throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
+ JSONException, ArrayIndexOutOfBoundsException, NoSuchMethodException, SecurityException {
if (classOfT.isArray()) {
T array;
try {
@@ -110,95 +119,112 @@ private static T parse(Object outerObject, JSONArray jsonArray, Class cla
}
return parse(outerObject, jsonArray, classOfT);
}
-
- public static T parse(JSONObject jsonObject, Class classOfT) throws InstantiationException,
- IllegalAccessException, IllegalArgumentException, InvocationTargetException, JSONException, NoSuchMethodException, SecurityException {
- return parse(null, jsonObject, classOfT);
+
+ public static T parse(JSONObject jsonObject, Class classOfT)
+ throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
+ JSONException, NoSuchMethodException, SecurityException {
+ return parse(null, jsonObject, classOfT);
}
- private static T parse(Object outerObject, JSONObject jsonObject, Class classOfT) throws InstantiationException,
- IllegalAccessException, IllegalArgumentException, InvocationTargetException, JSONException, NoSuchMethodException, SecurityException {
+ private static T parse(Object outerObject, JSONObject jsonObject, Class classOfT)
+ throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
+ JSONException, NoSuchMethodException, SecurityException {
if (classOfT.isArray()) {
throw new IllegalArgumentException();
}
T object = null;
try {
- object = classOfT.newInstance();
+ object = classOfT.newInstance();
} catch (InstantiationException e) {
- if (outerObject != null && classOfT.getName().indexOf(outerObject.getClass().getName()) != -1) {
- Constructor constructorOfT = classOfT.getDeclaredConstructor(outerObject.getClass());
- if (constructorOfT != null) {
- object = constructorOfT.newInstance(outerObject);
- }
+ if (outerObject != null && classOfT.getName().indexOf(outerObject.getClass().getName()) != -1) {
+ Constructor constructorOfT = classOfT.getDeclaredConstructor(outerObject.getClass());
+ if (constructorOfT != null) {
+ object = constructorOfT.newInstance(outerObject);
}
- if (object == null) {
- throw e;
+ }
+ if (object == null) {
+ throw e;
+ }
+ }
+
+ Map methodCache = classes.get(classOfT);
+ if (methodCache == null) {
+ methodCache = mapClassMethodsToJsonNames(classOfT);
+ classes.put(classOfT, methodCache);
+ }
+
+ Iterator keyIterator = jsonObject.keys();
+ while (keyIterator.hasNext()) {
+ final String name = keyIterator.next();
+ Method method = methodCache.get(name);
+ if (method == null || jsonObject.isNull(name)) {
+ continue;
+ }
+
+ Class> parameterType = method.getParameterTypes()[0];
+ if (parameterType.isPrimitive()) {
+ if (parameterType.isAssignableFrom(boolean.class)) {
+ method.invoke(object, jsonObject.getBoolean(name));
+ } else if (parameterType.isAssignableFrom(int.class)) {
+ method.invoke(object, jsonObject.getInt(name));
+ } else if (parameterType.isAssignableFrom(long.class)) {
+ method.invoke(object, jsonObject.getLong(name));
+ } else if (parameterType.isAssignableFrom(double.class)) {
+ method.invoke(object, jsonObject.getDouble(name));
}
+ } else if (parameterType.isAssignableFrom(String.class)) {
+ method.invoke(object, jsonObject.getString(name));
+ } else if (parameterType.isAssignableFrom(Double.class)) {
+ method.invoke(object, jsonObject.getDouble(name));
+ } else if (parameterType.isAssignableFrom(Integer.class)) {
+ method.invoke(object, jsonObject.getInt(name));
+ } else if (parameterType.isAssignableFrom(Long.class)) {
+ method.invoke(object, jsonObject.getLong(name));
+ } else if (parameterType.isAssignableFrom(Boolean.class)) {
+ method.invoke(object, jsonObject.getBoolean(name));
+ } else if (parameterType.isArray()) {
+ method.invoke(object, parse(object, jsonObject.getJSONArray(name), parameterType));
+ } else {
+ method.invoke(object, parse(object, jsonObject.getJSONObject(name), parameterType));
+ }
}
+ return object;
+ }
+
+ private static Map mapClassMethodsToJsonNames(Class> classOfT) {
+ final Map methodCache = new HashMap<>();
Method[] methods = classOfT.getMethods();
for (Method method : methods) {
String methodName = method.getName();
Class>[] paramTypes = method.getParameterTypes();
if (paramTypes != null && paramTypes.length == 1 && methodName.length() > 3 && methodName.startsWith("set")) {
final String originalName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
- String name = null;
- // look for the field name in the json based on the method name
- if (jsonObject.has(originalName)) {
- name = originalName;
- } else if (!jsonObject.has(name = originalName.toLowerCase())) {
- // not found as-is or lowercased? try replacing camel case with underscore
- /*
- * originally done using regex, but totalcross implementation has some bugs and
- * until they are fixed this is done looping through the characters
- * originalName.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();
- */
- StringBuilder sb = new StringBuilder();
- boolean lastWasUnderscored = false;
- for(int i = 0 ; i < originalName.length() ; i++) {
- char c = originalName.charAt(i);
- if (!lastWasUnderscored && Character.isUpperCase(c)) {
- lastWasUnderscored = true;
- sb.append('_');
- } else {
- lastWasUnderscored = false;
- }
- sb.append(Character.toLowerCase(c));
- }
- final String underscoredName = sb.toString();
- if (jsonObject.has(underscoredName)) {
- name = underscoredName;
- }
- }
- if (!jsonObject.isNull(name)) {
- Class> parameterType = method.getParameterTypes()[0];
- if (parameterType.isPrimitive()) {
- if (parameterType.isAssignableFrom(boolean.class)) {
- method.invoke(object, jsonObject.getBoolean(name));
- } else if (parameterType.isAssignableFrom(int.class)) {
- method.invoke(object, jsonObject.getInt(name));
- } else if (parameterType.isAssignableFrom(long.class)) {
- method.invoke(object, jsonObject.getLong(name));
- } else if (parameterType.isAssignableFrom(double.class)) {
- method.invoke(object, jsonObject.getDouble(name));
- }
- } else if (parameterType.isAssignableFrom(String.class)) {
- method.invoke(object, jsonObject.getString(name));
- } else if (parameterType.isAssignableFrom(Double.class)) {
- method.invoke(object, jsonObject.getDouble(name));
- } else if (parameterType.isAssignableFrom(Integer.class)) {
- method.invoke(object, jsonObject.getInt(name));
- } else if (parameterType.isAssignableFrom(Long.class)) {
- method.invoke(object, jsonObject.getLong(name));
- } else if (parameterType.isAssignableFrom(Boolean.class)) {
- method.invoke(object, jsonObject.getBoolean(name));
- } else if (parameterType.isArray()) {
- method.invoke(object, parse(object, jsonObject.getJSONArray(name), parameterType));
+ // name as-is
+ methodCache.put(originalName, method);
+ // lowercased name
+ methodCache.put(originalName.toLowerCase(), method);
+ // not found as-is or lowercased? try replacing camel case with underscore
+ /*
+ * originally done using regex, but totalcross implementation has some bugs and
+ * until they are fixed this is done looping through the characters
+ * originalName.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();
+ */
+ StringBuilder sb = new StringBuilder();
+ boolean lastWasUnderscored = false;
+ for (int i = 0; i < originalName.length(); i++) {
+ char c = originalName.charAt(i);
+ if (!lastWasUnderscored && Character.isUpperCase(c)) {
+ lastWasUnderscored = true;
+ sb.append('_');
} else {
- method.invoke(object, parse(object, jsonObject.getJSONObject(name), parameterType));
+ lastWasUnderscored = false;
}
+ sb.append(Character.toLowerCase(c));
}
+ final String underscoredName = sb.toString();
+ methodCache.put(underscoredName, method);
}
}
- return object;
+ return methodCache;
}
}
diff --git a/TotalCrossSDK/src/main/java/totalcross/lang/Class4D.java b/TotalCrossSDK/src/main/java/totalcross/lang/Class4D.java
index 99f547e314..34b1528b64 100644
--- a/TotalCrossSDK/src/main/java/totalcross/lang/Class4D.java
+++ b/TotalCrossSDK/src/main/java/totalcross/lang/Class4D.java
@@ -32,6 +32,7 @@ public final class Class4D {
Object nativeStruct; // TClass
String targetName; // java.lang.String
String ncached, cached;
+ Method[] methods;
/** The TotalCross deployer can find classes that are instantiated using Class.forName if, and only if, they are
* String constants. If you build the className dynamically, then you must include the file passing it to the tc.Deploy
diff --git a/TotalCrossVM/src/nm/instancefields.h b/TotalCrossVM/src/nm/instancefields.h
index 39bfdb8b98..1f5a8641df 100644
--- a/TotalCrossVM/src/nm/instancefields.h
+++ b/TotalCrossVM/src/nm/instancefields.h
@@ -47,10 +47,6 @@
#define StringBuffer_charsStart(o) ((JCharP)(ARRAYOBJ_START(StringBuffer_chars(o))))
#define StringBuffer_count(o) FIELD_I32(o, 0)
-// java.lang.Class
-#define Class_nativeStruct(o) FIELD_OBJ(o, OBJ_CLASS(o), 0)
-#define Class_targetName(o) FIELD_OBJ(o, OBJ_CLASS(o), 1)
-
// java.lang.reflect.Field
#define Field_index(o) FIELD_I32(o, 0)
#define Field_mod(o) FIELD_I32(o, 1)
diff --git a/TotalCrossVM/src/nm/lang/Class.c b/TotalCrossVM/src/nm/lang/Class.c
index 57115fc378..3cda9cfecb 100644
--- a/TotalCrossVM/src/nm/lang/Class.c
+++ b/TotalCrossVM/src/nm/lang/Class.c
@@ -5,7 +5,7 @@
-#include "tcvm.h"
+#include "Class.h"
TC_API void jlC_forName_s(NMParams p);
@@ -129,20 +129,23 @@ static void createMethodObject(Context currentContext, Method m, TCClass declari
setObjectLock(Method_name(*ret) = createStringObjectFromCharP(currentContext,isConstructor ? declaringClass->name : m->name,-1),UNLOCKED);
createClassObject(currentContext, declaringClass->name, Type_Null, &Method_declaringClass(*ret),null);
// parameters and exceptions
- Method_parameterTypes(*ret) = createArrayObject(currentContext, "[java.lang.Class", n = m->paramCount);
- if (Method_parameterTypes(*ret) && n > 0)
+ ptrObj = createArrayObject(currentContext, "[java.lang.Class", n = m->paramCount);
+ if (ptrObj && n > 0)
{
- TCObject* oa = (TCObject*)ARRAYOBJ_START(Method_parameterTypes(*ret));
+ TCObject* oa = (TCObject*)ARRAYOBJ_START(ptrObj);
for (i=0; i < n; i++)
createClassObject(currentContext, declaringClass->cp->cls[m->cpParams[i]], m->cpParams[i] < Type_Object ? m->cpParams[i] : Type_Null, oa++, null);
}
- Method_exceptionTypes(*ret) = createArrayObject(currentContext, "[java.lang.Class", n = 0); // thrown exceptions is not stored in TCClass!
- if (Method_exceptionTypes(*ret) && n > 0)
+ setObjectLock(Method_parameterTypes(*ret) = ptrObj, UNLOCKED);
+
+ ptrObj = createArrayObject(currentContext, "[java.lang.Class", n = 0); // thrown exceptions is not stored in TCClass!
+ if (ptrObj && n > 0)
{
- TCObject* oa = (TCObject*)ARRAYOBJ_START(Method_exceptionTypes(*ret));
+ TCObject* oa = (TCObject*)ARRAYOBJ_START(ptrObj);
for (i=0; i < n; i++)
createClassObject(currentContext, m->exceptionHandlers[i].className, Type_Null, oa++, null);
}
+ setObjectLock(Method_exceptionTypes(*ret) = ptrObj, UNLOCKED);
// return and type
if (!isConstructor)
@@ -566,7 +569,27 @@ TC_API void jlC_getFields(NMParams p) // java/lang/Class public native java.lang
//////////////////////////////////////////////////////////////////////////
TC_API void jlC_getMethods(NMParams p) // java/lang/Class public native java.lang.reflect.Method[] getMethods() throws SecurityException;
{
- getMCarray(p,false,true);
+ TCObject this_ = p->obj[0];
+ TCObject methods = Class_methods(this_);
+ TCObject ret;
+
+ if (methods == null)
+ {
+ getMCarray(p,false,true);
+ Class_methods(this_) = methods = p->retO;
+ }
+
+ ret = createArrayObject(p->currentContext, "[java.lang.reflect.Method", ARRAYOBJ_LEN(methods));
+ if (ret)
+ {
+ int32 length = ARRAYOBJ_LEN(methods);
+ TCObjectArray psrc = (TCObjectArray) ARRAYOBJ_START(methods);
+ TCObjectArray pdst = (TCObjectArray) ARRAYOBJ_START(ret);
+
+ while (length-- >= 0)
+ *pdst++ = *psrc++;
+ }
+ setObjectLock(p->retO = ret, UNLOCKED);
}
//////////////////////////////////////////////////////////////////////////
TC_API void jlC_getConstructors(NMParams p) // java/lang/Class public native java.lang.reflect.Constructor[] getConstructors() throws SecurityException;
diff --git a/TotalCrossVM/src/nm/lang/Class.h b/TotalCrossVM/src/nm/lang/Class.h
new file mode 100644
index 0000000000..c0c39c5df5
--- /dev/null
+++ b/TotalCrossVM/src/nm/lang/Class.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 TotalCross Global Mobile Platform Ltda.
+//
+// SPDX-License-Identifier: LGPL-2.1-only
+
+#ifndef Class_h
+#define Class_h
+
+#include "tcvm.h"
+
+// java.lang.Class
+#define Class_nativeStruct(o) FIELD_OBJ(o, OBJ_CLASS(o), 0)
+#define Class_targetName(o) FIELD_OBJ(o, OBJ_CLASS(o), 1)
+#define Class_methods(o) FIELD_OBJ(o, OBJ_CLASS(o), 4)
+
+#endif
diff --git a/TotalCrossVM/src/nm/lang/Reflection.c b/TotalCrossVM/src/nm/lang/Reflection.c
index 8786fad842..e85798f459 100644
--- a/TotalCrossVM/src/nm/lang/Reflection.c
+++ b/TotalCrossVM/src/nm/lang/Reflection.c
@@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: LGPL-2.1-only
-#include "tcvm.h"
+#include "Class.h"
typedef char NameBuf[256];
diff --git a/TotalCrossVM/src/tcvm/objectmemorymanager.c b/TotalCrossVM/src/tcvm/objectmemorymanager.c
index a92c3e5bcc..5ce9ffdaeb 100644
--- a/TotalCrossVM/src/tcvm/objectmemorymanager.c
+++ b/TotalCrossVM/src/tcvm/objectmemorymanager.c
@@ -501,7 +501,7 @@ static TCObject privateCreateObject(Context currentContext, CharP className, boo
if (IS_VMTWEAK_ON(VMTWEAK_TRACE_CREATED_CLASSOBJS))
{
if (!htObjsPerClass.items) htObjsPerClass = htNew(511, null);
- htInc(&htObjsPerClass, (int32)c, 1);
+ htInc(&htObjsPerClass, (size_t)c, 1);
}
if (_TRACE_OBJCREATION) debug("G %X obj created %s of size %d at %d. lock: %d. mark: %d. context: %X", o, className, objectSize, size2idx(objectSize), OBJ_ISLOCKED(o), markedAsUsed, currentContext);
@@ -547,7 +547,7 @@ TCObject createArrayObject(Context currentContext, CharP type, int32 len)
if (IS_VMTWEAK_ON(VMTWEAK_TRACE_CREATED_CLASSOBJS))
{
if (!htObjsPerClass.items) htObjsPerClass = htNew(511, null);
- htInc(&htObjsPerClass, (int32)c, 1);
+ htInc(&htObjsPerClass, (size_t)c, 1);
}
if (_TRACE_OBJCREATION) debug("G %X array obj created %s len %d, size = %d at %d. lock: %d", o, c->name,len, objectSize, size2idx(objectSize), OBJ_ISLOCKED(o));
end:
@@ -785,7 +785,7 @@ static int32 countObjectsInList(TCObject o, bool dump, int32 mark, int32* size,
{
ObjectProperties op = OBJ_PROPERTIES(o);
if (htOut)
- htInc(htOut, (int)OBJ_CLASS(o),1);
+ htInc(htOut, (size_t)OBJ_CLASS(o),1);
if (size)
*size += op->size;
if (_TRACE_OBJCREATION && dump) debug("G %X",o);
@@ -928,12 +928,12 @@ static void finalizeObject(TCObject o, TCClass c)
{
MUTEX_TYPE* mutex;
- mutex = htGetPtr(&htMutexes, (int32)o);
+ mutex = htGetPtr(&htMutexes, (size_t)o);
if (mutex)
{
DESTROY_MUTEX_VAR(*mutex);
xfree(mutex);
- htRemove(&htMutexes, (int32)o);
+ htRemove(&htMutexes, (size_t)o);
}
if (c->finalizeMethod == null)
@@ -1150,7 +1150,7 @@ void gc2(Context currentContext, bool lockOMM)
{
if (strEq(OBJ_CLASS(o)->name,BYTE_ARRAY))
debug("locked ba: %X",o);
- htInc(&htCount, (int32)OBJ_CLASS(o), 1);
+ htInc(&htCount, (size_t)OBJ_CLASS(o), 1);
lockCount++;
}
//if (_TRACE_OBJCREATION) debug("G marking locked obj %X",o);
@@ -1189,7 +1189,7 @@ void gc2(Context currentContext, bool lockOMM)
if ((c = OBJ_CLASS(o)) != null)
{
if (_TRACE_OBJCREATION) debug("G object being freed: %X (%s)",o, OBJ_CLASS(o)->name);
- if (traceCreatedClassObjs) htInc(&htObjsPerClass, (int32)OBJ_CLASS(o),-1);
+ if (traceCreatedClassObjs) htInc(&htObjsPerClass, (size_t)OBJ_CLASS(o),-1);
OBJ_CLASS(o) = null; // set the object "free"
}
diff --git a/TotalCrossVM/src/tcvm/tcmethod.c b/TotalCrossVM/src/tcvm/tcmethod.c
index 445fd96be5..b141c20cb0 100644
--- a/TotalCrossVM/src/tcvm/tcmethod.c
+++ b/TotalCrossVM/src/tcvm/tcmethod.c
@@ -8,7 +8,6 @@
TC_API Method getMethod(TCClass c, bool searchSuperclasses, CharP methodName, int32 nparams, ...) // not used internally
{
int32 i,j;
- va_list params;
if (c)
do
{
@@ -20,18 +19,22 @@ TC_API Method getMethod(TCClass c, bool searchSuperclasses, CharP methodName, in
if (strEq(methodName,mn) && nparams == mm->paramCount)
{
bool found = true;
- va_start(params, nparams);
- for (j = 0; j < nparams; j++) // do NOT invert the loop!
+ if (nparams > 0)
{
- CharP pt = (CharP)(va_arg(params, CharP));
- CharP po = c->cp->cls[mm->cpParams[j]];
- if (!strEq(pt,po))
+ va_list params;
+ va_start(params, nparams);
+ for (j = 0; j < nparams; j++) // do NOT invert the loop!
{
- found = false;
- break;
+ CharP pt = (CharP)(va_arg(params, CharP));
+ CharP po = c->cp->cls[mm->cpParams[j]];
+ if (!strEq(pt,po))
+ {
+ found = false;
+ break;
+ }
}
+ va_end(params);
}
- va_end(params);
if (found && (mm->code || mm->flags.isNative)) // not an abstract class?
return mm;
}
diff --git a/TotalCrossVM/src/tcvm/tcvm.c b/TotalCrossVM/src/tcvm/tcvm.c
index f4cb275976..5df418fc86 100644
--- a/TotalCrossVM/src/tcvm/tcvm.c
+++ b/TotalCrossVM/src/tcvm/tcvm.c
@@ -490,6 +490,7 @@ TC_API TValue executeMethod(Context context, Method method, ...)
goto throwNullPointerException;
}
thisClass = OBJ_CLASS(regO[code->mtd.this_]);
+callVirtualClass:
if (thisClass == null)
{
exceptionMsg = "Obj class is null";
@@ -764,6 +765,11 @@ TC_API TValue executeMethod(Context context, Method method, ...)
if (c == null)
goto throwClassNotFoundException;
}
+ if (thisClass != c)
+ {
+ thisClass = c;
+ goto callVirtualClass;
+ }
}
else
if (className == class_->name || strEq(className, class_->name)) // calling a method inside this class? (first comparison is always true, but keep 2nd for safety)