deathcap / Junket

an implementation of the Bukkit API for JavaScript using doppio (incomplete)
MIT License
2 stars 0 forks source link

Cannot read property 'length' of undefined at ReferenceClassData.is_subinterface (doppio/build/dev-cli/src/ClassData.ts:677:31) #2

Closed deathcap closed 10 years ago

deathcap commented 10 years ago
+ ../doppio/doppio-dev -jar build/distributions/Junket-0.0.1-shadow.jar
Hello
May 15, 2014 2:15:51 AM deathcap.junket.Server <init>
INFO: Starting up...
TypeError: Cannot read property 'length' of undefined
    at ReferenceClassData.is_subinterface (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/ClassData.ts:677:31)
    at ReferenceClassData.is_castable (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/ClassData.ts:664:21)
    at java_lang_Class.isAssignableFrom(Ljava/lang/Class;)Z (eval at <anonymous> (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/jvm.ts:296:17), <anonymous>:109:25)
    at NativeStackFrame.run (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:233:37)
    at JVMThread.run (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:568:31)
    at JVMThread.setStatus (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:652:16)
    at Object._onImmediate (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:393:24)
    at processImmediate [as _immediateCallback] (timers.js:330:15)

using 53fb0c7d3cfa48b5f8913cd585332417f90b0e59 on doppio natives_refactor https://github.com/plasma-umass/doppio/commit/1625c920392a0bf80817a62302e860c766091931

deathcap commented 10 years ago

Looks like the problem is:

this_class: 'Lorg/bukkit/configuration/serialization/ConfigurationSerializable;',

edit: actually, the dynamically loaded plugin class MyPlugin. added to src/ClassData.ts

  public get_interfaces(): ReferenceClassData[] {
  if(this.interface_cdatas===undefined)console.log('ABOUT TO RETURN undefined cdatas for',this);
    return this.interface_cdatas;
  }

logs:

ABOUT TO RETURN undefined cdatas for { 
…
  this_class: 'LMyPlugin;',
  super_class: 'Lorg/bukkit/plugin/java/JavaPlugin;',
  interfaces: [],
  fields: [],
  fl_cache: {},
  methods: 
   { '<init>()V': 
      { cls: [Circular],
        idx: 0,
        access_byte: 1,
        access_flags: [Object],
        name: '<init>',
        raw_descriptor: '()V',
        reset_caches: false,
        param_types: [],
        param_bytes: 1,
        num_args: 1,
        return_type: 'V',
        attrs: [Object],
        code: [Object] },
     'onEnable()V': 
      { cls: [Circular],
        idx: 1,
        access_byte: 1,
        access_flags: [Object],
        name: 'onEnable',
        raw_descriptor: '()V',
        reset_caches: false,
        param_types: [],
        param_bytes: 1,
        num_args: 1,
        return_type: 'V',
        attrs: [Object],
        code: [Object] } },
  ml_cache: {},
  attrs: [ { name: 'SourceFile', filename: 'MyPlugin.java' } ],

The interface_cdatas property is not only undefined, but it does not exist on this ('interface_cdatas' in this === false). It is supposed to be set in setResolved() —but this isn't called.

The class is checked for in is_subinterface before it is resolved.

deathcap commented 10 years ago

Bukkit dynamically loads the plugin class through its own class loader in https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java#L40:

                jarClass = Class.forName(description.getMain(), true, this);

where true=initialize, this=the custom classloader PluginClassLoader. It then tries to cast it:

                pluginClass = jarClass.asSubclass(JavaPlugin.class);

which fails because the class hasn't been resolved yet, much less initialized. The ClassState state enum for the class with undefined interface_cdatas is 1 = loaded (not yet 2=resolved, or 3=initialized). Might be a problem when initializing classes loaded via custom loaders.

there is a classes/test/CustomClassLoader test case in doppio, using the same Class.forName(s,true,loader1) method (to load classes.test.CustomClassLoader through itself), and also CustomClassLoader2. Neither seem to hit this problem.

deathcap commented 10 years ago

Correction: the line of Java code which causes the doppio crash is in https://github.com/Bukkit/Bukkit/blob/master/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java#L201

    void setClass(final String name, final Class<?> clazz) {
        if (!classes.containsKey(name)) {
            classes.put(name, clazz);

            if (ConfigurationSerializable.class.isAssignableFrom(clazz)) { // <-- here ***
                Class<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
                ConfigurationSerialization.registerClass(serializable);
            }
        }
    }

setClass is called by PluginClassLoader's findClass. So the isAssignableFrom() call happens right after the class is defined, before it is "resolved".

--- a/classes/test/CustomClassLoader2.java
+++ b/classes/test/CustomClassLoader2.java
@@ -63,6 +63,7 @@ public class CustomClassLoader2 extends ClassLoader {
       result = defineClass(className,classByte,0,classByte.length,null);
       System.out.println("Registering class " + className);
       classes.put(className,result);
+      System.out.println("isAssignableFrom = " + String.class.isAssignableFrom(result));
       System.out.println("Found class. Returning.");
       return result;
     } catch(Exception e){

result in doppio (ok in oracle jvm):

doppio $ ./doppio-dev classes/test/CustomClassLoader2
Loading Dog...
Loading class classes.test.Dog
Finding class classes.test.Dog
Defining class classes.test.Dog
Registering class classes.test.Dog
isAssignableFrom = false
Found class. Returning.
Dog's methods
Loading class java.lang.String
Finding class java.lang.String
Couldn't get the class ourselves!
Getting the system class.
TypeError: Cannot call method 'map' of undefined
    at Method.java_lang_Class.getInterfaces()[Ljava/lang/Class; [as code] (eval at <anonymous> (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/jvm.ts:296:17), <anonymous>:151:33)
    at code (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/methods.ts:284:22)
    at NativeStackFrame.run (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:233:37)
    at JVMThread.run (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:568:31)
    at JVMThread.setStatus (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:652:16)
    at Object._onImmediate (/Users/admin/games/voxeljs/doppio/build/dev-cli/src/threading.ts:393:24)
    at processImmediate [as _immediateCallback] (timers.js:330:15)
deathcap commented 10 years ago

Isolated and opened a doppio issue: https://github.com/plasma-umass/doppio/issues/313

Interestingly, the isolated test case succeeds on their master branch, only fails on natives_refactor, but master hits https://github.com/deathcap/Junket/issues/1 so I can't use it, but hopefully this will help track down the problem.

Thinking how I might be able to workaround this on my side.. if I comment out the ConfigurationSerializable check in Bukkit:

diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
index b178c0d..d85bc6b 100644
--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java
@@ -202,10 +202,12 @@ public final class JavaPluginLoader implements PluginLoader {
         if (!classes.containsKey(name)) {
             classes.put(name, clazz);

+            /* workaround https://github.com/deathcap/Junket/issues/2
             if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
                 Class<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
                 ConfigurationSerialization.registerClass(serializable);
             }
+            */
         }
     }

then it works! The sample plugin loads and runs:

+ java -jar build/distributions/Junket-0.0.1-shadow.jar
Hello
May 15, 2014 7:25:05 PM deathcap.junket.Server <init>
INFO: Starting up...
May 15, 2014 7:25:05 PM deathcap.junket.Server <init>
INFO: Loaded 1 plugins
May 15, 2014 7:25:05 PM deathcap.junket.Server <init>
INFO: Enabling plugin MyPlugin vv5cd2e67
May 15, 2014 7:25:05 PM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Enabling MyPlugin vv5cd2e67
May 15, 2014 7:25:05 PM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Hello, plugin!
+ ../doppio/doppio-dev -jar build/distributions/Junket-0.0.1-shadow.jar
Hello
May 16, 2014 2:25:09 AM deathcap.junket.Server <init>
INFO: Starting up...
May 16, 2014 2:25:27 AM deathcap.junket.Server <init>
INFO: Loaded 1 plugins
May 16, 2014 2:25:27 AM deathcap.junket.Server <init>
INFO: Enabling plugin MyPlugin vv5cd2e67
May 16, 2014 2:25:27 AM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Enabling MyPlugin vv5cd2e67
May 16, 2014 2:25:27 AM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Hello, plugin!

but, would like to avoid changes to the Bukkit dependency if possible.

deathcap commented 10 years ago

Fixed in doppio as of https://github.com/plasma-umass/doppio/commit/44b137a49df7dc8efaa39abd0c67526d904e79bf - can now load plugins with the native Bukkit API:

+ java -jar build/distributions/Junket-0.0.1-shadow.jar
Hello
May 16, 2014 12:26:35 PM deathcap.junket.Server <init>
INFO: Starting up...
May 16, 2014 12:26:35 PM deathcap.junket.Server <init>
INFO: Loaded 1 plugins
May 16, 2014 12:26:35 PM deathcap.junket.Server <init>
INFO: Enabling plugin MyPlugin vv5cd2e67
May 16, 2014 12:26:35 PM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Enabling MyPlugin vv5cd2e67
May 16, 2014 12:26:35 PM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Hello, plugin!
+ ../doppio/doppio-dev -jar build/distributions/Junket-0.0.1-shadow.jar
Hello
May 16, 2014 7:26:39 PM deathcap.junket.Server <init>
INFO: Starting up...
May 16, 2014 7:26:58 PM deathcap.junket.Server <init>
INFO: Loaded 1 plugins
May 16, 2014 7:26:58 PM deathcap.junket.Server <init>
INFO: Enabling plugin MyPlugin vv5cd2e67
May 16, 2014 7:26:58 PM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Enabling MyPlugin vv5cd2e67
May 16, 2014 7:26:58 PM org.bukkit.plugin.PluginLogger log
INFO: [MyPlugin] Hello, plugin!