Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Support for running jar-packaged programs
  • Loading branch information
sorear committed Jun 21, 2013
1 parent 2aef1f6 commit 522ae10
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 11 deletions.
83 changes: 77 additions & 6 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/LibraryLoader.java
@@ -1,17 +1,23 @@
package org.perl6.nqp.runtime;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class LibraryLoader {
static HashMap<String,Class<?>> sharedClassHash = new HashMap<String,Class<?>>();

public void load(ThreadContext tc, String origFilename) {
public void load(ThreadContext tc, String origFilename) {
// Don't load the same thing multiple times.
if (tc.gc.loaded.contains(origFilename))
return;
Expand Down Expand Up @@ -63,23 +69,88 @@ public static Class<?> loadFile(String cf, boolean shared) throws Exception {
}
}

byte[] bytes = Files.readAllBytes( FileSystems.getDefault().getPath(cf) );
return loadNew(bytes);
byte[] b = Files.readAllBytes( FileSystems.getDefault().getPath(cf) );
long sig = b.length < 4 ? 0 :
(b[3] & 0xFF) | ((b[2] & 0xFF) << 8) | ((b[1] & 0xFF) << 16) | ((b[0] & 0xFFL) << 24);

if (sig == 0xCAFEBABEL) {
// This is a class file

return loadNew(b, null);
} else if (sig == 0x504B0304) {
// This is a (non-empty, non-self-extracting) zip file
// These are quite constrained for now

JarEntry je;
JarInputStream jis = new JarInputStream(new ByteArrayInputStream(b));
byte[] kl = null;
byte[] ser = null;

while ((je = jis.getNextJarEntry()) != null) {
byte[] data = new byte[(int)je.getSize()];
new DataInputStream(jis).readFully(data);

if (je.getName().endsWith(".class") && kl == null) kl = data;
else if (je.getName().endsWith(".serialized") && ser == null) ser = data;
else throw new RuntimeException("Bytecode jar contains unexpected file "+je.getName());
}

if (kl == null) throw new RuntimeException("Bytecode jar lacks class file");
if (ser == null) throw new RuntimeException("Bytecode jar lacks serialization file");

return loadNew(kl, ser);
} else {
throw new RuntimeException("Unrecognized bytecode format in "+cf);
}
}

public static Class<?> loadNew(byte[] bytes) {
return new IgnoreNameClassLoader(bytes).loadClass();
public static Class<?> loadNew(byte[] bytes, byte[] serial) {
return new IgnoreNameClassLoader(bytes, serial).loadClass();
}

private static class IgnoreNameClassLoader extends ClassLoader {
private byte[] bytes;
private byte[] serial;

public IgnoreNameClassLoader(byte[] bytes) {
public IgnoreNameClassLoader(byte[] bytes, byte[] serial) {
this.bytes = bytes;
this.serial = serial;
}

public Class<?> loadClass() {
return defineClass(null, this.bytes, 0, this.bytes.length);
}

@Override
public InputStream getResourceAsStream(String name) {
return new ByteArrayInputStream(serial);
}
}

// this is dumb
public static byte[] readEverything(InputStream from) throws IOException {
final int bufSize = 65536;
ArrayList<byte[]> chunks = new ArrayList<byte[]>();
byte[] currentChunk = new byte[bufSize];
int currentFill = 0;

while (true) {
if (currentFill == bufSize) {
chunks.add(currentChunk);
currentChunk = new byte[bufSize];
currentFill = 0;
}

int addl = from.read(currentChunk, currentFill, bufSize - currentFill);
if (addl < 0) break;
currentFill += addl;
}

byte[] finished = new byte[chunks.size() * bufSize + currentFill];
for (int i = 0; i < chunks.size(); i++)
System.arraycopy(chunks.get(i), 0, finished, i*bufSize, bufSize);
System.arraycopy(currentChunk, 0, finished, chunks.size()*bufSize, currentFill);

return finished;
}
}
17 changes: 12 additions & 5 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java
Expand Up @@ -15,6 +15,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
Expand Down Expand Up @@ -3057,7 +3058,7 @@ public static String serialize(SixModelObject scRef, SixModelObject sh, ThreadCo
throw ExceptionHandling.dieInternal(tc, "serialize was not passed a valid SCRef");
}
}
public static String deserialize(String blob, SixModelObject scRef, SixModelObject sh, SixModelObject cr, SixModelObject conflict, ThreadContext tc) {
public static String deserialize(String blob, SixModelObject scRef, SixModelObject sh, SixModelObject cr, SixModelObject conflict, ThreadContext tc) throws IOException {
if (scRef instanceof SCRefInstance) {
SerializationContext sc = ((SCRefInstance)scRef).referencedSC;

Expand All @@ -3070,8 +3071,8 @@ public static String deserialize(String blob, SixModelObject scRef, SixModelObje
CodeRef[] crArray;
int crCount;

CompilationUnit cu = tc.curFrame.codeRef.staticInfo.compUnit;
if (cr == null) {
CompilationUnit cu = tc.curFrame.codeRef.staticInfo.compUnit;
crArray = cu.qbidToCodeRef;
crCount = cu.serializedCodeRefCount();
} else {
Expand All @@ -3081,11 +3082,17 @@ public static String deserialize(String blob, SixModelObject scRef, SixModelObje
crArray[i] = (CodeRef)cr.at_pos_boxed(tc, i);
}

ByteBuffer binaryBlob;
if (blob == null) {
binaryBlob = ByteBuffer.wrap( LibraryLoader.readEverything( cu.getClass().getResourceAsStream( cu.getClass().getSimpleName() + ".serialized" ) ) );
} else {
binaryBlob = Base64.decode(blob);
}

SerializationReader sr = new SerializationReader(
tc, sc, shArray, crArray, crCount,
Base64.decode(blob));
tc, sc, shArray, crArray, crCount, binaryBlob);
sr.deserialize();

return blob;
}
else {
Expand Down

0 comments on commit 522ae10

Please sign in to comment.