blob: 41a6e91bad9542620845c949f4542c01ff1aff7f [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.tools;
import flash.swf.Frame;
import flash.swf.Movie;
import flash.swf.tags.DoABC;
import flex2.compiler.mxml.lang.StandardDefs;
import flex2.linker.LinkerConfiguration;
import flex2.linker.ConsoleApplication;
import flex2.linker.FlexMovie;
import macromedia.abc.BytecodeBuffer;
import macromedia.abc.ConstantPool;
import macromedia.abc.Decoder;
import macromedia.abc.Encoder;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Implementation of flex2.linker.PostLink which merges ABC blocks and
* runs a peephole optimizer on the byte code.
*/
public class PostLink implements flex2.linker.PostLink
{
public PostLink(boolean debug, boolean optimize)
{
this.debug = debug;
this.optimize = optimize;
as3metadata = StandardDefs.DefaultAS3Metadata;
}
public PostLink(LinkerConfiguration linkerConfiguration)
{
this.linkerConfiguration = linkerConfiguration;
debug = linkerConfiguration.debug() || linkerConfiguration.verboseStacktraces();
optimize = linkerConfiguration.optimize();
as3metadata = linkerConfiguration.getMetadataToKeep();
}
private boolean debug;
private boolean optimize;
private String[] as3metadata;
LinkerConfiguration linkerConfiguration;
public void run(ConsoleApplication app)
{
List<byte[]> abcList = new ArrayList<byte[]>(1);
abcList.add(null);
if (optimize)
{
merge(app.getABCs(), app.enableDebugger || debug, optimize);
}
else
{
for (int j = 0, codeSize = app.getABCs().size(); j < codeSize; j++)
{
byte[] abc = app.getABCs().get(j);
abcList.set(0, abc);
merge(abcList, app.enableDebugger || debug, optimize);
app.getABCs().set(j, abcList.get(0));
}
}
}
public void run(Movie movie)
{
// See if a flex movie has metadata it needs saved.
if (movie instanceof FlexMovie)
{
Set<String> moreMetadata = ((FlexMovie)movie).getMetadata();
if (moreMetadata.size() > 0)
{
// merge metadata from the flex movie with any existing
// metadata from the configuration.
Set<String> mergedMetaData = null;
if (as3metadata != null && as3metadata.length > 0)
{
mergedMetaData = new HashSet<String>();
mergedMetaData.addAll(moreMetadata);
mergedMetaData.addAll(Arrays.asList(as3metadata));
}
else
{
mergedMetaData = moreMetadata;
}
as3metadata = mergedMetaData.toArray(new String[mergedMetaData.size()]);
}
}
List<DoABC> doABCs = new ArrayList<DoABC>(1);
doABCs.add(null);
// abc merge... frame by frame... if merging fails on one of the frames, it will continue.
for (int i = 0, frameSize = movie.frames.size(); i < frameSize; i++)
{
Frame f = movie.frames.get(i);
if (optimize)
{
merge(f.doABCs, movie.enableDebugger != null || debug, optimize, "frame" + (i + 1));
}
else
{
for (int j = 0, codeSize = f.doABCs.size(); j < codeSize; j++)
{
DoABC abc = f.doABCs.get(j);
doABCs.set(0, abc);
merge(doABCs, movie.enableDebugger != null || debug, optimize, abc.name);
f.doABCs.set(j, doABCs.get(0));
}
}
}
}
private void merge(List<DoABC> doABCs, boolean debug, boolean runPeephole, String name)
{
Encoder encoder;
Decoder decoder;
boolean skipFrame = false;
int majorVersion = 0, minorVersion = 0, abcSize = doABCs.size(), flag = 1;
if (abcSize == 0)
{
return;
}
else if (abcSize == 1)
{
flag = doABCs.get(0).flag;
}
else
{
flag = 1;
}
Decoder[] decoders = new Decoder[abcSize];
ConstantPool[] pools = new ConstantPool[abcSize];
// create decoders...
for (int j = 0; j < abcSize; j++)
{
DoABC tag = doABCs.get(j);
BytecodeBuffer in = new BytecodeBuffer(tag.abc);
try
{
// ThreadLocalToolkit.logInfo(tag.name);
decoders[j] = new Decoder(in);
majorVersion = decoders[j].majorVersion;
minorVersion = decoders[j].minorVersion;
pools[j] = decoders[j].constantPool;
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
encoder = new Encoder(majorVersion, minorVersion);
// all the constant pools are merged here...
try
{
encoder.addConstantPools(pools);
if (!debug)
{
encoder.disableDebugging();
}
// always remove metadata...
encoder.removeMetadata();
// keep the following metadata
for (int m = 0; as3metadata != null && m < as3metadata.length; m++)
{
encoder.addMetadataToKeep(as3metadata[m]);
}
// always enable peephole optimization...
if (runPeephole)
{
encoder.enablePeepHole();
}
encoder.configure(decoders);
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
return;
}
// decode methodInfo...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.MethodInfo methodInfo = decoder.methodInfo;
try
{
for (int k = 0, infoSize = methodInfo.size(); k < infoSize; k++)
{
methodInfo.decode(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode metadataInfo...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.MetaDataInfo metadataInfo = decoder.metadataInfo;
try
{
for (int k = 0, infoSize = metadataInfo.size(); k < infoSize; k++)
{
metadataInfo.decode(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode classInfo...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.ClassInfo classInfo = decoder.classInfo;
try
{
for (int k = 0, infoSize = classInfo.size(); k < infoSize; k++)
{
classInfo.decodeInstance(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.ClassInfo classInfo = decoder.classInfo;
try
{
for (int k = 0, infoSize = classInfo.size(); k < infoSize; k++)
{
classInfo.decodeClass(k, 0, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode scripts...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.ScriptInfo scriptInfo = decoder.scriptInfo;
try
{
for (int k = 0, scriptSize = scriptInfo.size(); k < scriptSize; k++)
{
scriptInfo.decode(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode method bodies...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.MethodBodies methodBodies = decoder.methodBodies;
try
{
for (int k = 0, bodySize = methodBodies.size(); k < bodySize; k++)
{
methodBodies.decode(k, 2, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// 0: eager
// 1: lazy
DoABC doABC = new DoABC(name, flag);
doABC.abc = encoder.toABC();
if (doABC.abc != null)
{
doABCs.clear();
doABCs.add(doABC);
}
}
// C: This is for console applications. don't refactor this method and the other merge()
// before we ship.
private void merge(List<byte[]> abcList, boolean debug, boolean runPeephole)
{
Encoder encoder;
Decoder decoder;
boolean skipFrame = false;
int majorVersion = 0, minorVersion = 0, abcSize = abcList.size();
if (abcSize == 0)
{
return;
}
Decoder[] decoders = new Decoder[abcSize];
ConstantPool[] pools = new ConstantPool[abcSize];
// create decoders...
for (int j = 0; j < abcSize; j++)
{
byte[] abc = abcList.get(j);
BytecodeBuffer in = new BytecodeBuffer(abc);
try
{
decoders[j] = new Decoder(in);
majorVersion = decoders[j].majorVersion;
minorVersion = decoders[j].minorVersion;
pools[j] = decoders[j].constantPool;
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
encoder = new Encoder(majorVersion, minorVersion);
// all the constant pools are merged here...
try
{
encoder.addConstantPools(pools);
if (!debug)
{
encoder.disableDebugging();
}
// always remove metadata...
encoder.removeMetadata();
// keep the following metadata
/*
encoder.addMetadataToKeep(StandardDefs.MD_BINDABLE);
encoder.addMetadataToKeep(StandardDefs.MD_MANAGED);
encoder.addMetadataToKeep(StandardDefs.MD_CHANGEEVENT);
encoder.addMetadataToKeep(StandardDefs.MD_NONCOMMITTINGCHANGEEVENT);
encoder.addMetadataToKeep(StandardDefs.MD_TRANSIENT);
*/
// always enable peephole optimization...
if (runPeephole)
{
encoder.enablePeepHole();
}
encoder.configure(decoders);
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
return;
}
// decode methodInfo...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.MethodInfo methodInfo = decoder.methodInfo;
try
{
for (int k = 0, infoSize = methodInfo.size(); k < infoSize; k++)
{
methodInfo.decode(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode metadataInfo...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.MetaDataInfo metadataInfo = decoder.metadataInfo;
try
{
for (int k = 0, infoSize = metadataInfo.size(); k < infoSize; k++)
{
metadataInfo.decode(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode classInfo...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.ClassInfo classInfo = decoder.classInfo;
try
{
for (int k = 0, infoSize = classInfo.size(); k < infoSize; k++)
{
classInfo.decodeInstance(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.ClassInfo classInfo = decoder.classInfo;
try
{
for (int k = 0, infoSize = classInfo.size(); k < infoSize; k++)
{
classInfo.decodeClass(k, 0, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode scripts...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.ScriptInfo scriptInfo = decoder.scriptInfo;
try
{
for (int k = 0, scriptSize = scriptInfo.size(); k < scriptSize; k++)
{
scriptInfo.decode(k, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
// decode method bodies...
for (int j = 0; j < abcSize; j++)
{
decoder = decoders[j];
encoder.useConstantPool(j);
Decoder.MethodBodies methodBodies = decoder.methodBodies;
try
{
for (int k = 0, bodySize = methodBodies.size(); k < bodySize; k++)
{
methodBodies.decode(k, 2, encoder);
}
}
catch (Throwable ex)
{
StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
assert false : stringWriter.toString();
skipFrame = true;
break;
}
}
if (skipFrame)
{
return;
}
byte[] abc = encoder.toABC();
if (abc != null)
{
abcList.clear();
abcList.add(abc);
}
}
}