blob: f675840b5669867b73bbde2027d9418741449c69 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package flex2.compiler;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import macromedia.asc.util.IntegerPool;
import flash.fonts.FontManager;
import flash.localization.LocalizationManager;
import flash.swf.CompressionLevel;
import flash.swf.Frame;
import flash.swf.Movie;
import flash.swf.MovieDecoder;
import flash.swf.MovieEncoder;
import flash.swf.Tag;
import flash.swf.TagDecoder;
import flash.swf.TagEncoder;
import flash.swf.tags.DefineFont;
import flash.swf.tags.DefineTag;
import flash.swf.types.Rect;
import flex2.compiler.common.Configuration;
import flex2.compiler.common.PathResolver;
import flex2.compiler.io.DeletedFile;
import flex2.compiler.io.FileUtil;
import flex2.compiler.io.ResourceFile;
import flex2.compiler.io.VirtualFile;
import flex2.compiler.util.CompilerMessage;
import flex2.compiler.util.LocalLogger;
import flex2.compiler.util.MultiName;
import flex2.compiler.util.QName;
import flex2.compiler.util.ThreadLocalToolkit;
import flex2.compiler.util.LocalLogger.Warning;
/**
* This class handles the reading and writing of the incremental
* compilation cache. The cache contains all the Source and
* CompilationUnit objects from an incremental compilation, except
* those from SWC's. A SWC already contains all the information we
* need, so we don't duplicate it in the cache. The cache is stored
* as a single monolithic file. Assets for each CompilationUnit are
* encoded as a SWF and included in the cache file.
*/
final class PersistenceStore
{
// C: If you update the encoding/decoding algorithm, please increment the minor version by 1. Thanks.
private static final int major_version = 4;
private static final int minor_version = 7;
PersistenceStore(Configuration configuration, RandomAccessFile file)
{
this(configuration, file, null);
}
PersistenceStore(Configuration configuration, RandomAccessFile file, FontManager fontManager)
{
assert file != null;
this.file = file;
key = new ArrayKey();
this.fontManager = fontManager;
this.configuration = configuration;
}
private final Configuration configuration;
private final RandomAccessFile file;
private final ArrayKey key;
private final FontManager fontManager;
/**
* An input stream that reads from another input stream, but sets a
* limit on how much data it will read.
*
* <p>Calls to close() do not close the parent InputStream.
*/
private static class SizeLimitingInputStream extends InputStream {
private InputStream in;
private int max;
public SizeLimitingInputStream(InputStream in, int max) {
this.in = in;
this.max = max;
}
@Override
public int read() throws IOException {
if (max == 0)
return -1;
--max;
return in.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
len = Math.min(len, max);
max -= len;
return in.read(b, off, len);
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int available() throws IOException {
return Math.min(max, in.available());
}
@Override
public void close() throws IOException {
// ignore
}
@Override
public synchronized void mark(int readLimit) {
in.mark(readLimit);
}
@Override
public boolean markSupported() {
return false;
}
@Override
public synchronized void reset() throws IOException {
in.reset();
}
@Override
public long skip(long n) throws IOException {
n = Math.min(n, max);
max -= n;
return in.skip(n);
}
/**
* Skips forward to the end of the size limit that had been
* specified when this SizeLimitingInputStream was created.
*/
public void skipToEnd() throws IOException {
skip(max);
}
}
/**
* An InputStream that reads from a RandomAccessFile. Calls to close()
* do not close the parent RandomAccessFile.
*/
private static class RandomAccessFileInputStream extends InputStream {
private RandomAccessFile raf;
public RandomAccessFileInputStream(RandomAccessFile raf) {
this.raf = raf;
}
@Override
public int read() throws IOException {
return raf.read();
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return raf.read(b, off, len);
}
@Override
public int read(byte[] b) throws IOException {
return raf.read(b);
}
}
/**
* An OutputStream that writes to a RandomAccessFile. Calls to close()
* do not close the parent RandomAccessFile.
*/
private static class RandomAccessFileOutputStream extends OutputStream {
private RandomAccessFile raf;
public RandomAccessFileOutputStream(RandomAccessFile raf) {
this.raf = raf;
}
@Override
public void write(int b) throws IOException {
raf.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
raf.write(b, off, len);
}
@Override
public void write(byte[] b) throws IOException {
raf.write(b);
}
}
int write(FileSpec fileSpec,
SourceList sourceList,
SourcePath sourcePath,
ResourceContainer resources,
ResourceBundlePath bundlePath,
List sources,
List units,
int checksum,
int cmd_checksum,
int linker_checksum,
int swc_checksum,
Map<QName, Long> swcDefSignatureChecksums,
Map<String, Long> swcFileChecksums,
Map<String, Long> archiveFileChecksums,
String description) throws IOException
{
Map<Object, Integer> pool = new HashMap<Object, Integer>();
BufferedOutputStream out = new BufferedOutputStream(new RandomAccessFileOutputStream(file));
writeHeader(checksum, cmd_checksum, linker_checksum, swc_checksum, description, out);
out.flush();
long offsetOfPointerToConstantPool = file.getFilePointer();
file.writeLong(0); // a dummy value for the pointer to the constant pool; will be replaced later
writeFileSpec(fileSpec, pool, out);
writeSourceList(sourceList, pool, out);
writeSourcePath(sourcePath, pool, out);
// C: There is no need to have writeResourceContainer() because it has nothing we need to persist.
// writeResourceContainer(resources, pool);
writeResourceBundlePath(bundlePath, pool, out);
int count = (sources == null) ? 0 : sources.size();
writeU32(out, count);
if (sources != null)
{
writeSourceNames(sources, pool, out);
}
int defCount = swcDefSignatureChecksums == null ? 0 : swcDefSignatureChecksums.size();
writeU32(out, defCount);
if (swcDefSignatureChecksums != null)
{
writeSwcDefSignatureChecksums(swcDefSignatureChecksums, pool, out);
}
int fileCount = swcFileChecksums == null ? 0 : swcFileChecksums.size();
writeU32(out, fileCount);
if (swcFileChecksums != null)
{
writeFileChecksums(swcFileChecksums, pool, out);
}
int archiveFileCount = archiveFileChecksums == null ? 0 : archiveFileChecksums.size();
writeU32(out, archiveFileCount);
if (archiveFileChecksums != null)
{
writeFileChecksums(archiveFileChecksums, pool, out);
}
Collection<Source> c1 = fileSpec == null ? Collections.<Source>emptyList() : fileSpec.sources();
Collection<Source> c2 = sourceList == null ? Collections.<Source>emptyList() : sourceList.sources().values();
Map<String, Source> c3 = sourcePath == null ? Collections.<String, Source>emptyMap() : sourcePath.sources();
Collection<Source> c4 = resources == null ? Collections.<Source>emptyList() : resources.sources().values();
Map<String, Source> c5 = bundlePath == null ? Collections.<String, Source>emptyMap() : bundlePath.sources();
int totalCount = c1.size() + c2.size() + c3.size() + c4.size() + c5.size();
writeU32(out, totalCount);
writeCompilationUnits(c1, pool, out);
writeCompilationUnits(c2, pool, out);
writeCompilationUnits(c3.values(), pool, out);
writeCompilationUnits(c5.values(), pool, out);
writeCompilationUnits(c4, pool, out);
// go back to near the beginning, and write out the location of the constant pool
out.flush();
long offsetOfConstantPool = file.getFilePointer();
file.seek(offsetOfPointerToConstantPool);
file.writeLong(offsetOfConstantPool);
file.seek(offsetOfConstantPool);
writeConstantPool(pool, out);
out.flush();
return totalCount;
}
private void writeSwcDefSignatureChecksums(Map<QName, Long> swcDefSignatureChecksums,
Map<Object, Integer> pool,
OutputStream out) throws IOException
{
for (Iterator<QName> i = swcDefSignatureChecksums.keySet().iterator(); i.hasNext(); )
{
QName qName = i.next();
Long ts = swcDefSignatureChecksums.get(qName);
writeU32(out, addQName(pool, qName));
writeLong(out, ts.longValue());
}
}
private void writeFileChecksums(Map<String, Long> m, Map<Object, Integer> pool, OutputStream out) throws IOException
{
for (Iterator<String> i = m.keySet().iterator(); i.hasNext();)
{
String fileName = i.next();
Long ts = m.get(fileName);
writeU32(out, addString(pool, fileName));
writeLong(out, ts.longValue());
}
}
private void writeHeader(int checksum,
int cmd_checksum,
int linker_checksum,
int swc_checksum,
String description,
OutputStream out) throws IOException
{
writeU32(out, major_version); // major version
writeU32(out, minor_version); // minor version
writeU32(out, checksum); // checksum
writeU32(out, cmd_checksum);
writeU32(out, linker_checksum);
writeU32(out, swc_checksum);
byte[] b = description.getBytes("UTF8");
writeU32(out, b.length);
writeBytes(out, b);
}
private void writeConstantPool(Map<Object, Integer> pool, OutputStream out) throws IOException
{
// invert the map
Object[] values = new Object[pool.size()];
for (Map.Entry<Object, Integer> entry: pool.entrySet()) {
int index = entry.getValue();
values[index] = entry.getKey();
}
writeU32(out, pool.size());
for (Object value : values)
{
if (value instanceof String)
{
writeU8(out, 1);
byte[] b = ((String) value).getBytes("UTF8");
writeU32(out, b.length);
writeBytes(out, b);
}
else if (value instanceof ArrayKey)
{
writeU8(out, 2);
String[] a = ((ArrayKey) value).a1;
writeU32(out, a == null ? 0 : a.length);
for (int j = 0, size = (a == null) ? 0 : a.length; j < size; j++)
{
writeU32(out, addString(pool, a[j]));
}
}
else if (value instanceof byte[])
{
writeU8(out, 3);
byte[] b = (byte[]) value;
writeU32(out, b.length);
if (b.length > 0)
{
writeBytes(out, b);
}
}
else if (value instanceof QName)
{
writeU8(out, 4);
QName qName = (QName) value;
writeU32(out, addString(pool, qName.getNamespace()));
writeU32(out, addString(pool, qName.getLocalPart()));
}
else if (value instanceof MultiName)
{
writeU8(out, 5);
MultiName multiName = (MultiName) value;
writeU32(out, addStrings(pool, multiName.namespaceURI));
writeU32(out, addString(pool, multiName.localPart));
}
else if (value instanceof flex2.compiler.abc.MetaData)
{
writeU8(out, 6);
flex2.compiler.abc.MetaData md = (flex2.compiler.abc.MetaData) value;
writeU32(out, addString(pool, md.getID()));
writeU32(out, md.count());
for (int j = 0, count = md.count(); j < count; j++)
{
String key = md.getKey(j);
String val = md.getValue(j);
writeU32(out, addString(pool, key == null ? "" : key));
writeU32(out, addString(pool, val));
}
}
else
{
assert false;
}
}
}
private void writeSourceNames(List sources, Map<Object, Integer> pool, OutputStream out) throws IOException
{
for (int i = 0, size = sources.size(); i < size; i++)
{
Source s = (Source) sources.get(i);
if (s != null)
{
writeU32(out, addString(pool, s.getName()));
if (s.isFileSpecOwner())
{
writeU8(out, 0);
}
else if (s.isSourceListOwner())
{
writeU8(out, 1);
}
else if (s.isSourcePathOwner())
{
writeU8(out, 2);
}
else if (s.isResourceContainerOwner())
{
writeU8(out, 3);
}
else if (s.isResourceBundlePathOwner())
{
writeU8(out, 4);
}
else if (s.isSwcScriptOwner())
{
writeU8(out, 5);
}
else if (s.isCompilerSwcContextOwner())
{
writeU8(out, 6);
}
else
{
writeU8(out, 7);
assert false : "s = " + s + ", owner = " + s.getOwner();
}
}
else
{
writeU32(out, addString(pool, "null"));
}
}
}
private void writeFileSpec(FileSpec fileSpec, Map<Object, Integer> pool, OutputStream out) throws IOException
{
writeU8(out, fileSpec != null ? 1 : 0);
if (fileSpec == null)
return;
String[] mimeTypes = fileSpec.getMimeTypes();
Collection<Source> sources = fileSpec.sources();
writeU32(out, mimeTypes.length);
for (int i = 0, length = mimeTypes.length; i < length; i++)
{
writeU32(out, addString(pool, mimeTypes[i]));
}
writeU32(out, sources.size());
for (Iterator<Source> i = sources.iterator(); i.hasNext();)
{
Source s = i.next();
writeU32(out, addString(pool, s.getName()));
}
}
private void writeSourceList(SourceList sourceList, Map<Object, Integer> pool, OutputStream out) throws IOException
{
writeU8(out, sourceList != null ? 1 : 0);
if (sourceList == null)
return;
String[] mimeTypes = sourceList.getMimeTypes();
List<File> paths = sourceList.getPaths();
Collection<Source> sources = sourceList.sources().values();
writeU32(out, mimeTypes.length);
for (int i = 0, length = mimeTypes.length; i < length; i++)
{
writeU32(out, addString(pool, mimeTypes[i]));
}
writeU32(out, paths.size());
for (int i = 0, length = paths.size(); i < length; i++)
{
writeU32(out, addString(pool, FileUtil.getCanonicalPath(paths.get(i))));
}
writeU32(out, sources.size());
for (Iterator<Source> i = sources.iterator(); i.hasNext();)
{
Source s = i.next();
writeU32(out, addString(pool, s.getName()));
}
}
private void writeSourcePath(SourcePath sourcePath, Map<Object, Integer> pool, OutputStream out) throws IOException
{
writeU8(out, sourcePath != null ? 1 : 0);
if (sourcePath == null)
return;
String[] mimeTypes = sourcePath.getMimeTypes();
List<File> paths = sourcePath.getPaths();
Map<String, Source> sources = sourcePath.sources();
writeU32(out, mimeTypes.length);
for (int i = 0, length = mimeTypes.length; i < length; i++)
{
writeU32(out, addString(pool, mimeTypes[i]));
}
writeU32(out, paths.size());
for (int i = 0, length = paths.size(); i < length; i++)
{
writeU32(out, addString(pool, FileUtil.getCanonicalPath(paths.get(i))));
}
writeU32(out, sources.size());
for (Iterator<String> i = sources.keySet().iterator(); i.hasNext();)
{
String className = i.next();
Source s = sources.get(className);
writeU32(out, addString(pool, className));
writeU32(out, addString(pool, s.getName()));
}
}
private void writeResourceBundlePath(ResourceBundlePath bundlePath, Map<Object, Integer> pool, OutputStream out) throws IOException
{
writeU8(out, bundlePath != null ? 1 : 0);
if (bundlePath == null)
return;
String[] mimeTypes = bundlePath.getMimeTypes();
String[] locales = bundlePath.getLocales();
Map<String, List<File>> rbDirectories = bundlePath.getResourceBundlePaths();
Map<String, Source> sources = bundlePath.sources();
writeU32(out, mimeTypes.length);
for (int i = 0, length = mimeTypes.length; i < length; i++)
{
writeU32(out, addString(pool, mimeTypes[i]));
}
writeU32(out, locales == null ? 0 : locales.length);
for (int i = 0, length = locales == null ? 0 : locales.length; i < length; i++)
{
writeU32(out, addString(pool, locales[i]));
List<File> paths = rbDirectories.get(locales[i]);
writeU32(out, paths == null ? 0 : paths.size());
for (int j = 0, size = paths.size(); j < size; j++)
{
writeU32(out, addString(pool, FileUtil.getCanonicalPath(paths.get(j))));
}
}
writeU32(out, sources.size());
for (Iterator<String> i = sources.keySet().iterator(); i.hasNext();)
{
String bundleName = i.next();
Source s = sources.get(bundleName);
writeU32(out, addString(pool, bundleName));
writeU32(out, addString(pool, s.getName()));
ResourceFile rf = (ResourceFile) s.getBackingFile();
VirtualFile[] rFiles = rf.getResourceFiles();
VirtualFile[] rRoots = rf.getResourcePathRoots();
writeU32(out, rFiles.length);
for (int j = 0, size = rFiles.length; j < size; j++)
{
writeU32(out, addString(pool, rFiles[j] != null ? rFiles[j].getName() : "null"));
writeU32(out, addString(pool, rRoots[j] != null ? rRoots[j].getName() : "null"));
}
}
}
private void writeCompilationUnits(Collection<Source> sources, Map<Object, Integer> pool, OutputStream out) throws IOException
{
for (Source s : sources)
{
writeSource(s, pool, out);
CompilationUnit u = s.getCompilationUnit();
if (u != null)
{
writeCompilationUnit(u, pool, out);
}
}
}
private void writeSource(Source s, Map<Object, Integer> pool, OutputStream out) throws IOException
{
final CompilationUnit unit = s.getCompilationUnit();
final boolean hasUnit = (unit != null);
writeU32(out, addString(pool, s.getName()));
writeU32(out, addString(pool, s.getRelativePath()));
writeU32(out, addString(pool, s.getShortName()));
if (s.isFileSpecOwner())
{
writeU8(out, 0);
}
else if (s.isSourceListOwner())
{
writeU8(out, 1);
writeU32(out, addString(pool, s.getPathRoot().getName()));
}
else if (s.isSourcePathOwner())
{
writeU8(out, 2);
writeU32(out, addString(pool, s.getPathRoot().getName()));
}
else if (s.isResourceContainerOwner())
{
writeU8(out, 3);
}
else if (s.isResourceBundlePathOwner())
{
writeU8(out, 4);
writeU32(out, addString(pool, s.getPathRoot().getName()));
}
else if (s.isCompilerSwcContextOwner())
{
writeU8(out, 5);
}
else
{
writeU8(out, 6);
assert false : "owner = " + s.getOwner();
}
writeU8(out, s.isInternal() ? 1 : 0);
writeU8(out, s.isRoot() ? 1 : 0);
writeU8(out, s.isDebuggable() ? 1 : 0);
writeU8(out, hasUnit ? 1 : 0);
writeLong(out, s.getFileTime());
// signatures
{
final boolean hasSignatureChecksum = hasUnit && unit.hasSignatureChecksum();
writeU8(out, (hasSignatureChecksum ? 1 : 0));
if (hasSignatureChecksum)
{
final Long signatureChecksum = unit.getSignatureChecksum();
writeLong(out, signatureChecksum.longValue());
}
}
writeU32(out, s.getFileIncludeSize());
for (Iterator<VirtualFile> j = s.getFileIncludes(); j.hasNext();)
{
VirtualFile f = j.next();
writeU32(out, addString(pool, f.getName()));
writeLong(out, s.getFileIncludeTime(f));
}
List<Warning> warnings = null;
if (s.getLogger() != null && (warnings = s.getLogger().getWarnings()) != null)
{
writeU32(out, warnings.size());
}
else
{
writeU32(out, 0);
}
for (int i = 0, size = warnings == null ? 0 : warnings.size(); i < size; i++)
{
LocalLogger.Warning w = warnings.get(i);
writeU32(out, addString(pool, w.path == null ? "" : w.path));
writeU32(out, addString(pool, w.warning == null ? "" : w.warning));
writeU32(out, addString(pool, w.source == null ? "" : w.source));
writeU32(out, w.line == null ? -1 : w.line.intValue());
writeU32(out, w.col == null ? -1 : w.col.intValue());
writeU32(out, w.errorCode == null ? -1 : w.errorCode.intValue());
}
}
private void writeMap(OutputStream out, Map<Object, Integer> pool, Map<String, Object> map) throws IOException
{
writeU32(out, map.size());
for (Iterator<Map.Entry<String, Object>> i = map.entrySet().iterator(); i.hasNext();)
{
Map.Entry<String, Object> e = i.next();
String key = e.getKey();
String value = (String) e.getValue();
if (value != null)
{
writeU32(out, addString(pool, key));
writeU32(out, addString(pool, value));
}
else
{
assert false : key + " can't be null.";
}
}
}
private void writeCompilationUnit(CompilationUnit u, Map<Object, Integer> pool, OutputStream out) throws IOException
{
writeU32(out, addBytes(pool, u.bytes.toByteArray()));
writeU32(out, u.getWorkflow());
writeU32(out, u.getState());
writeU32(out, u.topLevelDefinitions.size());
for (Iterator<QName> i = u.topLevelDefinitions.iterator(); i.hasNext();)
{
writeU32(out, addQName(pool, i.next()));
}
writeU32(out, u.inheritanceHistory.size());
for (MultiName multiName : u.inheritanceHistory.keySet())
{
QName qName = u.inheritanceHistory.get(multiName);
writeU32(out, addMultiName(pool, multiName));
writeU32(out, addQName(pool, qName));
}
writeU32(out, u.typeHistory.size());
for (MultiName multiName : u.typeHistory.keySet())
{
QName qName = u.typeHistory.get(multiName);
writeU32(out, addMultiName(pool, multiName));
writeU32(out, addQName(pool, qName));
}
writeU32(out, u.namespaceHistory.size());
for (MultiName multiName : u.namespaceHistory.keySet())
{
QName qName = u.namespaceHistory.get(multiName);
writeU32(out, addMultiName(pool, multiName));
writeU32(out, addQName(pool, qName));
}
writeU32(out, u.expressionHistory.size());
for (MultiName multiName : u.expressionHistory.keySet())
{
QName qName = u.expressionHistory.get(multiName);
writeU32(out, addMultiName(pool, multiName));
writeU32(out, addQName(pool, qName));
}
if (u.auxGenerateInfo != null && u.auxGenerateInfo.size() > 0)
{
writeU8(out, 1);
String baseLoaderClass = (String) u.auxGenerateInfo.get( "baseLoaderClass");
writeU32(out, addString(pool, baseLoaderClass == null ? "" : baseLoaderClass));
String generateLoaderClass = (String) u.auxGenerateInfo.get( "generateLoaderClass");
writeU32(out, addString(pool, generateLoaderClass == null ? "" : generateLoaderClass));
String className = (String) u.auxGenerateInfo.get( "windowClass");
writeU32(out, addString(pool, className == null ? "" : className));
String preLoader = (String) u.auxGenerateInfo.get( "preloaderClass");
writeU32(out, addString(pool, preLoader == null ? "" : preLoader));
Boolean usePreloader = (Boolean) u.auxGenerateInfo.get( "usePreloader");
writeU8(out, usePreloader.booleanValue() ? 1 : 0);
Map<String, Object> rootAttributeMap = (Map<String, Object>) u.auxGenerateInfo.get( "rootAttributes");
writeMap(out, pool, rootAttributeMap);
Map<String, Object> rootAttributeEmbedVars = (Map<String, Object>) u.auxGenerateInfo.get( "rootAttributeEmbedVars");
writeMap(out, pool, rootAttributeEmbedVars);
Map<String, Object> rootAttributeEmbedNames = (Map<String, Object>) u.auxGenerateInfo.get( "rootAttributeEmbedNames");
writeMap(out, pool, rootAttributeEmbedNames);
}
else
{
writeU8(out, 0);
}
if (u.iconFile != null)
{
writeU8(out, 1);
writeU32(out, addString(pool, u.icon));
writeU32(out, addString(pool, u.iconFile.getName()));
}
else
{
writeU8(out, 0);
}
writeAssets(u, pool, out);
}
private void writeAssets(CompilationUnit u, Map<Object, Integer> pool, OutputStream out) throws IOException
{
if (u.hasAssets())
{
writeU32(out, u.getAssets().count());
Movie movie = new Movie();
movie.version = configuration.getTargetPlayerMajorVersion();
assert movie.version >= 9;
movie.size = new Rect(100 * 20, 100 * 20);
movie.framerate = 12;
Frame frame = new Frame();
movie.frames = new ArrayList<Frame>();
movie.frames.add(frame);
for (Iterator<Entry<String, AssetInfo>> i = u.getAssets().iterator(); i.hasNext();)
{
Entry<String, AssetInfo> entry = i.next();
String className = entry.getKey();
AssetInfo assetInfo = entry.getValue();
writeU32(out, addString(pool, className));
if (assetInfo.getPath() != null)
{
writeU32(out, addString(pool, assetInfo.getPath().getName()));
}
else
{
writeU32(out, addString(pool, ""));
}
writeLong(out, assetInfo.getCreationTime());
DefineTag asset = assetInfo.getDefineTag();
frame.addSymbolClass(className, asset);
if (asset.name != null)
{
frame.addExport(asset);
}
}
TagEncoder handler = new TagEncoder();
MovieEncoder encoder = new MovieEncoder(handler);
encoder.export(movie, true); // use compression
// Hack: Nasty hard-coded knowledge that 'out' refers to the same file as 'file'
writeU32(out, 0);
out.flush();
long before = file.getFilePointer();
handler.writeTo(out);
out.flush();
long after = file.getFilePointer();
file.seek(before - 4);
file.writeInt((int)(after - before));
file.seek(after);
}
else
{
writeU32(out, 0);
}
}
//FIXME all codepaths to here are List<Source>; this code expects List<String>, which is consumed by
// readCompilationUnits() which expects strings too, then converts them back to List<Source>.
// this is abusive; we should create a temporary list for this purpose.
int read(FileSpec fileSpec,
SourceList sourceList,
SourcePath sourcePath,
ResourceContainer resources,
ResourceBundlePath bundlePath,
List sources,
List<CompilationUnit> units,
int[] checksums,
Map<QName, Long> swcDefSignatureChecksums,
Map<String, Long> swcFileChecksums,
Map<String, Long> archiveFileChecksums) throws IOException
{
if (!readVersion())
{
LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
throw new IOException(l10n.getLocalizedTextString(new ObsoleteCacheFileFormat()));
}
if (!readChecksum(checksums))
{
return -1;
}
Object[] pool = readConstantPool();
if (!readFileSpec(pool, fileSpec))
{
return -2;
}
if (!readSourceList(pool, sourceList))
{
return -3;
}
if (!readSourcePath(pool, sourcePath))
{
return -4;
}
if (!readResourceBundlePath(pool, bundlePath))
{
return -5;
}
Map<String, Object> owners = new HashMap<String, Object>();
if (!readSourceNames(pool, sources, units, owners))
{
return -6;
}
if (!readSwcDefSignatureChecksums(pool, swcDefSignatureChecksums))
{
return -7;
}
if (!readFileChecksums(pool, swcFileChecksums))
{
return -8;
}
if (!readFileChecksums(pool, archiveFileChecksums))
{
return -9;
}
int count = readCompilationUnits(pool, fileSpec, sourceList, sourcePath, resources, bundlePath,
sources, units, owners);
resources.refresh();
return count;
}
private boolean readVersion() throws IOException
{
int major = readU32(); // major version
int minor = readU32(); // minor version
return (major == major_version) && (minor == minor_version);
}
private boolean readChecksum(int[] checksums) throws IOException
{
int targetChecksum = readU32(); // checksum
int targetCmdChecksum = readU32();
int targetLinkerChecksum = readU32();
int targetSwcChecksum = readU32();
/* String description = */ new String(readBytes(readU32()));
boolean result = checksums[1] == targetCmdChecksum;
checksums[0] = targetChecksum;
checksums[1] = targetCmdChecksum;
checksums[2] = targetLinkerChecksum;
checksums[3] = targetSwcChecksum;
return result;
}
private Object[] readConstantPool() throws IOException
{
// Read the offset of the constant pool and seek there
long constantPoolOffset = file.readLong();
long startingOffset = file.getFilePointer();
file.seek(constantPoolOffset);
Object[] pool = new Object[readU32()];
for (int i = 0; i < pool.length; i++)
{
switch (readU8())
{
case 1: // String
pool[i] = new String(readBytes(readU32()), "UTF8");
break;
case 2: // String[]
String[] strings = new String[readU32()];
for (int j = 0; j < strings.length; j++)
{
strings[j] = (String) pool[readU32()];
}
pool[i] = strings;
break;
case 3: // byte[]
pool[i] = readBytes(readU32());
break;
case 4: // QName
pool[i] = new QName((String) pool[readU32()], (String) pool[readU32()]);
break;
case 5: // MultiName
pool[i] = new MultiName((String[]) pool[readU32()], (String) pool[readU32()]);
break;
case 6: // MetaData
MetaData md = new MetaData((String) pool[readU32()], readU32());
for (int j = 0; j < md.count(); j++)
{
String key = (String) pool[readU32()];
if (key.length() > 0)
{
md.setKeyValue(j, key, (String) pool[readU32()]);
}
else
{
md.setValue(j, (String) pool[readU32()]);
}
}
pool[i] = md;
break;
default:
assert false;
}
}
// Now that we've read the constant pool from near the end of the file,
// seek back to where everything else is
file.seek(startingOffset);
return pool;
}
private boolean readSourceNames(Object[] pool, List<Object> sources, List<CompilationUnit> units, Map<String, Object> owners) throws IOException
{
int size = readU32();
if (sources != null) sources.clear();
if (units != null) units.clear();
for (int i = 0; i < size; i++)
{
String name = (String) pool[readU32()];
if (!"null".equals(name))
{
owners.put(name, new Integer(readU8()));
if (sources != null) sources.add(name);
}
else
{
if (sources != null) sources.add(null);
}
if (units != null) units.add(null);
}
return true;
}
private boolean readSwcDefSignatureChecksums(Object[] pool, Map<QName, Long> swcDefSignatureChecksums) throws IOException
{
int size = readU32();
for (int i = 0; i < size; i++)
{
QName qName = (QName) pool[readU32()];
long ts = readLong();
if (swcDefSignatureChecksums != null) swcDefSignatureChecksums.put(qName, new Long(ts));
}
return true;
}
private boolean readFileChecksums(Object[] pool, Map<String, Long> m) throws IOException
{
int size = readU32();
for (int i = 0; i < size; i++)
{
String fileName = (String) pool[readU32()];
long ts = readLong();
if (m != null) m.put(fileName, new Long(ts));
}
return true;
}
private boolean readFileSpec(Object[] pool, FileSpec fileSpec) throws IOException
{
boolean fileSpecDataExists = readU8()==1 ? true : false;
if (fileSpecDataExists && fileSpec == null)
{
LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
throw new IOException(l10n.getLocalizedTextString(new NoFileSpec()));
}
else if (!fileSpecDataExists)
{
return true;
}
int length = readU32();
String[] mimeTypes = new String[length];
for (int i = 0; i < length; i++)
{
mimeTypes[i] = (String) pool[readU32()];
}
length = readU32();
String[] sources = new String[length];
for (int i = 0; i < length; i++)
{
sources[i] = (String) pool[readU32()];
}
// check FileSpec
String[] targetMimeTypes = fileSpec.getMimeTypes();
if ((length = targetMimeTypes.length) == mimeTypes.length)
{
for (int i = 0; i < length; i++)
{
if (!mimeTypes[i].equals(targetMimeTypes[i]))
{
return false;
}
}
}
else
{
return false;
}
Collection<Source> c = fileSpec.sources();
if (c.size() == sources.length)
{
Iterator<Source> it = c.iterator();
for (int i = 0; it.hasNext() && i < sources.length; i++)
{
Source s = it.next();
if (!s.getName().equals(sources[i]))
{
return false;
}
}
}
else
{
return false;
}
return true;
}
private boolean readSourceList(Object[] pool, SourceList sourceList) throws IOException
{
boolean sourceListDataExists = readU8()==1 ? true : false;
if (sourceListDataExists && sourceList == null)
{
LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
throw new IOException(l10n.getLocalizedTextString(new NoSourceList()));
}
else if (!sourceListDataExists)
{
return true;
}
int length = readU32();
String[] mimeTypes = new String[length];
for (int i = 0; i < length; i++)
{
mimeTypes[i] = (String) pool[readU32()];
}
length = readU32();
String[] paths = new String[length];
// classpath
for (int i = 0; i < length; i++)
{
paths[i] = (String) pool[readU32()];
}
length = readU32();
String[] sources = new String[length];
for (int i = 0; i < length; i++)
{
sources[i] = (String) pool[readU32()];
}
// check SourceList
String[] targetMimeTypes = sourceList.getMimeTypes();
if ((length = targetMimeTypes.length) == mimeTypes.length)
{
for (int i = 0; i < length; i++)
{
if (!mimeTypes[i].equals(targetMimeTypes[i]))
{
return false;
}
}
}
else
{
return false;
}
Collection<Source> c = sourceList.sources().values();
if (c.size() == sources.length)
{
Iterator<Source> it = c.iterator();
for (int i = 0; it.hasNext() && i < sources.length; i++)
{
Source s = it.next();
if (!s.getName().equals(sources[i]))
{
return false;
}
}
}
else
{
return false;
}
return true;
}
private boolean readSourcePath(Object[] pool, SourcePath sourcePath) throws IOException
{
boolean sourcePathDataExists = readU8()==1 ? true : false;
if (sourcePathDataExists && sourcePath == null)
{
LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
throw new IOException(l10n.getLocalizedTextString(new NoSourcePath()));
}
else if (!sourcePathDataExists)
{
return true;
}
int length = readU32();
String[] mimeTypes = new String[length];
for (int i = 0; i < length; i++)
{
mimeTypes[i] = (String) pool[readU32()];
}
length = readU32();
String[] paths = new String[length];
// classpath
for (int i = 0; i < length; i++)
{
paths[i] = (String) pool[readU32()];
}
length = readU32();
String[] sources = new String[length];
String[] classNames = new String[length];
// filename
for (int i = 0; i < length; i++)
{
classNames[i] = (String) pool[readU32()];
sources[i] = (String) pool[readU32()];
}
// check SourcePath
String[] targetMimeTypes = sourcePath.getMimeTypes();
if ((length = targetMimeTypes.length) == mimeTypes.length)
{
for (int i = 0; i < length; i++)
{
if (!mimeTypes[i].equals(targetMimeTypes[i]))
{
return false;
}
}
}
else
{
return false;
}
List<File> targetPaths = sourcePath.getPaths();
if ((length = targetPaths.size()) == paths.length)
{
for (int i = 0; i < length; i++)
{
if (!paths[i].equals(FileUtil.getCanonicalPath(targetPaths.get(i))))
{
return false;
}
}
}
else
{
return false;
}
//FIXME we're abusing this list by adding Strings into it temporarily and turning them to sources later
// I'm not going to ruin the type parameters on sources() (<String, Object>) just to make this work,
// instead I'll keep the abuse and the warnings
Map c = sourcePath.sources(); // <String, {Source, String}>
for (int i = 0; i < classNames.length; i++)
{
// TODO Fix this...
// C: the value should be a Source, not a String... It's sort of okay because readCompilationUnit()
// will replace the String...
c.put(classNames[i], sources[i]);
}
return true;
}
private boolean readResourceBundlePath(Object[] pool, ResourceBundlePath bundlePath) throws IOException
{
boolean resourceBundlePathDataExists = readU8()==1 ? true : false;
if (resourceBundlePathDataExists && bundlePath == null)
{
LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
throw new IOException(l10n.getLocalizedTextString(new NoSourcePath()));
}
else if (!resourceBundlePathDataExists)
{
return true;
}
int size;
int length = readU32();
String[] mimeTypes = new String[length];
for (int i = 0; i < length; i++)
{
mimeTypes[i] = (String) pool[readU32()];
}
length = readU32();
String[] locales = new String[length];
Map<String, String[]> rbDirectories = new HashMap<String, String[]>();
for (int i = 0; i < length; i++)
{
locales[i] = (String) pool[readU32()];
size = readU32();
String[] paths = new String[size];
// classpath
for (int j = 0; j < size; j++)
{
paths[j] = (String) pool[readU32()];
}
rbDirectories.put(locales[i], paths);
}
length = readU32();
String[] bundleNames = new String[length];
String[] sources = new String[length], list = null, list2 = null;
Object[] rFiles = new Object[length], rRoots = new Object[length];
// filename
for (int i = 0; i < length; i++)
{
bundleNames[i] = (String) pool[readU32()];
sources[i] = (String) pool[readU32()];
size = readU32();
rFiles[i] = list = new String[size];
rRoots[i] = list2 = new String[size];
for (int j = 0; j < size; j++)
{
list[j] = (String) pool[readU32()];
if ("null".equals(list[j]))
{
list[j] = null;
}
list2[j] = (String) pool[readU32()];
if ("null".equals(list2[j]))
{
list2[j] = null;
}
}
}
// check ResourceBundlePath
String[] targetMimeTypes = bundlePath.getMimeTypes();
if ((length = targetMimeTypes.length) == mimeTypes.length)
{
for (int i = 0; i < length; i++)
{
if (!mimeTypes[i].equals(targetMimeTypes[i]))
{
return false;
}
}
}
else
{
return false;
}
String[] targetLocales = bundlePath.getLocales();
if ((length = targetLocales.length) == locales.length)
{
for (int i = 0; i < length; i++)
{
if (!locales[i].equals(targetLocales[i]))
{
return false;
}
}
}
else
{
return false;
}
Map<String, List<File>> targetRBDirectories = bundlePath.getResourceBundlePaths();
if ((size = targetRBDirectories.size()) == rbDirectories.size())
{
for (int i = 0; i < size; i++)
{
List<File> targetPaths = targetRBDirectories.get(targetLocales[i]);
String[] paths = rbDirectories.get(locales[i]);
if ((length = targetPaths.size()) == paths.length)
{
for (int j = 0; j < length; j++)
{
if (!paths[j].equals(FileUtil.getCanonicalPath(targetPaths.get(j))))
{
return false;
}
}
}
else
{
return false;
}
}
}
else
{
return false;
}
Map c = bundlePath.sources(); // <String, {Source, Object[]}>
assert c.size() == 0;
for (int i = 0; i < bundleNames.length; i++)
{
//FIXME
// C: the value should be a Source, not an Object[]... It's sort of okay because readCompilationUnit()
// will replace the Object[]...
// we cannot do anything about the warning this generates
c.put(bundleNames[i], new Object[] { sources[i], rFiles[i], rRoots[i] });
}
return true;
}
//FIXME Sources is a List<String> when it enters the function, and a List<Source> when it leaves...
private int readCompilationUnits(Object[] pool, FileSpec fileSpec, SourceList sourceList, SourcePath sourcePath,
ResourceContainer resources, ResourceBundlePath bundlePath,
List<Object> sources, List<CompilationUnit> units, Map<String, Object> owners) throws IOException
{
int src_count = readU32();
RandomAccessFileInputStream in = new RandomAccessFileInputStream(file);
Map m = sourcePath.sources();
Map<String, String> mappings = new HashMap<String, String>();
Map<String, Object> rbMappings = new HashMap<String, Object>();
for (Iterator i = m.keySet().iterator(); i.hasNext();)
{
String className = (String) i.next();
String fileName = (String) m.get(className);
mappings.put(fileName, className);
}
m.clear();
m = bundlePath.sources();
for (Iterator i = m.keySet().iterator(); i.hasNext();)
{
String className = (String) i.next();
Object[] value = (Object[]) m.get(className);
String fileName = (String) value[0];
String[] rFiles = (String[]) value[1];
String[] rRoots = (String[]) value[2];
rbMappings.put(fileName, new Object[] { className, rFiles, rRoots });
}
m.clear();
for (int i = 0; i < src_count; i++)
{
readCompilationUnit(pool, mappings, rbMappings, in, fileSpec, sourceList, sourcePath, resources, bundlePath, owners);
}
for (int i = 0, len = sources == null ? 0 : sources.size(); i < len; i++)
{
String n = (String) sources.get(i);
Object obj = owners.get(n);
if (obj instanceof Source)
{
Source s = (Source) obj;
sources.set(i, s);
units.set(i, s.getCompilationUnit());
}
}
return src_count;
}
private void readMap(InputStream in, Object[] pool, Map<String, Object> map) throws IOException
{
int size = readU32(in);
for (int i = 0; i < size; i++)
{
String key = (String) pool[readU32(in)];
String value = (String) pool[readU32(in)];
map.put(key, value);
}
}
private void readCompilationUnit(Object[] pool, Map<String, String> mappings, Map<String, Object> rbMappings, InputStream in,
FileSpec fileSpec, SourceList sourceList, SourcePath sourcePath,
ResourceContainer resources, ResourceBundlePath bundlePath, Map<String, Object> owners)
throws IOException
{
PathResolver resolver = ThreadLocalToolkit.getPathResolver();
String name = (String) pool[readU32(in)];
String relativePath = (String) pool[readU32(in)];
String shortName = (String) pool[readU32(in)];
int owner = readU8(in);
VirtualFile pathRoot = null;
// 1 == SourceList
// 2 == SourcePath
// 4 == ResourceBundlePath
if ((owner == 1 ) || (owner == 2) || (owner == 4))
{
// C: Unfortunately, PathResolver itself is not a complete solution. For each type
// of VirtualFile, there must be a mechanism to recognize the name format and
// construct an appropriate VirtualFile instance.
pathRoot = resolver.resolve((String) pool[readU32(in)]);
}
boolean isInternal = (readU8(in) == 1);
boolean isRoot = (readU8(in) == 1);
boolean isDebuggable = (readU8(in) == 1);
boolean hasUnit = (readU8(in) == 1);
long fileTime = readLong(in);
final boolean hasSignatureChecksum = (readU8(in) == 1);
Long signatureChecksum = null;
if (hasSignatureChecksum)
{
assert hasUnit;
signatureChecksum = new Long(readLong(in));
// SignatureExtension.debug("READ CRC32: " + signatureChecksum + "\t--> " + name);
}
int size = readU32(in);
Set<VirtualFile> includes = new HashSet<VirtualFile>(size);
Map<VirtualFile, Long> includeTimes = new HashMap<VirtualFile, Long>(size);
for (int i = 0; i < size; i++)
{
String fileName = (String) pool[readU32(in)];
VirtualFile f = resolver.resolve(fileName);
long ts = readLong(in);
if (f == null)
{
// C: create an instance of DeletedFile...
f = new DeletedFile(fileName);
}
includes.add(f);
includeTimes.put(f, new Long(ts));
}
size = readU32(in);
LocalLogger logger = size == 0 ? null : new LocalLogger(null);
for (int i = 0; i < size; i++)
{
String path = (String) pool[readU32(in)];
if (path.length() == 0)
{
path = null;
}
String warning = (String) pool[readU32(in)];
if (warning.length() == 0)
{
warning = null;
}
String source = (String) pool[readU32(in)];
if (source.length() == 0)
{
source = null;
}
int line = readU32(in);
int col = readU32(in);
int errorCode = readU32(in);
logger.recordWarning(path,
line == -1 ? null : IntegerPool.getNumber(line),
col == -1 ? null : IntegerPool.getNumber(col),
warning,
source,
errorCode == -1 ? null : IntegerPool.getNumber(errorCode));
}
byte[] abc = (hasUnit) ? (byte[]) pool[readU32(in)] : null;
Source s = null;
if (owner == 0) // FileSpec
{
Collection<Source> c = fileSpec.sources();
for (Iterator<Source> i = c.iterator(); i.hasNext();)
{
s = i.next();
if (s.getName().equals(name))
{
Source.populateSource(s, fileTime, pathRoot, relativePath, shortName, fileSpec, isInternal, isRoot, isDebuggable,
includes, includeTimes, logger);
break;
}
}
}
else if (owner == 1) // SourceList
{
Collection<Source> c = sourceList.sources().values();
for (Iterator<Source> i = c.iterator(); i.hasNext();)
{
s = i.next();
if (s.getName().equals(name))
{
Source.populateSource(s, fileTime, pathRoot, relativePath, shortName, sourceList, isInternal, isRoot, isDebuggable,
includes, includeTimes, logger);
break;
}
}
}
else if (owner == 2) // SourcePath
{
Map<String, Source> c = sourcePath.sources();
String className = mappings.get(name);
if ((className != null) && !c.containsKey(className))
{
VirtualFile f = resolver.resolve(name);
if (f == null)
{
f = new DeletedFile(name);
}
s = Source.newSource(f, fileTime, pathRoot, relativePath, shortName, sourcePath, isInternal, isRoot, isDebuggable,
includes, includeTimes, logger);
c.put(className, s);
}
else
{
assert false : name;
}
}
else if (owner == 3) // ResourceContainer
{
if (resources == null)
{
LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();
throw new IOException(l10n.getLocalizedTextString(new NoResourceContainer()));
}
s = Source.newSource(abc, name, fileTime, pathRoot, relativePath, shortName, resources, isInternal, isRoot, isDebuggable,
includes, includeTimes, logger);
s = resources.addResource(s);
}
else if (owner == 4) // ResourceBundlePath
{
Map<String, Source> c = bundlePath.sources();
Object[] value = (Object[]) rbMappings.get(name);
String bundleName = (String) value[0];
String[] rNames = (String[]) value[1];
String[] rRoots = (String[]) value[2];
if (bundleName != null)
{
VirtualFile[] rFiles = new VirtualFile[rNames.length];
for (int i = 0; i < rFiles.length; i++)
{
if (rNames[i] != null)
{
rFiles[i] = resolver.resolve(rNames[i]);
if (rFiles[i] == null)
{
rFiles[i] = new DeletedFile(rNames[i]);
}
}
}
VirtualFile[] rRootFiles = new VirtualFile[rRoots.length];
for (int i = 0; i < rRootFiles.length; i++)
{
if (rRoots[i] != null)
{
rRootFiles[i] = resolver.resolve(rRoots[i]);
if (rRootFiles[i] == null)
{
rRootFiles[i] = new DeletedFile(rRoots[i]);
}
}
}
VirtualFile f = new ResourceFile(name, bundlePath.getLocales(), rFiles, rRootFiles);
s = Source.newSource(f, fileTime, pathRoot, relativePath, shortName, bundlePath, isInternal, isRoot, isDebuggable,
includes, includeTimes, logger);
c.put(bundleName, s);
}
else
{
assert false : name;
}
}
else
{
assert false : "owner = " + owner;
}
if (logger != null)
{
logger.setSource(s);
}
if (hasUnit)
{
CompilationUnit u = s.newCompilationUnit(null, new CompilerContext());
u.setSignatureChecksum(signatureChecksum);
u.bytes.addAll(abc);
u.setWorkflow(readU32(in));
u.setState(readU32(in));
size = readU32(in);
for (int i = 0; i < size; i++)
{
u.topLevelDefinitions.add((QName) pool[readU32(in)]);
}
size = readU32(in);
for (int i = 0; i < size; i++)
{
MultiName mName = (MultiName) pool[readU32(in)];
QName qName = (QName) pool[readU32(in)];
u.inheritanceHistory.put(mName, qName);
u.inheritance.add(qName);
}
size = readU32(in);
for (int i = 0; i < size; i++)
{
MultiName mName = (MultiName) pool[readU32(in)];
QName qName = (QName) pool[readU32(in)];
u.typeHistory.put(mName, qName);
u.types.add(qName);
}
size = readU32(in);
for (int i = 0; i < size; i++)
{
MultiName mName = (MultiName) pool[readU32(in)];
QName qName = (QName) pool[readU32(in)];
u.namespaceHistory.put(mName, qName);
u.namespaces.add(qName);
}
size = readU32(in);
for (int i = 0; i < size; i++)
{
MultiName mName = (MultiName) pool[readU32(in)];
QName qName = (QName) pool[readU32(in)];
u.expressionHistory.put(mName, qName);
u.expressions.add(qName);
}
boolean hasAuxGenerateInfo = readU8(in) == 1;
if (hasAuxGenerateInfo)
{
u.auxGenerateInfo = new HashMap<String, Object>();
String baseLoaderClass = (String) pool[readU32(in)];
u.auxGenerateInfo.put("baseLoaderClass", baseLoaderClass.length() > 0 ? baseLoaderClass : null);
String generateLoaderClass = (String) pool[readU32(in)];
u.auxGenerateInfo.put("generateLoaderClass", generateLoaderClass.length() > 0 ? generateLoaderClass : null);
String className = (String) pool[readU32(in)];
u.auxGenerateInfo.put("windowClass", className.length() > 0 ? className : null);
String preLoader = (String) pool[readU32(in)];
u.auxGenerateInfo.put("preloaderClass", preLoader.length() > 0 ? preLoader : null);
u.auxGenerateInfo.put("usePreloader", new Boolean(readU8(in) == 1));
Map<String, Object> rootAttributeMap = new HashMap<String, Object>();
u.auxGenerateInfo.put("rootAttributes", rootAttributeMap);
readMap(in, pool, rootAttributeMap);
Map<String, Object> rootAttributeEmbedVars = new HashMap<String, Object>();
u.auxGenerateInfo.put("rootAttributeEmbedVars", rootAttributeEmbedVars);
readMap(in, pool, rootAttributeEmbedVars);
Map<String, Object> rootAttributeEmbedNames = new HashMap<String, Object>();
u.auxGenerateInfo.put("rootAttributeEmbedNames", rootAttributeEmbedNames);
readMap(in, pool, rootAttributeEmbedNames);
}
if (readU8(in) == 1)
{
u.icon = (String) pool[readU32(in)];
u.iconFile = resolver.resolve((String) pool[readU32(in)]);
}
readAssets(pool, u, in);
}
if (s != null)
{
name = s.getName();
Object obj = owners.get(name);
if (obj == null || obj instanceof Source)
{
return;
}
int value = ((Integer) obj).intValue();
if ((s.isFileSpecOwner() && value == 0) ||
(s.isSourceListOwner() && value == 1) ||
(s.isSourcePathOwner() && value == 2) ||
(s.isResourceContainerOwner() && value == 3) ||
(s.isResourceBundlePathOwner() && value == 4))
{
owners.put(name, s);
}
}
}
private void readAssets(final Object[] pool, final CompilationUnit u, final InputStream in) throws IOException
{
int size = readU32(in);
if (size > 0)
{
final Map<String, AssetInfo> assets = new HashMap<String, AssetInfo>();
PathResolver resolver = ThreadLocalToolkit.getPathResolver();
for (int i = 0; i < size; i++)
{
String className = (String) pool[readU32(in)];
String pathName = (String) pool[readU32(in)];
VirtualFile f = null;
if (pathName.length() == 0)
{
f = null;
}
else
{
f = resolver.resolve(pathName);
if (f == null)
{
f = new DeletedFile(pathName);
}
}
assets.put(className, new AssetInfo(null, f, readLong(in), null));
}
int swfSize = readU32(in);
SizeLimitingInputStream in2 = new SizeLimitingInputStream(in, swfSize);
Movie movie = new Movie();
MovieDecoder movieDecoder = new MovieDecoder(movie);
TagDecoder tagDecoder = new TagDecoder(in2);
tagDecoder.parse(movieDecoder);
// For some reason, sometimes the process of decoding the movie does not read
// all that bytes that had been written previously. So, skip to the end.
in2.skipToEnd();
for (Frame frame : movie.frames)
{
for (Entry<String, Tag> e : frame.symbolClass.class2tag.entrySet())
{
String className = e.getKey();
DefineTag tag = (DefineTag) e.getValue();
AssetInfo assetInfo = assets.get(className);
assetInfo.setDefineTag(tag);
u.getAssets().add(className, assetInfo);
// We special case DefineFont tags so that the FontManager
// can cache them and avoid re-creating them on subsequent
// compiles.
if (fontManager != null && tag instanceof DefineFont)
{
VirtualFile f = assetInfo.getPath();
String path = null;
if (f != null)
{
path = f.getURL();
}
fontManager.loadDefineFont((DefineFont)tag, path);
}
}
}
}
}
// Methods for adding constant pool entries
private int addObject(Map<Object, Integer> pool, Object obj)
{
assert obj != null;
Integer index = pool.get(obj);
if (index == null)
{
index = IntegerPool.getNumber(pool.size());
pool.put(obj, index);
}
return index.intValue();
}
private int addBytes(Map<Object, Integer> pool, byte[] b)
{
return addObject(pool, b);
}
private int addString(Map<Object, Integer> pool, String s)
{
return addObject(pool, s);
}
private int addStrings(Map<Object, Integer> pool, String[] array)
{
assert array != null;
key.a1 = array;
Integer index = pool.get(key);
if (index == null)
{
for (int i = 0, size = array.length; i < size; i++)
{
addString(pool, array[i]);
}
index = IntegerPool.getNumber(pool.size());
pool.put(new ArrayKey(array), index);
}
return index.intValue();
}
private int addQName(Map<Object, Integer> pool, QName qName)
{
assert qName != null;
Integer index = pool.get(qName);
if (index == null)
{
addString(pool, qName.getNamespace());
addString(pool, qName.getLocalPart());
index = IntegerPool.getNumber(pool.size());
pool.put(qName, index);
}
return index.intValue();
}
private int addMultiName(Map<Object, Integer> pool, MultiName multiName)
{
assert multiName != null;
Integer index = pool.get(multiName);
if (index == null)
{
addStrings(pool, multiName.namespaceURI);
addString(pool, multiName.localPart);
index = IntegerPool.getNumber(pool.size());
pool.put(multiName, index);
}
return index.intValue();
}
/*
private int addMetaData(Map pool, flex2.compiler.abc.MetaData md)
{
assert md != null;
Integer index = (Integer) pool.get(md);
if (index == null)
{
addString(pool, md.getID());
for (int j = 0, count = md.count(); j < count; j++)
{
String key = md.getKey(j);
String val = md.getValue(j);
addString(pool, key == null ? "" : key);
addString(pool, val);
}
index = IntegerPool.getNumber(pool.size());
pool.put(md, index);
}
return index.intValue();
}
*/
// Low-level encoding methods
private void writeBytes(OutputStream out, byte[] b) throws IOException
{
out.write(b);
}
private void writeLong(OutputStream out, long num) throws IOException
{
out.write((int) (num >>> 56) & 0xFF);
out.write((int) (num >>> 48) & 0xFF);
out.write((int) (num >>> 40) & 0xFF);
out.write((int) (num >>> 32) & 0xFF);
out.write((int) (num >>> 24) & 0xFF);
out.write((int) (num >>> 16) & 0xFF);
out.write((int) (num >>> 8) & 0xFF);
out.write((int) num & 0xFF);
}
private void writeU32(OutputStream out, int num) throws IOException
{
out.write((num >>> 24) & 0xFF);
out.write((num >>> 16) & 0xFF);
out.write((num >>> 8) & 0xFF);
out.write( num & 0xFF);
}
private void writeU8(OutputStream out, int num) throws IOException
{
out.write(num & 0xFF);
}
private byte[] readBytes(int length) throws IOException
{
byte[] b = new byte[length];
file.readFully(b);
return b;
}
private int readU32() throws IOException
{
return file.readInt();
}
private int readU8() throws IOException
{
return file.read();
}
private byte[] readBytes(InputStream in, int length) throws IOException
{
byte[] b = new byte[length];
for (int size = 0, start = 0, len = length - size; (size = in.read(b, start, len)) != -1 && start + size < length;)
{
start += size;
len -= size;
}
return b;
}
private long readLong() throws IOException
{
return ((long) (readU32()) << 32) + (readU32() & 0xFFFFFFFFL);
}
private long readLong(InputStream in) throws IOException
{
return ((long) (readU32(in)) << 32) + (readU32(in) & 0xFFFFFFFFL);
}
private int readU32(InputStream in) throws IOException
{
return ((in.read() << 24) + (in.read() << 16) + (in.read() << 8) + in.read());
}
private int readU8(InputStream in) throws IOException
{
return in.read();
}
// Helper classes
private class ArrayKey
{
ArrayKey()
{
}
ArrayKey(String[] array)
{
a1 = array;
}
private String[] a1;
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
else if (obj instanceof ArrayKey)
{
String[] a2 = ((ArrayKey) obj).a1;
if (a1 == a2)
{
return true;
}
if (a1.length != a2.length)
{
return false;
}
for (int i = 0, size = a1.length; i < size; i++)
{
if (!a1[i].equals(a2[i]))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
@Override
public int hashCode()
{
int c = 0;
for (int i = 0, size = a1.length; i < size; i++)
{
c = (i == 0) ? a1[i].hashCode() : c ^ a1[i].hashCode();
}
return c;
}
}
public final class MetaData implements flex2.compiler.abc.MetaData
{
public MetaData(String id, int count)
{
this.id = id;
this.keys = new String[count];
this.values = new String[count];
}
private String id;
private String[] keys;
private String[] values;
public String getID()
{
return id;
}
public void setValue(int index, String value)
{
values[index] = value;
}
public void setKeyValue(int index, String key, String value)
{
keys[index] = key;
values[index] = value;
}
public String getKey(int index)
{
if (index < 0 || index >= count())
{
throw new ArrayIndexOutOfBoundsException();
}
else
{
return keys[index];
}
}
public String getValue(String key)
{
for (int i = 0, length = count(); i < length; i++)
{
if (key.equals(keys[i]))
{
return values[i];
}
}
return null;
}
public String getValue(int index)
{
if (index < 0 || index >= count())
{
throw new ArrayIndexOutOfBoundsException();
}
else
{
return values[index];
}
}
public Map<String, String> getValueMap()
{
Map<String, String> result = new HashMap<String, String>();
for (int i = 0, length = count(); i < length; i++)
{
result.put(keys[i], values[i]);
}
return result;
}
public int count()
{
return values != null ? values.length : 0;
}
}
// error messages
public static class ObsoleteCacheFileFormat extends CompilerMessage.CompilerInfo
{
private static final long serialVersionUID = -8594915455219662842L;
public ObsoleteCacheFileFormat()
{
super();
}
}
public static class NoFileSpec extends CompilerMessage.CompilerInfo
{
private static final long serialVersionUID = -5780228997078423591L;
public NoFileSpec()
{
super();
}
}
public static class NoSourceList extends CompilerMessage.CompilerInfo
{
private static final long serialVersionUID = 1489613684797688310L;
public NoSourceList()
{
super();
}
}
public static class NoSourcePath extends CompilerMessage.CompilerInfo
{
private static final long serialVersionUID = -4989314191998065597L;
public NoSourcePath()
{
super();
}
}
public static class NoResourceContainer extends CompilerMessage.CompilerInfo
{
private static final long serialVersionUID = -384784734412773490L;
public NoResourceContainer()
{
super();
}
}
}