blob: ca473a032a4239f281deaabd55c1aa57d7941f4c [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 org.apache.royale.compiler.internal.embedding.transcoders;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.ABCEmitter;
import org.apache.royale.abc.instructionlist.InstructionList;
import org.apache.royale.abc.semantics.Label;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.semantics.Namespace;
import org.apache.royale.abc.semantics.Nsset;
import org.apache.royale.compiler.common.ISourceLocation;
import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.definitions.references.IResolvedQualifiersReference;
import org.apache.royale.compiler.definitions.references.ReferenceFactory;
import org.apache.royale.compiler.embedding.EmbedAttribute;
import org.apache.royale.compiler.internal.abc.ClassGeneratorHelper;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase;
import org.apache.royale.compiler.internal.embedding.EmbedData;
import org.apache.royale.compiler.internal.embedding.EmbedData.SkinClassInfo;
import org.apache.royale.compiler.internal.tree.as.FileNode;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.problems.EmbedNoSkinClassProblem;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.InternalCompilerProblem;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.swf.tags.ICharacterTag;
import org.apache.royale.swf.tags.ITag;
import com.google.common.collect.ImmutableList;
/**
* Handle the embedding of Skin assets
*/
public class SkinTranscoder extends TranscoderBase
{
/**
* @param data The embedding data.
* @param workspace The workspace.
*/
public SkinTranscoder(EmbedData data, Workspace workspace, SkinClassInfo skinClassInfo)
{
super(data, workspace);
iBorderReference = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".IBorder");
iFlexDisplayObjectReference = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".IFlexDisplayObject");
iFlexAssetReference = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".IFlexAsset");
this.skinClass = "";
this.skinClassInfo = skinClassInfo;
}
private String skinClass;
private final SkinClassInfo skinClassInfo;
private final IResolvedQualifiersReference iBorderReference;
private final IResolvedQualifiersReference iFlexDisplayObjectReference;
private final IResolvedQualifiersReference iFlexAssetReference;
/**
* {@inheritDoc}
*/
@Override
public boolean analyze(ISourceLocation location, Collection<ICompilerProblem> problems)
{
boolean result = super.analyze(location, problems);
if (!result)
return false;
baseClassQName = skinClass;
return result;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean setAttribute(EmbedAttribute attribute)
{
boolean isSupported = true;
switch (attribute)
{
case SKIN_CLASS:
skinClass = (String)data.getAttribute(EmbedAttribute.SKIN_CLASS);
break;
default:
isSupported = super.setAttribute(attribute);
}
return isSupported;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean checkAttributeValues(ISourceLocation location, Collection<ICompilerProblem> problems)
{
if (skinClass.length() == 0)
{
problems.add(new EmbedNoSkinClassProblem(location));
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
protected Map<String, ICharacterTag> doTranscode(Collection<ITag> tags, Collection<ICompilerProblem> problems)
{
// no tags related to skins, as all it's only a generated class
return Collections.emptyMap();
}
/**
* {@inheritDoc}
*/
@Override
public FileNode buildAST(Collection<ICompilerProblem> problems, String filename)
{
// TODO: remove when all other transcoders updated to generate ABC - should never be called.
assert false : "buildAST() called on a SkinTranscoder";
return null;
}
/**
* {@inheritDoc}
*/
@Override
public byte[] buildABC(ICompilerProject project, Collection<ICompilerProblem> problems)
{
ABCEmitter emitter = new ABCEmitter();
final String classQName = data.getQName();
Name className = new Name(classQName);
IDefinition baseClass = project.resolveQNameToDefinition(skinClass);
assert (baseClass instanceof ClassDefinition) : "skinClass does not resolve to a class";
ClassDefinition baseClassDef = (ClassDefinition)baseClass;
Collection<Name> implementedInterfaces = new ImmutableList.Builder<Name>()
.add(iBorderReference.getMName())
.add(iFlexDisplayObjectReference.getMName())
.add(iFlexAssetReference.getMName())
.build();
//FIXME: move this to ClassGeneratorHelper
Namespace privateNs = new Namespace(ABCConstants.CONSTANT_PrivateNs, className.getSingleQualifier().getName() + ":" + className.getBaseName());
// generate the constructor:
// public function $className() extends $baseClassDef implements IBorder, IFlexDisplayObject, IFlexAsset
// {
// super();
InstructionList classITraitsInit = new InstructionList();
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
classITraitsInit.addInstruction(ABCConstants.OP_constructsuper, 0);
// generate:
// _measuredWidth = width;
Name _measuredWidth = null;
if (skinClassInfo.needsMeasuredWidth)
{
Name width = new Name("width");
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
classITraitsInit.addInstruction(ABCConstants.OP_getproperty, width);
_measuredWidth = new Name(ABCConstants.CONSTANT_Qname, new Nsset(privateNs), "_measuredWidth");
classITraitsInit.addInstruction(ABCConstants.OP_initproperty, _measuredWidth);
}
// generate:
// _measuredHeight = height;
Name _measuredHeight = null;
if (skinClassInfo.needsMeasuredHeight)
{
Name height = new Name("height");
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
classITraitsInit.addInstruction(ABCConstants.OP_getproperty, height);
_measuredHeight = new Name(ABCConstants.CONSTANT_Qname, new Nsset(privateNs), "_measuredHeight");
classITraitsInit.addInstruction(ABCConstants.OP_initproperty, _measuredHeight);
}
// generate: TODO: currently we don't wrap this call in a try/catch, as it
// seems unnecessary. Change if need be.
// try
// {
// name = NameUtil.createUniqueName(this);
// }
// catch(e:Error)
// {
// }
if (!skinClassInfo.royaleMovieClipOrSprite)
{
Name name = new Name("name");
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
Name nameUtil = ReferenceFactory.packageQualifiedReference(workspace, UTILS_PACKAGE + ".NameUtil").getMName();
classITraitsInit.addInstruction(ABCConstants.OP_getlex, nameUtil);
classITraitsInit.addInstruction(ABCConstants.OP_getlocal0);
Object[] createUniqueName = new Object[] { new Name("createUniqueName"), 1 };
classITraitsInit.addInstruction(ABCConstants.OP_callproperty, createUniqueName);
classITraitsInit.addInstruction(ABCConstants.OP_initproperty, name);
}
// finish the constructor
classITraitsInit.addInstruction(ABCConstants.OP_returnvoid);
TypeDefinitionBase numberDef = (TypeDefinitionBase)project.getBuiltinType(BuiltinType.NUMBER);
Name numberName = numberDef.getMName(project);
// add all the member variables to the class
ClassGeneratorHelper classGen = new ClassGeneratorHelper(project, emitter, className, baseClassDef, implementedInterfaces, classITraitsInit);
// generate:
// public function get borderMetrics():EdgeMetrics
// {
// if (scale9Grid == null)
// {
// return EdgeMetrics.EMPTY;
// }
// else
// {
// return new EdgeMetrics(scale9Grid.left,
// scale9Grid.top,
// Math.ceil(measuredWidth - scale9Grid.right),
// Math.ceil(measuredHeight - scale9Grid.bottom));
// }
// }
if (skinClassInfo.needsIBorder && skinClassInfo.needsBorderMetrics)
{
InstructionList body = new InstructionList();
Name scale9Grid = new Name("scale9Grid");
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, scale9Grid);
Label trueLabel = new Label();
body.addInstruction(ABCConstants.OP_iftrue, trueLabel);
Name edgeMetrics = ReferenceFactory.packageQualifiedReference(workspace, CORE_PACKAGE + ".EdgeMetrics").getMName();
body.addInstruction(ABCConstants.OP_getlex, edgeMetrics);
body.addInstruction(ABCConstants.OP_getproperty, new Name("EMPTY"));
body.addInstruction(ABCConstants.OP_returnvalue);
body.labelNext(trueLabel);
body.addInstruction(ABCConstants.OP_findpropstrict, edgeMetrics);
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, scale9Grid);
body.addInstruction(ABCConstants.OP_getproperty, new Name("left"));
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, scale9Grid);
body.addInstruction(ABCConstants.OP_getproperty, new Name("top"));
Name math = new Name("Math");
body.addInstruction(ABCConstants.OP_getlex, math);
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, new Name("measuredWidth"));
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, scale9Grid);
body.addInstruction(ABCConstants.OP_getproperty, new Name("right"));
body.addInstruction(ABCConstants.OP_subtract);
Object[] ceil = new Object[] {new Name("ceil"), 1};
body.addInstruction(ABCConstants.OP_callproperty, ceil);
body.addInstruction(ABCConstants.OP_getlex, math);
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, new Name("measuredHeight"));
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, scale9Grid);
body.addInstruction(ABCConstants.OP_getproperty, new Name("bottom"));
body.addInstruction(ABCConstants.OP_subtract);
body.addInstruction(ABCConstants.OP_callproperty, ceil);
body.addInstruction(ABCConstants.OP_constructprop, new Object[] {edgeMetrics, 4});
body.addInstruction(ABCConstants.OP_returnvalue);
classGen.addITraitsGetter(new Name("borderMetrics"), edgeMetrics, body);
}
if (skinClassInfo.needsIFlexDisplayObject)
{
// generate:
// public function get measuredWidth():Number
// {
// return _measuredWidth;
// }
if (skinClassInfo.needsMeasuredWidth)
{
InstructionList body = new InstructionList();
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, _measuredWidth);
body.addInstruction(ABCConstants.OP_returnvalue);
classGen.addMemberVariable(_measuredWidth, numberName);
classGen.addITraitsGetter(new Name("measuredWidth"), numberName, body);
}
// generate:
// public function get measuredHeight():Number
// {
// return _measuredHeight;
// }
if (skinClassInfo.needsMeasuredHeight)
{
InstructionList body = new InstructionList();
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, _measuredHeight);
body.addInstruction(ABCConstants.OP_returnvalue);
classGen.addMemberVariable(_measuredHeight, numberName);
classGen.addITraitsGetter(new Name("measuredHeight"), numberName, body);
}
// generate:
// public function move(x:Number, y:Number):void
// {
// this.x = x;
// this.y = y;
// }
if (skinClassInfo.needsMove)
{
InstructionList body = new InstructionList();
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getlocal1);
body.addInstruction(ABCConstants.OP_setproperty, new Name("x"));
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getlocal2);
body.addInstruction(ABCConstants.OP_setproperty, new Name("y"));
body.addInstruction(ABCConstants.OP_returnvoid);
Collection<Name> paramTypes = new ImmutableList.Builder<Name>()
.add(numberName)
.add(numberName)
.build();
classGen.addITraitsMethod(new Name("move"), paramTypes, new Name("void"), Collections.<Object>emptyList(), false, false, false, body);
}
// generate:
// public function setActualSize(newWidth:Number, newHeight:Number):void
// {
// if (width != newWidth)
// {
// width = newWidth;
// }
// if (height != newHeight)
// {
// height = newHeight;
// }
// }
if (skinClassInfo.needsSetActualSize)
{
InstructionList body = new InstructionList();
Name width = new Name("width");
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, width);
body.addInstruction(ABCConstants.OP_getlocal1);
Label trueLabel = new Label();
body.addInstruction(ABCConstants.OP_ifeq, trueLabel);
body.addInstruction(ABCConstants.OP_findproperty, width);
body.addInstruction(ABCConstants.OP_getlocal1);
body.addInstruction(ABCConstants.OP_setproperty, width);
body.labelNext(trueLabel);
Name height = new Name("height");
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_getproperty, height);
body.addInstruction(ABCConstants.OP_getlocal2);
trueLabel = new Label();
body.addInstruction(ABCConstants.OP_ifeq, trueLabel);
body.addInstruction(ABCConstants.OP_findproperty, height);
body.addInstruction(ABCConstants.OP_getlocal2);
body.addInstruction(ABCConstants.OP_setproperty, height);
body.labelNext(trueLabel);
body.addInstruction(ABCConstants.OP_returnvoid);
Collection<Name> paramTypes = new ImmutableList.Builder<Name>()
.add(numberName)
.add(numberName)
.build();
classGen.addITraitsMethod(new Name("setActualSize"), paramTypes, new Name("void"), Collections.<Object>emptyList(), false, false, false, body);
}
}
// generate:
// override public function toString():String
// {
// return NameUtil.displayObjectToString(this);
// }
if (!skinClassInfo.royaleMovieClipOrSprite)
{
InstructionList body = new InstructionList();
body.addInstruction(ABCConstants.OP_getlocal0);
body.addInstruction(ABCConstants.OP_pushscope);
Name nameUtil = ReferenceFactory.packageQualifiedReference(workspace, UTILS_PACKAGE + ".NameUtil").getMName();
body.addInstruction(ABCConstants.OP_getlex, nameUtil);
body.addInstruction(ABCConstants.OP_getlocal0);
Object[] displayObjectToString = new Object[] { new Name("displayObjectToString"), 1 };
body.addInstruction(ABCConstants.OP_callproperty, displayObjectToString);
body.addInstruction(ABCConstants.OP_returnvalue);
TypeDefinitionBase stringDef = (TypeDefinitionBase)project.getBuiltinType(BuiltinType.STRING);
Name stringName = stringDef.getMName(project);
classGen.addITraitsMethod(new Name("toString"), Collections.<Name>emptyList(), stringName, Collections.<Object>emptyList(), false, false, true, body);
}
classGen.finishScript();
try
{
return emitter.emit();
}
catch (Exception e)
{
problems.add(new InternalCompilerProblem(e));
return null;
}
}
@Override
public boolean equals(Object o)
{
if (!super.equals(o))
return false;
if (!(o instanceof SkinTranscoder))
return false;
SkinTranscoder t = (SkinTranscoder)o;
if (!skinClass.equals(t.skinClass))
return false;
return true;
}
@Override
public int hashCode()
{
int hashCode = super.hashCode();
hashCode += skinClass.hashCode();
return hashCode;
}
}