| /* |
| * |
| * 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.flex.compiler.internal.as.codegen; |
| |
| import static org.apache.flex.abc.ABCConstants.*; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| 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.TreeSet; |
| import java.util.Vector; |
| |
| import org.apache.flex.abc.ABCConstants; |
| import org.apache.flex.abc.visitors.IABCVisitor; |
| import org.apache.flex.abc.visitors.IClassVisitor; |
| import org.apache.flex.abc.visitors.IMetadataVisitor; |
| import org.apache.flex.abc.visitors.IMethodBodyVisitor; |
| import org.apache.flex.abc.visitors.IMethodVisitor; |
| import org.apache.flex.abc.visitors.IScriptVisitor; |
| import org.apache.flex.abc.visitors.ITraitVisitor; |
| import org.apache.flex.abc.visitors.ITraitsVisitor; |
| import org.apache.flex.abc.graph.IBasicBlock; |
| import org.apache.flex.abc.semantics.ClassInfo; |
| import org.apache.flex.abc.semantics.ExceptionInfo; |
| import org.apache.flex.abc.semantics.InstanceInfo; |
| import org.apache.flex.abc.semantics.Instruction; |
| import org.apache.flex.abc.semantics.Label; |
| import org.apache.flex.abc.semantics.Metadata; |
| import org.apache.flex.abc.semantics.MethodBodyInfo; |
| import org.apache.flex.abc.semantics.MethodInfo; |
| import org.apache.flex.abc.semantics.Name; |
| import org.apache.flex.abc.semantics.Namespace; |
| import org.apache.flex.abc.semantics.Nsset; |
| import org.apache.flex.abc.semantics.PooledValue; |
| import org.apache.flex.abc.semantics.ScriptInfo; |
| import org.apache.flex.abc.semantics.Trait; |
| import org.apache.flex.abc.semantics.Traits; |
| import org.apache.flex.abc.instructionlist.InstructionList; |
| import org.apache.flex.compiler.constants.IMetaAttributeConstants; |
| import org.apache.flex.compiler.definitions.IClassDefinition; |
| import org.apache.flex.compiler.definitions.IDefinition; |
| import org.apache.flex.compiler.definitions.INamespaceDefinition; |
| import org.apache.flex.compiler.internal.definitions.AmbiguousDefinition; |
| import org.apache.flex.compiler.internal.definitions.ClassDefinition; |
| import org.apache.flex.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.flex.compiler.internal.definitions.PackageDefinition; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter.AccessValue; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter.ClassificationValue; |
| import org.apache.flex.compiler.internal.legacy.ASDefinitionFilter.SearchScopeValue; |
| import org.apache.flex.compiler.internal.legacy.ASScopeUtils; |
| import org.apache.flex.compiler.internal.legacy.MemberedDefinitionUtils; |
| import org.apache.flex.compiler.internal.projects.CompilerProject; |
| import org.apache.flex.compiler.internal.projects.FlexJSProject; |
| import org.apache.flex.compiler.projects.ICompilerProject; |
| import org.apache.flex.compiler.scopes.IASScope; |
| import org.apache.flex.compiler.tree.as.IImportNode.ImportKind; |
| import org.apache.flex.compiler.units.ICompilationUnit; |
| import org.apache.flex.abc.ABCEmitter; |
| |
| /** |
| * JSEmitter is used in two different phases. During compilation it acts as a |
| * visitor that gets called for each layer in the AST (see Emitter*IVisitor |
| * classes). The second phase is triggered by JSCompilationUnit's |
| * handleABCBytesRequest() and uses emitCode() for building the JavaScript code |
| * (see emit* methods). This implementation is part of FalconJS. For more |
| * details on FalconJS see org.apache.flex.compiler.JSDriver. |
| */ |
| @SuppressWarnings("nls") |
| public class JSEmitter implements IABCVisitor |
| { |
| protected JSOutputStream w = new JSOutputStream(); |
| protected Set<String> m_importNames = new TreeSet<String>(); |
| protected Set<String> m_useNames = new TreeSet<String>(); |
| |
| protected Vector<EmitterClassVisitor> definedClasses = new Vector<EmitterClassVisitor>(); |
| private Vector<MethodInfo> methodInfos = new Vector<MethodInfo>(); |
| private Vector<MethodBodyInfo> methodBodies = new Vector<MethodBodyInfo>(); |
| private Vector<ScriptInfo> scriptInfos = new Vector<ScriptInfo>(); |
| protected final Boolean emitExterns = JSSharedData.m_useClosureLib && false; // only set emitExterns to true if you want to generate extern files, i.e. svg.js. |
| |
| private JSSharedData m_sharedData; |
| private EmitterClassVisitor m_currentClassVisitor; |
| private String m_methodPrologue = ""; |
| private String m_methodPostlogue = ""; |
| private static String EXTERN_PREFIX = "// EXTERN "; |
| // private ICompilationUnit.Operation m_buildPhase; |
| private IABCVisitor m_visitor = null; |
| protected ICompilerProject m_project = null; |
| protected JSGenerator m_generator = null; |
| private IASScope m_currentScope = null; |
| protected Map<MethodInfo, FunctionDefinition> m_methodInfoToDefinition = new HashMap<MethodInfo, FunctionDefinition>(); |
| protected Map<FunctionDefinition, MethodInfo> m_definitionToMethodInfo = new HashMap<FunctionDefinition, MethodInfo>(); |
| protected String m_packageName = null; |
| |
| public JSEmitter(JSSharedData sharedData, |
| ICompilationUnit.Operation buildPhase, |
| ICompilerProject project, |
| JSGenerator generator) |
| { |
| this.w = new JSOutputStream(); |
| m_sharedData = sharedData; |
| m_currentClassVisitor = null; |
| // m_buildPhase = buildPhase; |
| m_project = project; |
| m_generator = generator; |
| } |
| |
| protected void writeString(String str) |
| { |
| w.writeString(str); |
| } |
| |
| public void setVisitor(IABCVisitor visitor) |
| { |
| m_visitor = visitor; |
| } |
| |
| public byte[] emit() |
| throws Exception |
| { |
| // Metadata |
| /* |
| * for (Metadata md : metadataPool.getValues()) { // name final String |
| * name = md.getName(); // items count assert md.getKeys().length == |
| * md.getValues().length; // metadata keys for (final String key : |
| * md.getKeys()) { writeString( "// META[" + name + "]: key=" + key + |
| * "\n" ); } // metadata values for (final String value : |
| * md.getValues()) { writeString( "// META[" + name + "]: value=" + |
| * value + "\n" ); } } |
| */ |
| m_currentClassVisitor = null; |
| |
| // first write out the framework class |
| EmitterClassVisitor frameWorkClassVisitor = null; |
| for (EmitterClassVisitor clz : this.definedClasses) |
| { |
| InstanceInfo ii = clz.instanceInfo; |
| if (!ii.isInterface() && JSGeneratingReducer.getBasenameFromName(ii.name).equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| { |
| m_currentClassVisitor = clz; |
| final IDefinition def = getDefinition(ii.name); |
| final String packageName = def.getPackageName(); |
| if (packageName.isEmpty()) |
| { |
| frameWorkClassVisitor = clz; |
| emitClass(clz); |
| } |
| } |
| } |
| |
| // write out all the other classes |
| for (EmitterClassVisitor clz : this.definedClasses) |
| { |
| if (clz != frameWorkClassVisitor) |
| { |
| m_currentClassVisitor = clz; |
| |
| if (clz.instanceInfo.isInterface()) |
| emitInterface(clz); |
| else |
| emitClass(clz); |
| } |
| } |
| |
| // static initializers? |
| /* |
| * for (EmitterClassVisitor clz : this.definedClasses) { |
| * m_currentClassVisitor = clz; |
| * w.writeU30(getMethodId(clz.classInfo.cInit)); |
| * emitTraits(clz.classTraits); } |
| */ |
| |
| m_currentClassVisitor = null; |
| |
| // write out free floating code: package functions and package variables. |
| for (ScriptInfo si : this.scriptInfos) |
| { |
| emitScriptInfo(si); |
| } |
| |
| return w.toByteArray(); |
| } |
| |
| protected MethodBodyInfo findMethodBodyInfo(MethodInfo mi) |
| { |
| for (int i = 0; i < this.methodBodies.size(); i++) |
| { |
| MethodBodyInfo mbi = methodBodies.elementAt(i); |
| if (mbi.getMethodInfo() == mi) |
| return mbi; |
| } |
| |
| // Don't throw, because interfaces don't have MethodBodyInfo: |
| // throw new IllegalArgumentException("Unable to find MethodBodyInfo for " + mi); |
| return null; |
| } |
| |
| protected String emitParameters(MethodInfo mi) throws Exception |
| { |
| final FunctionDefinition fdef = this.m_methodInfoToDefinition.get(mi); |
| String[] chunk = JSGeneratingReducer.emitParameters(m_project, fdef, mi); |
| // String[] chunk = JSGeneratingReducer.emitParameters(mi); |
| |
| final String a_priori_insns = chunk[0]; |
| final String code = chunk[1]; |
| |
| writeString(code); |
| return a_priori_insns; |
| } |
| |
| protected void emitInstanceTraits(Traits traits, MethodInfo ctor, String packageName, String className, String superClass, |
| Boolean isExtern, Boolean isInterface, Boolean isPackageFunction) throws Exception |
| { |
| // private void emitTraits(Traits traits, Boolean isInstanceTraits, MethodInfo ctor, String packageName, String className, String methodPrefix, String assignmentOp, String separator, String indent ) throws Exception |
| |
| if (JSSharedData.m_useClosureLib) |
| { |
| if (isExtern) |
| emitTraits(traits, true, isExtern, isInterface, isPackageFunction, ctor, packageName, className, superClass, className + ".prototype.", "=", ";", EXTERN_PREFIX); |
| else |
| { |
| final String fullName = createFullName(packageName, className); |
| emitTraits(traits, true, isExtern, isInterface, isPackageFunction, ctor, packageName, className, superClass, fullName + ".prototype.", "=", ";", ""); |
| } |
| } |
| else |
| emitTraits(traits, true, isExtern, isInterface, isPackageFunction, ctor, packageName, className, superClass, "this.", "", ",", isExtern ? EXTERN_PREFIX : " "); |
| } |
| |
| protected Boolean isExtern(Name name) |
| { |
| if (name != null) |
| { |
| final IDefinition def = JSGeneratingReducer.getDefinitionForName(m_project, name); |
| if (def == null) |
| { |
| JSGeneratingReducer.getDefinitionForName(m_project, name); |
| throw new IllegalArgumentException("isExtern: can't find definition for name " + name.toString()); |
| } |
| |
| if (def.getMetaTagByName(JSSharedData.EXTERN_METATAG) != null) |
| return true; |
| } |
| return false; |
| } |
| |
| /** packageName can be "" */ |
| protected IDefinition getDefinition(Name name) |
| { |
| if (name == null) |
| return null; |
| |
| final IDefinition def = JSGeneratingReducer.getDefinitionForNameByScope(m_project, m_project.getScope(), name); |
| |
| if (def == null) |
| { |
| throw new IllegalArgumentException("getDefinition: can't find definition for name " + name.toString()); |
| } |
| |
| return def; |
| } |
| |
| /** packageName can be "" */ |
| private ClassDefinition getClassDefinition(String shortClassName, String packageName) |
| { |
| if (packageName.isEmpty()) |
| return getClassDefinition(shortClassName); |
| else |
| return getClassDefinition(packageName + "." + shortClassName); |
| } |
| |
| /** expects className to be fully-qualified if it's in a package */ |
| private ClassDefinition getClassDefinition(String className) |
| { |
| IDefinition definitionContext = null; |
| ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, SearchScopeValue.ALL_SCOPES, AccessValue.ALL, definitionContext); |
| IDefinition def = ASScopeUtils.findDefinitionByName(m_project.getScope(), m_project, className, filter); |
| if (def instanceof ClassDefinition) |
| { |
| final ClassDefinition classDef = (ClassDefinition)def; |
| return classDef; |
| } |
| return null; |
| } |
| |
| // Symbol support |
| private Boolean isSymbolClass(String className) |
| { |
| final ClassDefinition classDef = getClassDefinition(className); |
| if (classDef != null) |
| { |
| for (String implementedInterface : classDef.getImplementedInterfacesAsDisplayStrings()) |
| { |
| if (implementedInterface.equals(JSSharedData.SYMBOL_INTERFACE_NAME)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void emitClassInfo(String packageName, String className, String superClass, Boolean isDynamic, Boolean isFinal) |
| { |
| final String fullClassNameWithoutDot = createFullName(packageName, className); |
| String fullClassName = fullClassNameWithoutDot; |
| if (!fullClassName.isEmpty()) |
| fullClassName += "."; |
| |
| if (superClass.isEmpty()) |
| superClass = "Object"; |
| |
| // Namespace support |
| final IDefinition def = JSSharedData.instance.getDefinition(fullClassNameWithoutDot); |
| if (def != null) |
| { |
| def.getQualifiedName(); |
| } |
| } |
| |
| private void emitClassTraits(Traits traits, MethodInfo staticInitMI, |
| String packageName, String className, String superClass, |
| Boolean isExtern, Boolean isPackageFunction, |
| Boolean isDynamicClass, Boolean isFinalClass, |
| Boolean emitClassPrologue) throws Exception |
| { |
| final Boolean isInterface = false; |
| writeString("\n\n"); |
| |
| // TODO: generalize |
| if (isExtern || packageName.startsWith("goog")) |
| return; |
| |
| if (emitClassPrologue && !className.isEmpty()) |
| { |
| emitClassInfo(packageName, className, superClass, isDynamicClass, isFinalClass); |
| } |
| |
| String fullClassName = createFullName(packageName, className); |
| if (!fullClassName.isEmpty()) |
| fullClassName += "."; |
| |
| emitTraits(traits, false, isExtern, isInterface, isPackageFunction, staticInitMI, packageName.replaceFirst(JSSharedData.ROOT_NAME, ""), className, superClass, fullClassName, "=", ";", ""); |
| |
| /* |
| * if( isExtern ) emitTraits( traits, false, isExtern, staticInitMI, |
| * packageName, className, superClass, fullClassName, "=", ";", "" ); |
| * else emitTraits( traits, false, isExtern, staticInitMI, |
| * packageName.replaceFirst( JSSharedData.ROOT_NAME, ""), className, |
| * superClass, fullClassName, "=", ";", "" ); |
| */ |
| } |
| |
| private String nameToString(Name name) |
| { |
| String baseName = JSGeneratingReducer.getBasenameFromName(name); |
| if (baseName == null || baseName.isEmpty()) |
| { |
| String packageName = ""; |
| final Nsset nsset = name.getQualifiers(); |
| if (nsset != null && nsset.length() > 0) |
| { |
| packageName = nsset.iterator().next().getName(); |
| if (!packageName.isEmpty()) |
| baseName = "{" + packageName + "}::" + baseName; |
| } |
| } |
| else |
| { |
| final IDefinition def = getDefinition(name); |
| if (name != null && !name.isTypeName()) |
| { |
| final String packageName = def.getPackageName(); |
| if (packageName != null && !packageName.isEmpty()) |
| { |
| final String fullName = "{" + packageName + "}::" + baseName; |
| return fullName; |
| } |
| } |
| } |
| return baseName; |
| |
| } |
| |
| // see Namespace::getKindString |
| /* |
| * private String getKindString(Namespace ns) { switch(ns.getKind()) { case |
| * ABCConstants.CONSTANT_Namespace: return "Ns"; case |
| * ABCConstants.CONSTANT_PackageNs: return "PackageNs"; case |
| * ABCConstants.CONSTANT_PackageInternalNs: return "PackageInternalNs"; case |
| * ABCConstants.CONSTANT_ProtectedNs: return "ProtectedNs"; case |
| * ABCConstants.CONSTANT_ExplicitNamespace: return "ExplicitNs"; case |
| * ABCConstants.CONSTANT_StaticProtectedNs: return "StaticProtectedNs"; case |
| * ABCConstants.CONSTANT_PrivateNs: return "PrivateNs"; } return "UnknownNs" |
| * ; } |
| */ |
| |
| private String mangleName(String baseName, Namespace ns) |
| { |
| String name = baseName + "::"; |
| String nsStr = ns.toString(); |
| |
| // workaround for Falcon bug. |
| // ns.toString() is now returning: |
| // ProtectedNs:"tests:Test" |
| // instead of |
| // ProtectedNs:"tests.Test" |
| if (ns.getName().contains(":") /* |
| * && ns.getKind() != |
| * ABCConstants.CONSTANT_Namespace |
| */) |
| { |
| String tmp = ns.getName(); |
| |
| // support for "use namespace" |
| /* |
| * final INamespaceDefinition ins = |
| * JSSharedData.instance.getNamespace(tmp); if( ins != null ) { tmp |
| * = ins.getQualifiedName(); } |
| */ |
| tmp = tmp.replaceAll(":", "."); |
| |
| // another workaround for a Falcon bug. |
| // WRONG: PrivateNs:"ClassPrivateNS:tests:Test" |
| // CORRECT: PrivateNs:ClassPrivateNS:"tests:Test" |
| if (tmp.startsWith("ClassPrivateNS.")) |
| tmp = tmp.replace("ClassPrivateNS.", "ClassPrivateNS:"); |
| nsStr = nsStr.substring(0, nsStr.length() - ns.getName().length() - 2) + tmp; |
| } |
| nsStr = nsStr.replaceAll("\\\"", ""); |
| |
| Boolean colon = false; |
| for (String part : nsStr.split(":")) |
| { |
| if (colon) |
| name += ":"; |
| else |
| colon = true; |
| part = part.replaceAll("\\\"", ""); |
| |
| if (part.equals("Ns")) |
| name += JSSharedData.CONSTANT_Namespace; |
| else if (part.equals("PackageNs")) |
| name += JSSharedData.CONSTANT_PackageNs; |
| else if (part.equals("PackageInternalNs")) |
| name += JSSharedData.CONSTANT_PackageInternalNs; |
| else if (part.equals("ProtectedNs")) |
| name += JSSharedData.CONSTANT_ProtectedNs; |
| else if (part.equals("ExplicitNs")) |
| name += JSSharedData.CONSTANT_ExplicitNamespace; |
| else if (part.equals("StaticProtectedNs")) |
| name += JSSharedData.CONSTANT_StaticProtectedNs; |
| else if (part.equals("PrivateNs")) |
| name += JSSharedData.CONSTANT_PrivateNs; |
| else if (part.equals("ClassPrivateNS")) |
| name += JSSharedData.CONSTANT_ClassPrivateNS; |
| else |
| name += part; |
| } |
| |
| return name; |
| } |
| |
| private void emitNamespaceInfo(EmitterClassVisitor clz, |
| String packageName, String className, String superClass) throws Exception |
| { |
| // check whether this class is tagged with [ClassInfo] |
| if (!className.isEmpty()) |
| { |
| final Traits instanceTraits = clz.instanceTraits; |
| final String fullName = createFullName(packageName, className); |
| boolean comma = false; |
| String names = ""; |
| for (Trait t : instanceTraits) |
| { |
| final Name name = t.getNameAttr("name"); |
| final Nsset nsset = name.getQualifiers(); |
| if (nsset != null && nsset.length() > 0) |
| { |
| if (comma) |
| names += ",\n"; |
| else |
| comma = true; |
| for (Namespace ns : nsset) |
| { |
| names += " \"" + mangleName(name.getBaseName(), ns) + "\" : true"; |
| } |
| } |
| } |
| |
| // add _NAMESPACES member to class |
| writeString("\n"); |
| writeString("/**\n"); |
| writeString(" * Member: " + fullName + "._NAMESPACES\n"); |
| writeString(" * @const\n"); |
| writeString(" * @type {Object}\n"); |
| writeString(" */\n"); |
| |
| if (names.isEmpty()) |
| writeString(fullName + "._NAMESPACES = {};\n"); |
| else |
| { |
| writeString(fullName + "._NAMESPACES = {\n"); |
| writeString(names + "\n"); |
| writeString("}\n"); |
| } |
| |
| // add _USES member to class |
| names = ""; |
| comma = false; |
| for (String uses : m_useNames) |
| { |
| final INamespaceDefinition ns = JSSharedData.instance.getNamespaceForQName(uses); |
| if (ns != null) |
| { |
| if (comma) |
| names += ",\n"; |
| else |
| comma = true; |
| names += (" \"" + ns.getURI().replaceAll(":", ".") + "\" : \"" + uses + "\""); |
| } |
| } |
| |
| if (!names.isEmpty()) |
| { |
| writeString("\n"); |
| writeString("/**\n"); |
| writeString(" * Member: " + fullName + "._USES\n"); |
| writeString(" * @const\n"); |
| writeString(" * @type {Object}\n"); |
| writeString(" */\n"); |
| |
| writeString(fullName + "._USES = {\n"); |
| writeString(names + "\n"); |
| writeString("}\n"); |
| } |
| } |
| } |
| |
| private Boolean needsClassInfo(Traits classTraits) |
| { |
| for (Trait t : classTraits) |
| { |
| final Vector<Metadata> metaData = t.getMetadata(); |
| for (Metadata md : metaData) |
| { |
| if (md.getName().equals(JSSharedData.GENERATE_CLASSINFO)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * tests.Test._CLASSINFO = { name : "", isDynamic : true, isFinal : true, |
| * isStatic : true, bases : [], traits : { bases : {}, interfaces : {}, |
| * constructor : { {type: "", optional:false}, ... }, variables : { name: |
| * "", type: "", access: "readwrite", uri: "", metadata: { name : "", value |
| * : { {key:"", value:""}, ... } } } accessors : { name: "", type: "", |
| * access: "readwrite", declaredBy: "", uri: "", metadata: { name : "", |
| * value : { {key:"", value:""}, ... } } }, methods : { name: "", |
| * returnType: "", declaredBy: "", parameters: { }, uri: "", metadata: { |
| * name : "", value : { {key:"", value:""}, ... } } }, metadata: { name : |
| * "", value : { {key:"", value:""}, ... } } } } |
| */ |
| private void emitJSONInfo(EmitterClassVisitor clz, |
| String packageName, String className, String superClass) throws Exception |
| { |
| final Traits classTraits = clz.classTraits; |
| // check whether this class is tagged with [ClassInfo] |
| if (!className.isEmpty() && needsClassInfo(classTraits)) |
| { |
| final Traits instanceTraits = clz.instanceTraits; |
| final String fullName = createFullName(packageName, className); |
| |
| // add _CLASSINFO member to class |
| writeString("\n"); |
| writeString("/**\n"); |
| writeString(" * Member: " + fullName + "._CLASSINFO\n"); |
| writeString(" * @const\n"); |
| writeString(" * @type {Object}\n"); |
| writeString(" */\n"); |
| writeString(fullName + "._CLASSINFO =\n"); |
| writeString("{\n"); |
| |
| final String indent = " "; |
| final String indent2 = " "; |
| final String indent3 = " "; |
| final String indent4 = " "; |
| final String indent5 = " "; |
| |
| // add name, bases, isDynamic, isFinal, isStatic |
| final InstanceInfo iinfo = clz.instanceInfo; |
| final Boolean isDynamic = !iinfo.isSealed(); |
| final Boolean isFinal = iinfo.isFinal(); |
| final Boolean isStatic = true; |
| if (packageName.isEmpty()) |
| writeString(indent + "name : \"" + className + "\",\n"); |
| else |
| writeString(indent + "name : \"{" + packageName + "}::" + className + "\",\n"); |
| writeString(indent + "bases : [\"Class\"],\n"); |
| writeString(indent + "isDynamic : " + (isDynamic ? "true" : "false") + ",\n"); |
| writeString(indent + "isFinal : " + (isFinal ? "true" : "false") + ",\n"); |
| writeString(indent + "isStatic : " + (isStatic ? "true" : "false") + ",\n"); |
| |
| // traits |
| writeString(indent + "traits:\n"); |
| writeString(indent + "{\n"); |
| |
| // add traits.bases |
| writeString(indent2 + "bases:\n"); |
| writeString(indent2 + "{\n"); |
| |
| ClassDefinition classDef = getClassDefinition(className, packageName); |
| |
| IClassDefinition superclassDef = classDef.resolveBaseClass(m_project); |
| while (superclassDef != null) |
| { |
| final String superPackageName = superclassDef.getPackageName(); |
| if (superPackageName.isEmpty()) |
| writeString(indent3 + superclassDef.getBaseName() + ": \"" + superclassDef.getBaseName() + "\",\n"); |
| else |
| writeString(indent3 + superclassDef.getBaseName() + ": \"{" + superPackageName + "}::" + superclassDef.getBaseName() + "\",\n"); |
| |
| superclassDef = superclassDef.resolveBaseClass(m_project); |
| } |
| writeString(indent2 + "},\n"); |
| |
| // add traits.interfaces |
| writeString(indent2 + "interfaces:\n"); |
| writeString(indent2 + "{\n"); |
| for (Name iname : iinfo.interfaceNames) |
| { |
| writeString(indent3 + "\"" + nameToString(iname) + "\",\n"); |
| } |
| writeString(indent2 + "},\n"); |
| |
| // add traits.constructor |
| writeString(indent2 + "constructor:\n"); |
| writeString(indent2 + "{\n"); |
| final MethodInfo ctor = clz.instanceInfo.iInit; |
| writeString(getParameterInfo(ctor, indent3)); |
| writeString(indent2 + "},\n"); |
| |
| // add variables, accessors, methods |
| List<String> variables = new ArrayList<String>(); |
| List<String> methods = new ArrayList<String>(); |
| Map<String, String> getters = new HashMap<String, String>(); |
| Map<String, String> setters = new HashMap<String, String>(); |
| |
| for (Trait t : instanceTraits) |
| { |
| String str = new String(); |
| final Name name = t.getNameAttr("name"); |
| String propName = JSGeneratingReducer.getBasenameFromName(name); |
| String propType = ""; |
| switch (t.getKind()) |
| { |
| // traits.variables |
| case TRAIT_Var: |
| case TRAIT_Const: |
| { |
| final Boolean isConst = t.getKind() == TRAIT_Const; |
| final Name type = t.getNameAttr(Trait.TRAIT_TYPE); |
| if (type == null) |
| propType = "Object"; |
| else |
| propType = nameToString(type);// JSGeneratingReducer.getBasenameFromName(type); |
| str += indent3 + propName + ":\n"; |
| str += indent3 + "{\n"; |
| str += indent4 + "name : " + "\"" + propName + "\",\n"; |
| str += indent4 + "type : " + "\"" + propType + "\",\n"; |
| str += indent4 + "access : " + "\"" + (isConst ? "readonly" : "readwrite") + "\",\n"; |
| // str += indent4 + "uri : " + "\"" + ??? + "\"\n"; |
| // str += getMetaDataInfo( findMetadata(classTraits, propName, t.getKind()), indent4 ); |
| str += getMetaDataInfo(t.getMetadata(), indent4); |
| str += indent3 + "}"; |
| variables.add(str); |
| break; |
| } |
| // traits.accessors |
| case TRAIT_Getter: |
| case TRAIT_Setter: |
| { |
| final String declaredBy = "(unknown)"; // TODO |
| final String access = t.getKind() == TRAIT_Getter ? "readonly" : "readwrite"; |
| str += indent3 + propName + ":\n"; |
| str += indent3 + "{\n"; |
| str += indent4 + "name : " + "\"" + propName + "\",\n"; |
| str += indent4 + "type : " + "\"" + propType + "\",\n"; |
| str += indent4 + "access : " + "\"" + access + "\",\n"; |
| str += indent4 + "declaredBy : " + "\"" + declaredBy + "\",\n"; |
| // str += indent4 + "uri : " + "\"" + ??? + "\"\n"; |
| // str += getMetaDataInfo( findMetadata(classTraits, propName, t.getKind()), indent4 ); |
| str += getMetaDataInfo(t.getMetadata(), indent4); |
| str += indent3 + "}"; |
| |
| if (t.getKind() == TRAIT_Getter) |
| getters.put(propName, str); |
| else |
| setters.put(propName, str); |
| break; |
| } |
| // traits.methods |
| case TRAIT_Method: |
| case TRAIT_Function: |
| { |
| final String declaredBy = "(unknown)"; // TODO |
| final MethodInfo mi = (MethodInfo)t.getAttr("method_id"); |
| str += indent3 + propName + ":\n"; |
| str += indent3 + "{\n"; |
| str += indent4 + "name : " + "\"" + propName + "\",\n"; |
| str += indent4 + "returnType : " + "\"" + nameToString(mi.getReturnType()) + "\",\n"; |
| str += indent4 + "declaredBy : " + "\"" + declaredBy + "\",\n"; |
| // str += indent4 + "uri : " + "\"" + ??? + "\"\n"; |
| str += indent4 + "parameters:\n"; |
| str += indent4 + "{\n"; |
| str += getParameterInfo(mi, indent5); |
| str += indent4 + "},\n"; |
| // str += getMetaDataInfo( findMetadata(classTraits, propName, t.getKind()), indent4 ); |
| str += getMetaDataInfo(t.getMetadata(), indent4); |
| str += indent3 + "}"; |
| methods.add(str); |
| |
| break; |
| } |
| } |
| } |
| |
| // add variables |
| writeString(indent2 + "variables:\n"); |
| writeString(indent2 + "{\n"); |
| for (String var : variables) |
| { |
| writeString(var + ",\n"); |
| } |
| writeString(indent2 + "},\n"); |
| |
| // add accessors |
| writeString(indent2 + "accessors:\n"); |
| writeString(indent2 + "{\n"); |
| for (Map.Entry<String, String> entry : getters.entrySet()) |
| { |
| final String s = setters.get(entry.getKey()); |
| if (s != null) |
| writeString(s + ",\n"); |
| else |
| writeString(entry.getValue() + ",\n"); |
| } |
| writeString(indent2 + "},\n"); |
| |
| // add methods |
| writeString(indent2 + "methods:\n"); |
| writeString(indent2 + "{\n"); |
| for (String met : methods) |
| { |
| writeString(met + ",\n"); |
| } |
| writeString("\n"); |
| writeString(indent2 + "},\n"); |
| |
| // closing traits |
| writeString(indent + "},\n"); |
| |
| // add class metadata |
| for (Trait t : classTraits) |
| { |
| if (t.getKind() == TRAIT_Class) |
| writeString(getMetaDataInfo(t.getMetadata(), indent)); |
| } |
| |
| writeString("}\n"); |
| } |
| } |
| |
| /* |
| * private Vector<Metadata> findMetadata( Traits classTraits, String name, |
| * int traitKind ) { for( Trait t : classTraits ) { if( t.getKind() == |
| * traitKind ) { final Name n = t.getNameAttr("name"); if( |
| * JSGeneratingReducer.getBasenameFromName(n).equals(name) ) return |
| * t.getMetadata(); } } return new Vector<Metadata>(); } |
| */ |
| |
| private String getMetaDataInfo(Vector<Metadata> metaData, String indent) |
| { |
| String s = ""; |
| if (!metaData.isEmpty()) |
| { |
| s += indent + "metadata:\n"; |
| s += indent + "{\n"; |
| for (Metadata md : metaData) |
| { |
| s += indent + " " + md.getName() + ":\n"; |
| s += indent + " {\n"; |
| s += indent + " " + "name : \"" + md.getName() + "\",\n"; |
| final String[] keys = md.getKeys(); |
| if (keys.length > 0) |
| { |
| final String[] values = md.getValues(); |
| |
| s += indent + " " + "value:\n"; |
| s += indent + " " + "{\n"; |
| for (int i = 0; i < keys.length; ++i) |
| { |
| s += indent + " " + keys[i] + ": {key: \"" + keys[i] + "\", value: \"" + values[i] + "\"},\n"; |
| } |
| s += indent + " " + "}\n"; |
| } |
| s += indent + " },\n"; |
| } |
| s += indent + "}\n"; |
| } |
| return s; |
| } |
| |
| private String getParameterInfo(MethodInfo mi, String indent) |
| { |
| String s = ""; |
| if (mi != null) |
| { |
| Vector<PooledValue> defaultValues = mi.getDefaultValues(); |
| Vector<Name> paramTypes = mi.getParamTypes(); |
| final int defaultsStartAt = paramTypes.size() - defaultValues.size(); |
| int current = 0; |
| for (Name type : paramTypes) |
| { |
| final String optional = current >= defaultsStartAt ? "true" : "false"; |
| s += indent + "p" + current + ": {type: \"" + nameToString(type) + "\", optional: " + optional + "},\n"; |
| current++; |
| } |
| } |
| return s; |
| } |
| |
| /* |
| * private void emitPackageNames() { writeString( "// Packages \n" ); // get |
| * all package names from the shared data object Set<String> packages = |
| * JSSharedData.instance.getPackages(); // "expand" the package names, i.e. |
| * "com.adobe" expands to "com", "com.adobe". Set<String> expanded = new |
| * TreeSet<String>(); for (String packageName : packages) { String path = |
| * ""; String[] parts = packageName.split("\\."); for( String part: parts ) |
| * { if( path.length() == 0 ) path += part; else path += "." + part; if( |
| * path.length() > 0 ) expanded.add( path ); } } // create an entry for each |
| * expanded package name Iterator<String> it = expanded.iterator(); if( |
| * it.hasNext() ) { while (it.hasNext()) { writeString( |
| * JSSharedData.ROOT_NAME + it.next().toString() + " = {};\n" ); } } |
| * writeString( "\n" ); } |
| */ |
| |
| private String createFullName(String packageName, String className) |
| { |
| String fullName = ""; |
| |
| // root name |
| if (!JSSharedData.ROOT_NAME.isEmpty() && !className.startsWith(JSSharedData.ROOT_NAME)) |
| fullName += JSSharedData.ROOT_NAME; |
| |
| // package name |
| if (!packageName.isEmpty()) |
| { |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += packageName; |
| } |
| |
| // class name |
| if (!className.isEmpty()) |
| { |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += className; |
| } |
| |
| return fullName; |
| } |
| |
| /* |
| * private int typeNameToAbcConstant( Name typeName ) { if( typeName != null |
| * ) { final String type = |
| * JSGeneratingReducer.getBasenameFromName(typeName); if( type.equals("int") |
| * ) return ABCConstants.CONSTANT_Int; if( type.equals("uint") ) return |
| * ABCConstants.CONSTANT_UInt; if( type.equals("Number") ) return |
| * ABCConstants.CONSTANT_Double; if( type.equals("String") ) return |
| * ABCConstants.CONSTANT_Utf8; if( type.equals("Boolean") ) return |
| * ABCConstants.CONSTANT_False; if( type.equals("null") ) return |
| * ABCConstants.CONSTANT_Null; } return ABCConstants.CONSTANT_Undefined; } |
| */ |
| private Boolean isDataClassDefinition(IDefinition def) |
| { |
| if (def != null && def instanceof IClassDefinition) |
| { |
| IClassDefinition classDef = (IClassDefinition)def; |
| Iterator<IClassDefinition> superClassIterator = classDef.classIterator(m_project, true); |
| |
| while (superClassIterator.hasNext()) |
| { |
| final IClassDefinition superClass = superClassIterator.next(); |
| if (superClass.getMetaTagByName("DataClass") != null) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected void emitCtor(MethodInfo ctor, Boolean isExtern, Boolean isInterface, String packageName, String className, String superClassName, |
| String methodPrefix, String assignmentOp, String separator, String indent) throws Exception |
| { |
| final Boolean isFramework = className.equals(JSSharedData.JS_FRAMEWORK_NAME); |
| |
| // first check whether there is any code... |
| final String body = getCodeForConstructor(ctor); |
| final String fullName = createFullName(packageName, className); |
| |
| if (!body.isEmpty() || JSSharedData.instance.hasClassInit(fullName)) |
| { |
| // writeString("\n" + indent + "// Constructor\n"); |
| |
| m_methodPrologue = ""; |
| |
| final Boolean isPackageFunction = false; |
| if (JSSharedData.m_useClosureLib) |
| { |
| final String pkg = packageName.isEmpty() ? "" : (createFullName(packageName, "") + "."); |
| emitMethod(null, ctor, isFramework, isExtern, isInterface, isPackageFunction, fullName, className, "", pkg, assignmentOp, separator, indent); |
| } |
| else |
| { |
| MethodBodyInfo mbi = findMethodBodyInfo(ctor); |
| emitMethodBody(mbi); |
| } |
| } |
| else |
| { |
| if (JSSharedData.m_useClosureLib) |
| writeString(fullName + " = function(){}\n"); |
| } |
| |
| // inherits |
| if (JSSharedData.m_useClosureLib && (!JSGeneratingReducer.isDataType(superClassName) || superClassName.equals("Error"))) |
| { |
| writeString("goog.inherits(" |
| + fullName + ", " |
| + superClassName + ");\n"); |
| } |
| |
| // don't add _CLASS to DataClass instances. |
| final IDefinition def = JSSharedData.instance.getDefinition(fullName); |
| if (JSSharedData.m_useClosureLib && (def == null || !isDataClassDefinition(def))) |
| { |
| // add _CLASS member to instance |
| writeString("\n"); |
| writeString("/**\n"); |
| writeString(" * Member: " + fullName + ".prototype._CLASS\n"); |
| writeString(" * @const\n"); |
| writeString(" * @type {" + fullName + "}\n"); |
| writeString(" */\n"); |
| writeString(fullName + ".prototype._CLASS = " + fullName + ";\n"); |
| } |
| } |
| |
| protected String emitSlotValue(Trait t) |
| { |
| Object trait_value = t.getAttr(Trait.SLOT_VALUE); |
| if (trait_value != null) |
| { |
| // See also poolTraitValues(Traits), which |
| // puts these values into the pools. |
| if (trait_value instanceof String) |
| { |
| return ((String)trait_value); |
| } |
| else if (trait_value instanceof Namespace) |
| { |
| return (((Namespace)trait_value).getName()); |
| } |
| else if (trait_value instanceof Double) |
| { |
| return (((Double)trait_value).toString()); |
| } |
| else if (trait_value instanceof Integer) |
| { |
| return (((Integer)trait_value).toString()); |
| } |
| else if (trait_value instanceof Long) |
| { |
| return (((Long)trait_value).toString()); |
| } |
| else if (trait_value instanceof Float) |
| { |
| return (((Float)trait_value).toString()); |
| } |
| else if (trait_value.equals(Boolean.TRUE)) |
| { |
| return ("true"); |
| } |
| else if (trait_value.equals(Boolean.FALSE)) |
| { |
| return ("false"); |
| } |
| else if (trait_value == ABCConstants.NULL_VALUE) |
| { |
| return ("null"); |
| } |
| else if (trait_value == ABCConstants.UNDEFINED_VALUE) |
| { |
| // Undefined is a special case; it has no kind byte. |
| return ("undefined"); |
| } |
| else |
| { |
| throw new IllegalStateException("Unrecognized initializer type: " + trait_value.getClass().toString()); |
| } |
| } |
| else |
| { |
| final Name type = t.getNameAttr(Trait.TRAIT_TYPE); |
| |
| // In AS, uninitialized variables have a type-specific default value (not always undefined). |
| // The local-var version of this fix is in JSGeneratingReducer.reduce_typedVariableDecl() |
| return (getDefaultInitializerForVariable(type)); |
| |
| /* |
| * // Flash assumes that uninitialized numbers default to "0" and |
| * not "undefined". String defaultVal = "undefined"; switch( |
| * typeNameToAbcConstant(type) ) { case ABCConstants.CONSTANT_Int : |
| * defaultVal = "0"; break; case ABCConstants.CONSTANT_UInt : |
| * defaultVal = "0"; break; case ABCConstants.CONSTANT_Double : |
| * defaultVal = "0.0"; break; case ABCConstants.CONSTANT_Utf8 : |
| * defaultVal = "null"; break; case ABCConstants.CONSTANT_False : |
| * defaultVal = "false"; break; case ABCConstants.CONSTANT_True : |
| * defaultVal = "true"; break; case ABCConstants.CONSTANT_Undefined |
| * : defaultVal = "undefined"; break; case |
| * ABCConstants.CONSTANT_Null : defaultVal = "null"; break; default |
| * : defaultVal = "undefined"; break; } return( defaultVal ); |
| */ |
| } |
| } |
| |
| protected Boolean emitVariable(Trait t, String baseName, |
| String packageName, String className, String superClassName, |
| String methodPrefix, String assignment, String separator, String indent) throws Exception |
| { |
| Boolean needsSkinPartProcessing = false; |
| final Name name = t.getNameAttr("name"); |
| final Name type = t.getNameAttr(Trait.TRAIT_TYPE); |
| |
| // JSDoc |
| List<String> jsdoc = new ArrayList<String>(); |
| // jsdoc.add("Member: " + createFullName(packageName, className + "." + baseName)); |
| |
| if (name != null) |
| { |
| Namespace ns = name.getSingleQualifier(); |
| if (ns.getKind() == CONSTANT_PrivateNs) |
| jsdoc.add("@private"); |
| else if (ns.getKind() == CONSTANT_ProtectedNs) |
| jsdoc.add("@protected"); |
| else |
| jsdoc.add("@expose"); |
| } |
| if (t.getKind() == TRAIT_Const) |
| jsdoc.add("@const"); |
| if (type != null) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| JSGeneratingReducer.nameToJSDocType(m_project, type, sb); |
| jsdoc.add("@type {" + sb.toString() + "}"); |
| } |
| |
| if (jsdoc.size() > 0) |
| { |
| writeString("\n"); |
| if (jsdoc.size() == 1) |
| writeString(indent + "/** " + jsdoc.get(0) + " */\n"); |
| else |
| { |
| writeString(indent + "/**\n"); |
| for (String decl : jsdoc) |
| { |
| writeString(indent + " * " + decl + "\n"); |
| } |
| writeString(indent + " */\n"); |
| } |
| } |
| |
| writeString(indent + methodPrefix + baseName + assignment + ";\n\n"); |
| |
| // Examine var/const metadata |
| final Vector<Metadata> metaData = t.getMetadata(); |
| for (Metadata md : metaData) |
| { |
| if (md.getName().equals(IMetaAttributeConstants.ATTRIBUTE_SKIN_PART)) |
| { |
| needsSkinPartProcessing = true; |
| break; |
| } |
| } |
| |
| return needsSkinPartProcessing; |
| } |
| |
| protected void emitTraits(Traits traits, |
| Boolean isInstanceTraits, Boolean isExtern, Boolean isInterface, Boolean isPackageFunction, |
| MethodInfo ctor, String packageName, String className, String superClassName, |
| String methodPrefix, String assignmentOp, String separator, String indent) throws Exception |
| { |
| final Boolean isFramework = className.equals(JSSharedData.JS_FRAMEWORK_NAME); |
| Boolean emitComma = false; |
| |
| // cache some data used by all the warnIfPrivateNameCollision() calls below |
| IClassDefinition classDef = getClassDefinition(className, packageName); |
| IClassDefinition baseClass = null; |
| ASDefinitionFilter filter = null; |
| if (classDef != null) |
| { |
| baseClass = classDef.resolveBaseClass(m_project); |
| filter = new ASDefinitionFilter(ClassificationValue.ALL, |
| ASDefinitionFilter.SearchScopeValue.INHERITED_MEMBERS, |
| ASDefinitionFilter.AccessValue.ALL, |
| baseClass); |
| } |
| |
| // 1. emit constructor |
| if (isInstanceTraits && ctor != null) |
| { |
| emitCtor(ctor, isExtern, isInterface, packageName, className, superClassName, |
| methodPrefix, assignmentOp, separator, indent); |
| } |
| |
| // Avoid emitting duplicate Traits, which starts happening after adding |
| // this line to JSClassDirectiveProcessor::declareVariable(): |
| // this.classScope.traitsVisitor = (is_static)? ctraits: itraits; |
| final Set<String> visitedTraits = new HashSet<String>(); |
| |
| // 2. emit all variables before any methods |
| boolean needsSkinPartProcessing = false; |
| |
| for (Trait t : traits) |
| { |
| // Get the kind byte with its flags set in the high nibble. |
| switch (t.getKind()) |
| { |
| case TRAIT_Var: |
| case TRAIT_Const: |
| |
| final Name name = t.getNameAttr("name"); |
| Namespace ns = name.getSingleQualifier(); |
| if (ns.getKind() != CONSTANT_PrivateNs && isInstanceTraits) |
| break; |
| final String baseName = JSGeneratingReducer.getBasenameFromName(name); |
| |
| if (!visitedTraits.contains(baseName)) |
| { |
| visitedTraits.add(baseName); |
| |
| // see JSGlobalDirectiveProcessor::declareFunction. |
| // Functions at the global scope create a var of type '*' |
| Boolean emitVar = true; |
| if (isPackageFunction) |
| { |
| final Name type = t.getNameAttr("type"); |
| if (type == null || type.equals("Function")) |
| { |
| for (MethodInfo mi : methodInfos) |
| { |
| if (mi.getMethodName() != null && mi.getMethodName().equals(baseName)) |
| { |
| emitMethod(t, mi, isFramework, isExtern, isInterface, isPackageFunction, baseName, baseName, "", "var ", assignmentOp, separator, indent); |
| emitVar = false; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (emitVar) |
| { |
| writeString("\n"); |
| String slotValue = (String)t.getAttr(Trait.SLOT_VALUE); |
| if (slotValue == null) |
| slotValue = ""; |
| else |
| { |
| slotValue = " = " + slotValue; |
| } |
| boolean methodNeedsSkinPartProcessing = emitVariable(t, baseName, |
| packageName, className, superClassName, |
| methodPrefix, slotValue, separator, indent); |
| |
| needsSkinPartProcessing = needsSkinPartProcessing || methodNeedsSkinPartProcessing; |
| // print warning in cases where FJS-24 is being hit |
| String fullName = createFullName(packageName, className); |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += baseName; |
| |
| warnIfPrivateNameCollision(baseClass, filter, baseName, fullName, "Field"); |
| } |
| } |
| break; |
| case TRAIT_Method: |
| case TRAIT_Function: |
| case TRAIT_Getter: |
| case TRAIT_Setter: |
| { |
| // methods will be processed below. |
| } |
| break; |
| case TRAIT_Class: |
| // TODO: non-zero slot id |
| // writeString( "\n // ClassInfo: " + ((ClassInfo)t.getAttr(Trait.TRAIT_CLASS)).toString() + "\n" ); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown trait kind " + t.getKind()); |
| } |
| } |
| if (isInstanceTraits && ctor != null) |
| { |
| writeString("};\n"); // end of constructor |
| final String cName = createFullName(packageName, className); |
| writeString("goog.inherits(" + cName + ", " + superClassName + ");\n"); |
| } |
| |
| // 3. emit public vars |
| for (Trait t : traits) |
| { |
| // Get the kind byte with its flags set in the high nibble. |
| switch (t.getKind()) |
| { |
| case TRAIT_Var: |
| case TRAIT_Const: |
| |
| final Name name = t.getNameAttr("name"); |
| Namespace ns = name.getSingleQualifier(); |
| if (ns.getKind() == CONSTANT_PrivateNs) |
| break; |
| final String baseName = JSGeneratingReducer.getBasenameFromName(name); |
| |
| if (!visitedTraits.contains(baseName)) |
| { |
| visitedTraits.add(baseName); |
| |
| // see JSGlobalDirectiveProcessor::declareFunction. |
| // Functions at the global scope create a var of type '*' |
| Boolean emitVar = true; |
| if (isPackageFunction) |
| { |
| final Name type = t.getNameAttr("type"); |
| if (type == null || type.equals("Function")) |
| { |
| for (MethodInfo mi : methodInfos) |
| { |
| if (mi.getMethodName() != null && mi.getMethodName().equals(baseName)) |
| { |
| emitMethod(t, mi, isFramework, isExtern, isInterface, isPackageFunction, baseName, baseName, "", "var ", assignmentOp, separator, indent); |
| emitVar = false; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (emitVar) |
| { |
| String slotValue = (String)t.getAttr(Trait.SLOT_VALUE); |
| if (slotValue == null) |
| slotValue = ""; |
| else |
| { |
| slotValue = " = " + slotValue; |
| } |
| writeString("\n"); |
| |
| boolean methodNeedsSkinPartProcessing = emitVariable(t, baseName, |
| packageName, className, superClassName, |
| (packageName == "") ? className + ".prototype." : |
| packageName + "." + className + ".prototype.", slotValue, "", ""); |
| |
| needsSkinPartProcessing = needsSkinPartProcessing || methodNeedsSkinPartProcessing; |
| // print warning in cases where FJS-24 is being hit |
| String fullName = createFullName(packageName, className); |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += baseName; |
| |
| warnIfPrivateNameCollision(baseClass, filter, baseName, fullName, "Field"); |
| } |
| } |
| break; |
| case TRAIT_Method: |
| case TRAIT_Function: |
| case TRAIT_Getter: |
| case TRAIT_Setter: |
| { |
| // methods will be processed below. |
| } |
| break; |
| case TRAIT_Class: |
| // TODO: non-zero slot id |
| // writeString( "\n // ClassInfo: " + ((ClassInfo)t.getAttr(Trait.TRAIT_CLASS)).toString() + "\n" ); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown trait kind " + t.getKind()); |
| } |
| } |
| |
| String proto = isInstanceTraits ? ".prototype." : "."; |
| // 4. emit all other methods (ctor already emitted) |
| for (Trait t : traits) |
| { |
| // Get the kind byte with its flags set in the high nibble. |
| switch (t.getKind()) |
| { |
| case TRAIT_Method: |
| case TRAIT_Function: |
| case TRAIT_Getter: |
| case TRAIT_Setter: |
| { |
| // we can't emit getter and setter for extern declarations. |
| if (!isExtern || t.getKind() != TRAIT_Setter) |
| { |
| |
| // write out full function name with package. |
| Name name = t.getNameAttr("name"); |
| MethodInfo mi = (MethodInfo)t.getAttr("method_id"); |
| final String baseName = JSGeneratingReducer.getBasenameFromName(name); |
| |
| // write out comment with the full class name |
| String fullName = createFullName(packageName, className); |
| String xetter = ""; |
| |
| // cleaning up static inits. |
| // Static initializer is generated but not called for classes without explicit constructor |
| //if ((!isInstanceTraits || ctor == null) && JSSharedData.instance.hasClassInit(fullName)) |
| // m_methodPrologue += " " + fullName + "." + JSSharedData.STATIC_INIT + "();\n"; |
| |
| if (!isExtern) |
| { |
| if (t.getKind() == TRAIT_Getter) |
| xetter = JSSharedData.GETTER_PREFIX; |
| else if (t.getKind() == TRAIT_Setter) |
| xetter = JSSharedData.SETTER_PREFIX; |
| } |
| |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += xetter + baseName; |
| |
| // don't emit extern package functions |
| if (!isExtern && isPackageFunction) |
| isExtern = isExtern(name); |
| |
| // actually emit the JS code |
| emitMethod(t, mi, isFramework, isExtern, isInterface, isPackageFunction, fullName, baseName, xetter, |
| (packageName == "") ? className + proto : |
| packageName + "." + className + proto , "=", "", ""); |
| |
| // print warning in cases where FJS-24 is being hit |
| warnIfPrivateNameCollision(baseClass, filter, baseName, fullName, "Method"); |
| } |
| } |
| break; |
| } |
| } |
| |
| // 5. custom methods generated from metadata |
| if (needsSkinPartProcessing) |
| { |
| if (emitComma) |
| writeString(separator + "\n"); |
| else |
| emitComma = separator.equals(","); |
| |
| emitGeneratedSkinPartsMethod(className, packageName, methodPrefix, assignmentOp); |
| } |
| |
| // 5. class constructor |
| if (ctor != null && !isInstanceTraits) |
| { |
| // first check whether there is any code... |
| final String body = getCodeForConstructor(ctor); |
| if (!body.isEmpty()) |
| { |
| if (emitComma) |
| writeString(separator + "\n"); |
| else |
| emitComma = separator.equals(","); |
| |
| final String fullName = createFullName(packageName, className); |
| |
| // writeString( "\n // Static inits:\n" ); |
| emitMethod(null, ctor, isFramework, isExtern, isInterface, isPackageFunction, fullName, null, "", methodPrefix, assignmentOp, separator, indent); |
| } |
| } |
| } |
| |
| /** |
| * Emits the skinParts getter definition that is implied by the use of |
| * [SkinPart] metadata on a Flex class. TODO: once Falcon starts handling |
| * [SkinPart] this code should probbly go away |
| */ |
| private void emitGeneratedSkinPartsMethod(String className, String packageName, String methodPrefix, String assignmentOp) |
| { |
| IClassDefinition def = getClassDefinition(className, packageName); |
| String jsonResult = JSFlexUtils.generateGetSkinPartsJSON(def, m_project); |
| |
| // Emit JSDoc -- based on emitJSDocForMethod() |
| String methodName = "get_skinParts"; |
| String methodFullName = packageName + "." + className + "." + methodName; |
| String indent = ""; |
| writeString(indent + "\n\n"); |
| writeString(indent + "/**\n"); |
| writeString(indent + " * Method: " + methodFullName + "()\n"); |
| writeString(indent + " * @this {" + methodFullName.substring(0, methodFullName.lastIndexOf(".")) + "}\n"); |
| writeString(indent + " * @protected\n"); |
| writeString(indent + " * @override\n"); |
| writeString(indent + " * @return {Object}\n"); |
| writeString(indent + " */\n"); |
| |
| // Emit method definition itself -- based on emitMethod() |
| writeString(indent + methodPrefix + methodName); |
| writeString(" " + assignmentOp + " "); |
| |
| writeString("function() /* : Object */\n"); |
| writeString("{\n"); |
| |
| indent = " "; |
| writeString(indent + "return " + jsonResult + ";\n"); |
| |
| writeString("}"); |
| } |
| |
| /** |
| * Given a var or const trait, return the default value it should be |
| * initialized to if there was no initializer in the AS code |
| */ |
| public static String getDefaultInitializerForVariable(Name varType) |
| { |
| if (varType != null) |
| { |
| String typeName = JSGeneratingReducer.getBasenameFromName(varType); |
| |
| // int/uint default to 0 in AS; this matters since, e.g.: 0++ = 1; undefined++ = NaN |
| if (typeName.equals("int") || typeName.equals("uint")) |
| return "0"; |
| |
| // Number defaults to naN |
| if (typeName.equals("Number")) |
| return "NaN"; |
| |
| // Boolean defaults to false in AS; this matters when comparing two bools: undefined != false |
| if (typeName.equals("Boolean")) |
| return "false"; |
| } |
| return "undefined"; |
| } |
| |
| /** |
| * Given a class member and the class's base class, try to detect whether it |
| * will hit FJS-24 - and if so print a warning. Intended to be called from |
| * emitTraits(). |
| */ |
| public void warnIfPrivateNameCollision(IClassDefinition baseClass, ASDefinitionFilter baseClassAllFilter, String memberName, String memberFullName, String memberDescription) |
| { |
| // If baseClass is null, we're probably looking at a global function like trace() or setTimeout() |
| if (baseClass != null) |
| { |
| IDefinition conflict = MemberedDefinitionUtils.getMemberByName(baseClass, m_project, memberName, baseClassAllFilter); |
| if (conflict != null) |
| { |
| // If member is non-private, it's *expected* to be overridden (note: we could also check isOverride() on classDef's member) |
| // If member is static, FJS-24 doesn't apply and all is well |
| if (conflict.isPrivate() && !conflict.isStatic()) |
| { |
| m_sharedData.stderr("Warning: " + memberDescription + " " + memberFullName + " will unexpectedly override a private member of superclass " + conflict.getParent().getBaseName()); |
| } |
| } |
| } |
| } |
| |
| /* |
| * private String getCodeForMethodInfo( MethodInfo mi ) { String body = ""; |
| * MethodBodyInfo mbi = findMethodBodyInfo( mi ); if( mbi != null ) { for |
| * (Block b : mbi.getBlocks()) { for (int i = 0; i < b.instructions.size() |
| * && !b.instructions.get(i).isBranch(); i++) { final Instruction insn = |
| * b.instructions.get(i); if( insn.getOpcode() == JSSharedData.OP_JS ) { |
| * final String str = (String)insn.getOperand(0); body += |
| * JSGeneratingReducer.indentBlock(str,1); } } } } return body; } |
| */ |
| |
| protected String getCodeForConstructor(MethodInfo mi) |
| { |
| String body = ""; |
| MethodBodyInfo mbi = findMethodBodyInfo(mi); |
| |
| if (mbi != null) |
| { |
| for (IBasicBlock b : mbi.getCfg().getBlocksInEntryOrder()) |
| { |
| for (int i = 0; i < b.size() && !b.get(i).isBranch(); i++) |
| { |
| final Instruction insn = b.get(i); |
| if (insn.getOpcode() == JSSharedData.OP_JS) |
| { |
| final String str = (String)insn.getOperand(0); |
| |
| // we are only interested in the instruction that initialize variables with a value. |
| // if( str.contains("=") ) |
| body += JSGeneratingReducer.indentBlock(str, 1); |
| } |
| } |
| } |
| } |
| return body; |
| } |
| |
| private void emitJSDocForMethod(Trait t, MethodInfo mi, MethodBodyInfo mbi, String fullName, String indent) |
| { |
| final Boolean isCtor = t == null; |
| final Boolean isInterface = mbi == null; |
| |
| writeString(indent + "\n\n"); |
| writeString(indent + "/**\n"); |
| /* |
| if (isCtor) |
| { |
| writeString(indent + " * Constructor: " + fullName + "()\n"); |
| writeString(indent + " * @constructor\n"); |
| } |
| else if (isInterface) |
| { |
| writeString(indent + " * Interface: " + fullName + "()\n"); |
| } |
| else |
| { |
| writeString(indent + " * Method: " + fullName + "()\n"); |
| } |
| */ |
| if (!isInterface) |
| { |
| if (fullName.contains(".")) |
| { |
| if (fullName.substring(0, fullName.lastIndexOf(".")).equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| writeString(indent + " * @this {" + JSSharedData.JS_FRAMEWORK_NAME + "}\n"); |
| else |
| writeString(indent + " * @this {" + fullName.substring(0, fullName.lastIndexOf(".")) + "}\n"); |
| } |
| |
| if (t != null) |
| { |
| final Name name = t.getNameAttr("name"); |
| if (name != null) |
| { |
| Namespace ns = name.getSingleQualifier(); |
| if (ns.getKind() == CONSTANT_PrivateNs) |
| writeString(indent + " * @private\n"); |
| else if (ns.getKind() == CONSTANT_ProtectedNs) |
| writeString(indent + " * @protected\n"); |
| else |
| writeString(indent + " * @expose\n"); |
| } |
| if (t.isOverride() || |
| (t.hasAttr("override") && (Boolean)(t.getAttr("override")) == true)) |
| { |
| writeString(indent + " * @override\n"); |
| } |
| } |
| } |
| |
| emitJSDocForParams(mi, indent); |
| |
| if (mi.getReturnType() != null && !JSGeneratingReducer.getBasenameFromName(mi.getReturnType()).equals("void")) |
| { |
| final StringBuilder sb = new StringBuilder(); |
| JSGeneratingReducer.nameToJSDocType(m_project, mi.getReturnType(), sb); |
| |
| writeString(indent + " * @return {" + sb.toString() + "}\n"); |
| } |
| |
| writeString(indent + " */\n"); |
| } |
| |
| private void emitJSDocForParams(MethodInfo mi, String indent) |
| { |
| Vector<PooledValue> defaultValues = mi.getDefaultValues(); |
| Vector<Name> paramTypes = mi.getParamTypes(); |
| List<String> paramNames = mi.getParamNames(); |
| |
| int nthParam = 0; |
| final int defaultsStartAt = paramTypes.size() - defaultValues.size(); |
| final Boolean needsRest = (mi.getFlags() & ABCConstants.NEED_REST) > 0; |
| final int restStartsAt = needsRest ? paramNames.size() - 1 : paramNames.size(); |
| |
| for (String argName : paramNames) |
| { |
| if (nthParam == restStartsAt) |
| { |
| // param is rest argument |
| writeString(indent + " * @param {...} " + argName + "\n"); |
| } |
| else |
| { |
| Name nextParam = paramTypes.elementAt(nthParam); |
| final StringBuilder sb = new StringBuilder(); |
| JSGeneratingReducer.nameToJSDocType(m_project, nextParam, sb); |
| final String argType = sb.toString(); |
| if (nthParam < defaultsStartAt) |
| { |
| // param without default value |
| writeString(indent + " * @param {" + argType + "} " + argName + "\n"); |
| } |
| else |
| { |
| // param with default value |
| writeString(indent + " * @param {" + argType + "} " + JSSharedData.DEFAULT_PARAM_PREFIX + argName); |
| |
| String defaultVal = "undefined"; |
| PooledValue val = defaultValues.elementAt(nthParam - defaultsStartAt); |
| if (val != null) |
| { |
| switch (val.getKind()) |
| { |
| case ABCConstants.CONSTANT_Int: |
| defaultVal = val.getIntegerValue().toString(); |
| break; |
| case ABCConstants.CONSTANT_UInt: |
| defaultVal = val.getLongValue().toString(); |
| break; |
| case ABCConstants.CONSTANT_Double: |
| defaultVal = val.getDoubleValue().toString(); |
| break; |
| case ABCConstants.CONSTANT_Utf8: |
| defaultVal = val.getStringValue(); |
| break; |
| case ABCConstants.CONSTANT_True: |
| defaultVal = "true"; |
| break; |
| case ABCConstants.CONSTANT_False: |
| defaultVal = "false"; |
| break; |
| case ABCConstants.CONSTANT_Undefined: |
| defaultVal = "undefined"; |
| break; |
| case ABCConstants.CONSTANT_Null: |
| defaultVal = "null"; |
| break; |
| default: |
| { |
| final Namespace ns = val.getNamespaceValue(); |
| if (ns != null) |
| defaultVal = ns.getName() + " /* (namespace) */"; |
| } |
| break; |
| } |
| } |
| writeString(" Defaults to " + defaultVal + ".\n"); |
| } |
| } |
| |
| nthParam++; |
| } |
| } |
| |
| protected Boolean isPackageFunction(MethodInfo mi) |
| { |
| final FunctionDefinition fdef = m_methodInfoToDefinition.get(mi); |
| if (fdef != null && fdef.getParent() instanceof PackageDefinition) |
| return true; |
| |
| return false; |
| } |
| |
| protected void emitFrameworkInit() |
| { |
| writeString("\n"); |
| writeString("// Overrides \n"); |
| |
| writeString("adobe.globals = __global;\n"); |
| |
| // auto-register classes |
| writeString("" + JSSharedData.JS_CLASSES + " = {};\n"); |
| writeString("" + JSSharedData.JS_INT_CLASS + " = IntClass;\n"); |
| writeString("" + JSSharedData.JS_UINT_CLASS + " = UIntClass;\n"); |
| |
| // override settings for adobe.USE_SELF and adobe.USE_STATIC |
| final Boolean useClosure = JSSharedData.m_useClosureLib; |
| final Boolean useSelfParameter = JSSharedData.m_useSelfParameter; |
| final Boolean convertToStatic = useSelfParameter && false; // see JSSharedData.m_convertToStatic |
| writeString("\n"); |
| writeString("// Settings \n"); |
| writeString("adobe.USE_CLOSURE = " + (useClosure ? "true" : "false") + ";\n"); |
| writeString("adobe.USE_SELF = " + (useSelfParameter ? "true" : "false") + ";\n"); |
| writeString("adobe.USE_STATIC = " + (convertToStatic ? "true" : "false") + ";\n"); |
| writeString("adobe.ROOT_NAME = \"" + JSSharedData.ROOT_NAME + "\";\n"); |
| writeString("adobe.NO_EXPORTS = " + JSSharedData.NO_EXPORTS + ";\n"); |
| writeString("adobe.root = " + JSSharedData.JS_SYMBOLS + ";\n"); |
| } |
| |
| protected void emitMethod(Trait t, MethodInfo mi, Boolean isFramework, Boolean isExtern, Boolean isInterface, Boolean isPackageFunction, String fullName, String name, String xetter, String methodPrefix, String assignmentOp, String separator, String indent) throws Exception |
| { |
| // don't emit extern package functions |
| if (isExtern && isPackageFunction) |
| return; |
| |
| MethodBodyInfo mbi = findMethodBodyInfo(mi); |
| |
| if (name != null) |
| { |
| // Sometimes mi.name is empty |
| if (mi.getMethodName() == null || mi.getMethodName().isEmpty()) |
| mi.setMethodName(name); |
| |
| // set m_currentScope to classScope |
| if (m_currentScope != null) |
| { |
| IASScope methodScope = null; |
| IASScope scope = m_currentScope; |
| ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.MEMBERS_AND_TYPES, |
| SearchScopeValue.ALL_SCOPES, |
| AccessValue.ALL, |
| scope.getDefinition()); |
| IDefinition def = ASScopeUtils.findDefinitionByName(scope, m_project, name, filter); |
| if (def != null) |
| { |
| //FunctionDefinition |
| // methodScope = new ASScope((ASScope)def.getContainingScope()); |
| methodScope = def.getContainingScope(); |
| m_currentScope = methodScope; |
| } |
| } |
| |
| emitJSDocForMethod(t, mi, mbi, fullName, indent); |
| |
| final Boolean isGetter = t != null && t.getKind() == TRAIT_Getter; |
| // final Boolean isPackageFunction = methodPrefix.isEmpty() && isPackageFunction(mi); |
| |
| // regular constructor |
| writeString(indent + methodPrefix + xetter + name); |
| if (isInterface && isGetter) |
| { |
| writeString(";\n"); |
| return; |
| } |
| |
| writeString(" " + assignmentOp + " "); |
| |
| writeString("function("); |
| String a_priori_insns = ""; |
| // a_priori_insns = emitParameters( m_methodInfoToDefinition.get(mi) ); |
| a_priori_insns = emitParameters(mi); |
| writeString(")"); |
| |
| // return type |
| // if (mi.getReturnType() != null) |
| // writeString(" /* : " + JSGeneratingReducer.getBasenameFromName(mi.getReturnType()) + " */"); |
| |
| if (isInterface) |
| { |
| writeString(" {};\n"); |
| } |
| else |
| { |
| writeString("\n"); |
| writeString(indent + "{"); |
| writeString("\n"); |
| if (!a_priori_insns.isEmpty()) |
| { |
| writeString(a_priori_insns); |
| } |
| emitMethodBody(mbi); |
| writeString(indent + "};"); |
| } |
| |
| } |
| else |
| { |
| // all __static_inits need to be registered. |
| if (!JSSharedData.instance.hasClassInit(fullName)) |
| JSSharedData.instance.registerClassInit(fullName); |
| |
| final String staticInitName = methodPrefix + JSSharedData.STATIC_INIT; |
| |
| writeString("\n\n"); |
| writeString("/**\n"); |
| writeString(" * Method: " + indent + staticInitName + "()\n"); |
| writeString(" */\n"); |
| |
| writeString(indent + staticInitName + " = "); |
| writeString("function() /* : void */\n"); |
| writeString(indent + "{\n"); |
| writeString(indent + " " + staticInitName + " = " + JSSharedData.JS_EMPTY_FUNCTION + ";\n"); |
| |
| // static init |
| emitMethodBody(mbi); |
| |
| writeString(indent + "}"); |
| } |
| } |
| |
| private void emitScriptInfo(ScriptInfo info) throws Exception |
| { |
| // w.writeU30(getScriptId(info.getInitId())); |
| // emitTraits(info.getTraits(), "", " " ); |
| |
| final Object init_id = info.getInit(); |
| ; |
| if (init_id instanceof MethodInfo) |
| { |
| final MethodInfo mi = (MethodInfo)init_id; |
| final MethodBodyInfo mbi = this.findMethodBodyInfo(mi); |
| if (mbi != null) |
| { |
| final String str = extractCodeFromMethodBodyInfo(mbi); |
| if (!str.isEmpty()) |
| { |
| final StringBuilder scriptInfos = new StringBuilder(); |
| |
| scriptInfos.append("// ScriptInfo \n"); |
| scriptInfos.append("{\n"); |
| scriptInfos.append(" var " + JSSharedData.THIS + " = "); |
| if (m_packageName != null && !m_packageName.isEmpty()) |
| { |
| scriptInfos.append(m_packageName + ";\n"); |
| } |
| else |
| { |
| scriptInfos.append(JSSharedData.JS_FRAMEWORK_NAME + ".globals;\n"); |
| } |
| |
| // This is just crazy... |
| // It looks like very variable declared in a script info block becomes a global variable. |
| // See testUnicodeRangeHelper() in Tamarin's ecma3/Unicode/unicodeUtil.as, which for some |
| // reasons uses "this.array" instead of just "array". |
| final StringBuilder sb = new StringBuilder(); |
| for (String line : str.split("\n")) |
| { |
| if (line.startsWith("var ")) |
| sb.append(line.replaceFirst("var ", JSSharedData.THIS + ".")); |
| else |
| sb.append(line); |
| sb.append("\n"); |
| } |
| |
| // the meat of emitCode(mbi); |
| scriptInfos.append(JSGeneratingReducer.indentBlock(sb.toString(), 1)); |
| |
| scriptInfos.append("}\n"); |
| |
| JSSharedData.instance.registerScriptInfo(scriptInfos.toString()); |
| } |
| } |
| } |
| } |
| |
| protected void emitMethodBody(MethodBodyInfo f) |
| throws Exception |
| { |
| if (f == null) |
| return; |
| |
| /* |
| * MethodInfo signature = f.getMethodInfo(); |
| * w.writeU30(getMethodId(signature)); f.computeFrameCounts(); |
| * w.writeU30(f.getMaxStack()); int max_local = f.getLocalCount(); if |
| * (signature.getParamCount() > max_local) max_local = |
| * signature.getParamCount(); w.writeU30(max_local); |
| * w.writeU30(f.getInitScopeDepth()); w.writeU30(f.getMaxScopeDepth()); |
| */ |
| |
| if (!m_methodPrologue.isEmpty()) |
| { |
| writeString(m_methodPrologue); |
| m_methodPrologue = ""; |
| } |
| |
| emitCode(f); |
| |
| if (!m_methodPostlogue.isEmpty()) |
| { |
| writeString(m_methodPostlogue); |
| m_methodPostlogue = ""; |
| } |
| } |
| |
| private void emitCode(MethodBodyInfo f) |
| throws Exception |
| { |
| String str = extractCodeFromMethodBodyInfo(f); |
| |
| /* |
| * Experimental: ABC decompilation if( str.isEmpty() ) { final |
| * ByteArrayOutputStream out = new ByteArrayOutputStream(); final |
| * JSConverter converter = new JSConverter(m_project, m_currentScope, |
| * out ); final MethodInfo mi = f.getMethodInfo(); final IMethodVisitor |
| * mv = converter.visitMethod(mi); mv.visit(); final IMethodBodyVisitor |
| * mbv = mv.visitBody(f); mbv.visit(); mbv.visitEnd(); mv.visitEnd(); |
| * str = out.toString(); } |
| */ |
| |
| if (!str.isEmpty()) |
| { |
| writeString(JSGeneratingReducer.indentBlock(str, 1)); |
| } |
| } |
| |
| /* |
| * private String indentBlock( String block, int indentBy ) { Boolean |
| * firstPart = true; String s = ""; String[] parts = block.split( "\n" ); |
| * for( String part : parts ) { if( firstPart ) firstPart = false; else s += |
| * "\n"; for( int i = 0; i < indentBy; ++i ) s += " "; s += part; } return |
| * s; } private void emitNamespace(Namespace ns) { w.write(ns.getKind()); |
| * w.writeU30(stringPool.id(ns.getName())); } |
| */ |
| |
| private String extractCodeFromBlock(IBasicBlock b) |
| { |
| String str = ""; |
| for (int i = 0; i < b.size() && !b.get(i).isBranch(); i++) |
| { |
| final Instruction insn = b.get(i); |
| if (insn.getOpcode() == JSSharedData.OP_JS) |
| str += (String)insn.getOperand(0); |
| } |
| return str; |
| } |
| |
| private String extractCodeFromMethodBodyInfo(MethodBodyInfo mbi) |
| { |
| String str = ""; |
| for (IBasicBlock b : mbi.getCfg().getBlocksInEntryOrder()) |
| { |
| str += extractCodeFromBlock(b); |
| } |
| return str; |
| } |
| |
| class JSOutputStream extends ByteArrayOutputStream |
| { |
| void rewind(int n) |
| { |
| super.count -= n; |
| } |
| |
| void writeString(String str) |
| { |
| try |
| { |
| write(str.getBytes()); |
| } |
| catch (IOException e) |
| { |
| |
| } |
| } |
| |
| public void write(int i) |
| { |
| // super.write(i); |
| } |
| |
| void writeU16(int i) |
| { |
| //write(i); |
| //write(i >> 8); |
| } |
| |
| void writeS24(int i) |
| { |
| //writeU16(i); |
| //write(i >> 16); |
| } |
| |
| void write64(long i) |
| { |
| //writeS24((int)i); |
| //writeS24((int)(i >> 24)); |
| //writeU16((int)(i >> 48)); |
| } |
| |
| void writeU30(int v) |
| { |
| /* |
| * if (v < 128 && v >= 0) { write(v); } else if (v < 16384 && v >= |
| * 0) { write(v & 0x7F | 0x80); write(v >> 7); } else if (v < |
| * 2097152 && v >= 0) { write(v & 0x7F | 0x80); write(v >> 7 | |
| * 0x80); write(v >> 14); } else if (v < 268435456 && v >= 0) { |
| * write(v & 0x7F | 0x80); write(v >> 7 | 0x80); write(v >> 14 | |
| * 0x80); write(v >> 21); } else { write(v & 0x7F | 0x80); write(v |
| * >> 7 | 0x80); write(v >> 14 | 0x80); write(v >> 21 | 0x80); |
| * write(v >> 28); } |
| */ |
| } |
| |
| int sizeOfU30(int v) |
| { |
| if (v < 128 && v >= 0) |
| { |
| return 1; |
| } |
| else if (v < 16384 && v >= 0) |
| { |
| return 2; |
| } |
| else if (v < 2097152 && v >= 0) |
| { |
| return 3; |
| } |
| else if (v < 268435456 && v >= 0) |
| { |
| return 4; |
| } |
| else |
| { |
| return 5; |
| } |
| } |
| } |
| |
| /** |
| * The nominal name given to a MultinameL. |
| * |
| * @see http://bugs.adobe.com/jira/browse/CMP-393, this may not be |
| * necessary, and if under some circumstances it is necessary this is |
| * certainly not the name required. |
| */ |
| private static Name indexAccessName; |
| /** |
| * The above Name packaged up into operand form. |
| */ |
| private static Object[] indexAccessOperands; |
| /* |
| * Initialization code for indexAccessname and indexAccessOperands. |
| */ |
| static |
| { |
| Vector<Namespace> usual_suspects = new Vector<Namespace>(2); |
| usual_suspects.add(new Namespace(CONSTANT_PrivateNs)); |
| usual_suspects.add(new Namespace(CONSTANT_PackageNs)); |
| indexAccessName = new Name(ABCConstants.CONSTANT_MultinameL, new Nsset(usual_suspects), null); |
| indexAccessOperands = new Object[] {indexAccessName}; |
| } |
| |
| /** |
| * Find all the operands in a method body and make sure they find their way |
| * into the appropriate pool. |
| * |
| * @param mi - the method body. |
| * @post any runtime multinames have a dummy Name operand. |
| */ |
| void _poolOperands(MethodBodyInfo mbi) |
| { |
| for (IBasicBlock b : mbi.getCfg().getBlocksInEntryOrder()) |
| for (Instruction insn : b.getInstructions()) |
| switch (insn.getOpcode()) |
| { |
| case OP_findproperty: |
| case OP_findpropstrict: |
| case OP_getlex: |
| case OP_getsuper: |
| case OP_setsuper: |
| case OP_getdescendants: |
| case OP_initproperty: |
| case OP_istype: |
| case OP_coerce: |
| case OP_astype: |
| case OP_finddef: |
| visitPooledName((Name)insn.getOperand(0)); |
| break; |
| case OP_deleteproperty: |
| case OP_getproperty: |
| case OP_setproperty: |
| if (insn.getOperandCount() > 0 && insn.getOperand(0) != null) |
| { |
| visitPooledName((Name)insn.getOperand(0)); |
| } |
| else |
| { |
| // get/set/deleteproperty with no operands => runtime multiname. |
| visitPooledName(indexAccessName); |
| insn.setOperands(indexAccessOperands); |
| } |
| break; |
| case OP_callproperty: |
| case OP_callproplex: |
| case OP_callpropvoid: |
| case OP_callsuper: |
| case OP_callsupervoid: |
| case OP_constructprop: |
| visitPooledName((Name)insn.getOperand(0)); |
| break; |
| case OP_pushstring: |
| case OP_dxns: |
| case OP_debugfile: |
| break; |
| case OP_pushnamespace: |
| visitPooledNamespace((Namespace)insn.getOperand(0)); |
| break; |
| case OP_pushint: |
| break; |
| case OP_pushuint: |
| break; |
| case OP_pushdouble: |
| break; |
| case OP_debug: |
| break; |
| } |
| } |
| |
| /** |
| * Inspect a set of Traits and ensure that constant initializer values have |
| * been properly registered in the constant pools. |
| */ |
| private void poolTraitValues(Traits ts) |
| { |
| for (Trait t : ts) |
| { |
| if (t.hasAttr(Trait.SLOT_VALUE)) |
| { |
| Object trait_value = t.getAttr(Trait.SLOT_VALUE); |
| if (trait_value == null) |
| { |
| // No action required; the pool ID resolution logic |
| // handles a null pointer by returning the nil pool value. |
| } |
| else if (trait_value instanceof String) |
| { |
| visitPooledString((String)trait_value); |
| } |
| else if (trait_value instanceof Namespace) |
| { |
| visitPooledNamespace((Namespace)trait_value); |
| } |
| else if (trait_value instanceof Double) |
| { |
| visitPooledDouble((Double)trait_value); |
| } |
| else if (trait_value instanceof Integer) |
| { |
| visitPooledInt((Integer)trait_value); |
| } |
| else if (trait_value instanceof Long) |
| { |
| visitPooledUInt((Long)trait_value); |
| } |
| else if (trait_value instanceof Float) |
| { |
| visitPooledFloat((Float)trait_value); |
| } |
| else if (trait_value.equals(ABCConstants.UNDEFINED_VALUE) |
| || trait_value.equals(ABCConstants.NULL_VALUE) |
| || trait_value.equals(Boolean.TRUE) |
| || trait_value.equals(Boolean.FALSE)) |
| { |
| // Nothing to do, predefined value. |
| } |
| else |
| { |
| throw new IllegalStateException("Unrecognized initializer type: " + trait_value.getClass().toString()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Disambiguate input from multiple ABCs by numbering them. |
| */ |
| // private int abcNum = 0; |
| |
| public void visit(int majorVersion, int minorVersion) |
| { |
| if (m_visitor != null) |
| m_visitor.visit(majorVersion, minorVersion); |
| // abcNum++; |
| } |
| |
| public void visitEnd() |
| { |
| if (m_visitor != null) |
| m_visitor.visitEnd(); |
| } |
| |
| public IClassVisitor visitClass(InstanceInfo iinfo, ClassInfo cinfo) |
| { |
| IClassVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitClass(iinfo, cinfo); |
| this.definedClasses.add(new EmitterClassVisitor(iinfo, cinfo, visitor)); |
| IClassVisitor result = this.definedClasses.lastElement(); |
| result.visit(); |
| return result; |
| } |
| |
| public IScriptVisitor visitScript() |
| { |
| IScriptVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitScript(); |
| this.scriptInfos.add(new ScriptInfo()); |
| return new EmitterScriptInfo(this.scriptInfos.lastElement(), visitor); |
| } |
| |
| public IMethodVisitor visitMethod(MethodInfo minfo) |
| { |
| IMethodVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitMethod(minfo); |
| |
| methodInfos.add(minfo); |
| for (Name param_type_name : minfo.getParamTypes()) |
| visitPooledName(param_type_name); |
| |
| return new EmitterMethodInfoVisitor(minfo, visitor); |
| } |
| |
| public void visitPooledDouble(Double d) |
| { |
| } |
| |
| public void visitPooledInt(Integer i) |
| { |
| } |
| |
| public void visitPooledMetadata(Metadata md) |
| { |
| visitPooledString(md.getName()); |
| |
| for (String key : md.getKeys()) |
| visitPooledString(key); |
| |
| for (String value : md.getValues()) |
| visitPooledString(value); |
| } |
| |
| public void visitPooledName(Name n) |
| { |
| if (null == n) |
| return; |
| |
| if (n.getKind() != ABCConstants.CONSTANT_TypeName) |
| { |
| visitPooledString(n.getBaseName()); |
| visitPooledNsSet(n.getQualifiers()); |
| } |
| else |
| { |
| visitPooledName(n.getTypeNameBase()); |
| visitPooledName(n.getTypeNameParameter()); |
| } |
| } |
| |
| public void visitPooledNamespace(Namespace ns) |
| { |
| if (ns != null) |
| visitPooledString(ns.getName()); |
| } |
| |
| public void visitPooledNsSet(Nsset nss) |
| { |
| if (nss != null) |
| { |
| for (Namespace ns : nss) |
| { |
| visitPooledNamespace(ns); |
| } |
| } |
| } |
| |
| public void visitPooledString(String s) |
| { |
| } |
| |
| public void visitPooledUInt(Long l) |
| { |
| } |
| |
| public void visitPooledFloat(Float f) |
| { |
| throw new IllegalStateException("not implemented."); |
| } |
| |
| public static int sizeOfU30(int v) |
| { |
| if (v < 128 && v >= 0) |
| { |
| return 1; |
| } |
| else if (v < 16384 && v >= 0) |
| { |
| return 2; |
| } |
| else if (v < 2097152 && v >= 0) |
| { |
| return 3; |
| } |
| else if (v < 268435456 && v >= 0) |
| { |
| return 4; |
| } |
| else |
| { |
| return 5; |
| } |
| } |
| |
| protected class EmitterClassVisitor implements IClassVisitor |
| { |
| ClassInfo classInfo; |
| Traits classTraits; |
| InstanceInfo instanceInfo; |
| Traits instanceTraits; |
| IClassVisitor m_visitor; |
| |
| /** |
| * Disambiguate classes with the same name from different ABCs. |
| */ |
| // TODO: public int abcNum = JSEmitter.this.abcNum; |
| |
| EmitterClassVisitor(InstanceInfo iinfo, ClassInfo cinfo, IClassVisitor visitor) |
| { |
| this.m_visitor = visitor; |
| |
| this.classInfo = cinfo; |
| if (null == cinfo.classTraits) |
| cinfo.classTraits = new Traits(); |
| this.classTraits = cinfo.classTraits; |
| |
| this.instanceInfo = iinfo; |
| |
| if (null == iinfo.traits) |
| iinfo.traits = new Traits(); |
| this.instanceTraits = iinfo.traits; |
| if (null == iinfo.interfaceNames) |
| iinfo.interfaceNames = new Name[0]; |
| } |
| |
| public void visit() |
| { |
| m_currentClassVisitor = this; |
| |
| // register as extern |
| final Name name = instanceInfo.name; |
| final IDefinition def = getDefinition(name); |
| final String packageName = def == null ? m_packageName : def.getPackageName(); |
| final Boolean isExtern = isExtern(instanceInfo); |
| if (isExtern) |
| { |
| m_sharedData.registerExtern(name); |
| // name = JSGeneratingReducer.makeName(JSGeneratingReducer.getBasenameFromName(name)); |
| } |
| |
| // extract and register package |
| m_sharedData.registerPackage(packageName); |
| |
| // register class with super class |
| if (!instanceInfo.isInterface() && !isExtern) |
| m_sharedData.registerClass(name, instanceInfo.superName); |
| |
| if (!isExtern && instanceInfo.interfaceNames != null) |
| { |
| for (Name iname : instanceInfo.interfaceNames) |
| { |
| // Skipping classes that are "marked" as IExtern. |
| if (!JSGeneratingReducer.getBasenameFromName(iname).equals(JSSharedData.EXTERN_INTERFACE_NAME)) |
| { |
| /* |
| * if( isExtern(iname) ) iname = |
| * JSGeneratingReducer.makeName |
| * (JSGeneratingReducer.getBasenameFromName(iname)); |
| */ |
| m_sharedData.registerInterface(name, iname); |
| } |
| } |
| } |
| } |
| |
| public ITraitsVisitor visitClassTraits() |
| { |
| ITraitsVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitClassTraits(); |
| return new EmitterTraitsVisitor(this.classTraits, visitor); |
| } |
| |
| public ITraitsVisitor visitInstanceTraits() |
| { |
| ITraitsVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitInstanceTraits(); |
| return new EmitterTraitsVisitor(this.instanceTraits, visitor); |
| } |
| |
| public void visitEnd() |
| { |
| if (null == classInfo.cInit) |
| { |
| classInfo.cInit = new MethodInfo(); |
| MethodBodyInfo m_cinit = new MethodBodyInfo(); |
| m_cinit.setMethodInfo(classInfo.cInit); |
| |
| IMethodVisitor mv = visitMethod(classInfo.cInit); |
| mv.visit(); |
| IMethodBodyVisitor mbv = mv.visitBody(m_cinit); |
| mbv.visit(); |
| mbv.visitInstruction(OP_returnvoid); |
| mbv.visitEnd(); |
| mv.visitEnd(); |
| } |
| |
| visitPooledName(instanceInfo.name); |
| visitPooledName(instanceInfo.superName); |
| |
| if (instanceInfo.hasProtectedNs()) |
| visitPooledNamespace(instanceInfo.protectedNs); |
| |
| if (null == instanceInfo.iInit) |
| { |
| instanceInfo.iInit = new MethodInfo(); |
| MethodBodyInfo iinit = new MethodBodyInfo(); |
| iinit.setMethodInfo(instanceInfo.iInit); |
| |
| IMethodVisitor mv = visitMethod(instanceInfo.iInit); |
| mv.visit(); |
| |
| // Interfaces need an instance init method, |
| // but it doesn't have a body. |
| if (0 == (instanceInfo.flags & ABCConstants.CLASS_FLAG_interface)) |
| { |
| IMethodBodyVisitor mbv = mv.visitBody(iinit); |
| mbv.visit(); |
| /* |
| * mbv.visitInstruction(OP_getlocal0); |
| * mbv.visitInstruction(OP_pushscope); |
| * mbv.visitInstruction(OP_getlocal0); |
| * mbv.visitInstruction(ABCConstants.OP_constructsuper, 0); |
| * mbv.visitInstruction(OP_returnvoid); |
| */ |
| mbv.visitEnd(); |
| } |
| mv.visitEnd(); |
| } |
| |
| if (instanceInfo.interfaceNames != null) |
| { |
| for (Name interface_name : instanceInfo.interfaceNames) |
| { |
| visitPooledName(interface_name); |
| } |
| } |
| |
| poolTraitValues(classInfo.classTraits); |
| poolTraitValues(instanceInfo.traits); |
| |
| // m_currentClassVisitor = null; |
| } |
| } |
| |
| private class EmitterTraitsVisitor implements ITraitsVisitor |
| { |
| |
| Traits traits; |
| ITraitsVisitor m_visitor; |
| |
| EmitterTraitsVisitor(Traits traits, ITraitsVisitor visitor) |
| { |
| this.traits = traits; |
| this.m_visitor = visitor; |
| } |
| |
| private String getFullClassName() |
| { |
| if (m_currentClassVisitor != null) |
| { |
| final IDefinition def = getDefinition(m_currentClassVisitor.instanceInfo.name); |
| if (def != null) |
| return def.getQualifiedName(); |
| } |
| return ""; |
| } |
| |
| private Boolean hasClassTraits() |
| { |
| return m_currentClassVisitor != null && m_currentClassVisitor.classInfo.classTraits == this.traits; |
| } |
| |
| private Boolean hasMethodTraits() |
| { |
| return m_currentClassVisitor != null && m_currentClassVisitor.instanceInfo.traits == this.traits; |
| } |
| |
| public ITraitVisitor visitClassTrait(int kind, Name name, int slot_id, ClassInfo clazz) |
| { |
| ITraitVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitClassTrait(kind, name, slot_id, clazz); |
| |
| Trait t = createTrait(kind, name); |
| if (slot_id != 0) |
| t.addAttr(Trait.TRAIT_SLOT, slot_id); |
| t.addAttr(Trait.TRAIT_CLASS, clazz); |
| |
| // this is missing in ABCEmitter. |
| clazz.classTraits.add(t); |
| |
| return new EmitterTraitVisitor(t, visitor); |
| } |
| |
| public ITraitVisitor visitMethodTrait(int kind, Name name, int dispId, |
| MethodInfo method) |
| { |
| ITraitVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitMethodTrait(kind, name, dispId, method); |
| |
| String fullName = getFullClassName(); |
| if (fullName.isEmpty() && m_packageName != null) |
| fullName = m_packageName; |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| |
| final String baseName = JSGeneratingReducer.getBasenameFromName(name); |
| String type = "Function"; |
| switch (kind) |
| { |
| case TRAIT_Method: |
| case TRAIT_Function: |
| { |
| type = "Function"; |
| fullName += baseName; |
| } |
| break; |
| case TRAIT_Getter: |
| { |
| type = "Getter"; |
| fullName += JSSharedData.GETTER_PREFIX + baseName; |
| // m_sharedData.registerGetter(fullName + baseName)); |
| } |
| break; |
| case TRAIT_Setter: |
| { |
| type = "Setter"; |
| fullName += JSSharedData.SETTER_PREFIX + baseName; |
| // m_sharedData.registerSetter(fullName + baseName); |
| } |
| break; |
| } |
| |
| m_sharedData.registerVarType(fullName, type); |
| // if( hasClassTraits() ) |
| // m_sharedData.registerStaticMember(fullName); |
| |
| Trait t = createTrait(kind, name); |
| t.addAttr(Trait.TRAIT_METHOD, method); |
| return new EmitterTraitVisitor(t, visitor); |
| } |
| |
| public ITraitVisitor visitSlotTrait(int kind, Name name, int slotId, |
| Name slotType, Object slotValue) |
| { |
| final String baseName = JSGeneratingReducer.getBasenameFromName(name); |
| |
| ITraitVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitSlotTrait(kind, name, slotId, slotType, slotValue); |
| |
| if (slotType != null && kind == TRAIT_Var && (hasClassTraits() || hasMethodTraits())) |
| { |
| |
| String fullName = getFullClassName(); |
| if (!fullName.isEmpty()) |
| fullName += "."; |
| fullName += baseName; |
| final IDefinition def = getDefinition(slotType); |
| if (!(def instanceof AmbiguousDefinition)) |
| m_sharedData.registerVarType(fullName, def.getQualifiedName()); |
| } |
| |
| Trait t = createTrait(kind, name); |
| t.addAttr(Trait.TRAIT_SLOT, slotId); |
| t.addAttr(Trait.TRAIT_TYPE, slotType); |
| t.addAttr(Trait.SLOT_VALUE, slotValue); |
| if (slotType != null) |
| visitPooledName(slotType); |
| return new EmitterTraitVisitor(t, visitor); |
| } |
| |
| public void visit() |
| { |
| if (m_visitor != null) |
| m_visitor.visit(); |
| } |
| |
| public void visitEnd() |
| { |
| if (m_visitor != null) |
| m_visitor.visitEnd(); |
| } |
| |
| public Traits getTraits() |
| { |
| return this.traits; |
| } |
| |
| private Trait createTrait(int kind, Name name) |
| { |
| Trait t = new Trait(kind, name); |
| traits.add(t); |
| visitPooledName(name); |
| return t; |
| } |
| } |
| |
| private class EmitterMetadataVisitor implements IMetadataVisitor |
| { |
| Trait t; |
| IMetadataVisitor m_visitor; |
| |
| EmitterMetadataVisitor(Trait t, IMetadataVisitor visitor) |
| { |
| this.t = t; |
| this.m_visitor = visitor; |
| } |
| |
| public void visit(Metadata md) |
| { |
| if (m_visitor != null) |
| m_visitor.visit(md); |
| |
| visitPooledMetadata(md); |
| t.addMetadata(md); |
| } |
| } |
| |
| public class EmitterTraitVisitor implements ITraitVisitor |
| { |
| Trait t; |
| ITraitVisitor m_visitor; |
| |
| EmitterTraitVisitor(Trait t, ITraitVisitor visitor) |
| { |
| this.t = t; |
| this.m_visitor = visitor; |
| } |
| |
| public IMetadataVisitor visitMetadata(int count) |
| { |
| IMetadataVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitMetadata(count); |
| |
| return new EmitterMetadataVisitor(t, visitor); |
| } |
| |
| public void visitAttribute(String attr_name, Object attr_value) |
| { |
| if (m_visitor != null) |
| m_visitor.visitAttribute(attr_name, attr_value); |
| this.t.addAttr(attr_name, attr_value); |
| } |
| |
| public void visitStart() |
| { |
| if (m_visitor != null) |
| m_visitor.visitStart(); |
| } |
| |
| public void visitEnd() |
| { |
| if (m_visitor != null) |
| m_visitor.visitEnd(); |
| } |
| } |
| |
| private class EmitterMethodBodyInfo implements IMethodBodyVisitor |
| { |
| private MethodBodyInfo mbi; |
| public IMethodBodyVisitor m_visitor; |
| |
| EmitterMethodBodyInfo(MethodBodyInfo mbinfo, IMethodBodyVisitor visitor) |
| { |
| this.mbi = mbinfo; |
| this.m_visitor = visitor; |
| } |
| |
| public void visit() |
| { |
| if (m_visitor != null) |
| m_visitor.visit(); |
| } |
| |
| public void visitEnd() |
| { |
| if (m_visitor != null) |
| m_visitor.visitEnd(); |
| } |
| |
| public void visitInstruction(int opcode) |
| { |
| if (m_visitor != null) |
| m_visitor.visitInstruction(opcode); |
| this.mbi.insn(opcode); |
| } |
| |
| public void visitInstruction(int opcode, int immediate_operand) |
| { |
| if (m_visitor != null) |
| m_visitor.visitInstruction(opcode, immediate_operand); |
| this.mbi.insn(opcode, immediate_operand); |
| } |
| |
| public void visitInstruction(int opcode, Object single_operand) |
| { |
| if (m_visitor != null) |
| m_visitor.visitInstruction(opcode, single_operand); |
| this.mbi.insn(opcode, single_operand); |
| } |
| |
| public void visitInstruction(int opcode, Object[] operands) |
| { |
| if (m_visitor != null) |
| m_visitor.visitInstruction(opcode, operands); |
| this.mbi.insn(opcode, operands); |
| } |
| |
| public void visitInstruction(Instruction i) |
| { |
| if (m_visitor != null) |
| m_visitor.visitInstruction(i); |
| this.mbi.insn(i); |
| } |
| |
| public ITraitsVisitor visitTraits() |
| { |
| ITraitsVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitTraits(); |
| return new EmitterTraitsVisitor(this.mbi.getTraits(), visitor); |
| } |
| |
| public int visitException(Label from, Label to, Label target, Name ex_type, Name ex_var) |
| { |
| if (m_visitor != null) |
| m_visitor.visitException(from, to, target, ex_type, ex_var); |
| visitPooledName(ex_type); |
| visitPooledName(ex_var); |
| return mbi.addExceptionInfo(new ExceptionInfo(from, to, target, ex_type, ex_var)); |
| } |
| |
| public void visitInstructionList(InstructionList new_list) |
| { |
| if (m_visitor != null) |
| m_visitor.visitInstructionList(new_list); |
| mbi.setInstructionList(new_list); |
| } |
| |
| public void labelCurrent(Label l) |
| { |
| if (m_visitor != null) |
| m_visitor.labelCurrent(l); |
| mbi.labelCurrent(l); |
| } |
| |
| public void labelNext(Label l) |
| { |
| if (m_visitor != null) |
| m_visitor.labelNext(l); |
| mbi.labelNext(l); |
| } |
| |
| } |
| |
| private class EmitterScriptInfo implements IScriptVisitor |
| { |
| |
| ScriptInfo si; |
| IScriptVisitor m_visitor; |
| |
| EmitterScriptInfo(ScriptInfo s, IScriptVisitor visitor) |
| { |
| this.si = s; |
| this.m_visitor = visitor; |
| } |
| |
| public void visit() |
| { |
| } |
| |
| public void visitEnd() |
| { |
| poolTraitValues(si.getTraits()); |
| } |
| |
| public void visitInit(MethodInfo init_method) |
| { |
| si.setInit(init_method); |
| } |
| |
| public ITraitsVisitor visitTraits() |
| { |
| ITraitsVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitTraits(); |
| |
| return new EmitterTraitsVisitor(si.getTraits(), visitor); |
| } |
| |
| } |
| |
| private class EmitterMethodInfoVisitor implements IMethodVisitor |
| { |
| MethodInfo mi; |
| MethodBodyInfo mbi; |
| IMethodVisitor m_visitor; |
| |
| EmitterMethodInfoVisitor(MethodInfo mi, IMethodVisitor visitor) |
| { |
| this.mi = mi; |
| this.m_visitor = visitor; |
| } |
| |
| public void visit() |
| { |
| if (m_visitor != null) |
| m_visitor.visit(); |
| |
| if (m_currentClassVisitor != null) |
| { |
| |
| } |
| } |
| |
| public IMethodBodyVisitor visitBody(MethodBodyInfo mbi) |
| { |
| IMethodBodyVisitor visitor = null; |
| if (m_visitor != null) |
| visitor = m_visitor.visitBody(mbi); |
| |
| this.mbi = mbi; |
| return new EmitterMethodBodyInfo(mbi, visitor); |
| } |
| |
| public void visitEnd() |
| { |
| if (m_visitor != null) |
| m_visitor.visitEnd(); |
| |
| if (mbi != null) |
| { |
| // a native method should never have a method body |
| assert (!mi.isNative()); |
| |
| _poolOperands(mbi); |
| poolTraitValues(mbi.getTraits()); |
| methodBodies.add(mbi); |
| } |
| |
| visitPooledString(mi.getMethodName()); |
| visitPooledName(mi.getReturnType()); |
| |
| if (mi.hasParamNames()) |
| { |
| for (String param_name : mi.getParamNames()) |
| { |
| visitPooledString(param_name); |
| } |
| } |
| } |
| } |
| |
| protected String buildSuperClassName(String className, String _super) |
| { |
| String superClass = ""; |
| |
| if (_super == null) |
| { |
| superClass = "Object"; |
| } |
| else if (_super.indexOf("jQuery.jQuery") == 0 || _super.indexOf("jQuery.event") == 0) |
| { |
| superClass = "jQuery"; |
| } |
| else if (_super.indexOf("jQuery.event") == 0) |
| { |
| superClass = "jQuery.Event"; |
| } |
| else if (_super.equals("browser.JSError")) |
| { |
| // JSError will get mapped to the browser's Error class. |
| superClass = "Error"; |
| } |
| else if (JSGeneratingReducer.isDataType(_super)) |
| { |
| superClass = _super; |
| } |
| else if (className.equals(JSSharedData.JS_FRAMEWORK_NAME) || |
| _super.indexOf("browser.") == 0 || |
| _super.indexOf("com.jquery.") == 0) |
| { |
| superClass = "Object"; |
| } |
| else |
| { |
| superClass = createFullName("", _super); |
| } |
| |
| return superClass; |
| } |
| |
| protected Boolean isExtern(InstanceInfo ii) |
| { |
| if (JSGeneratingReducer.getBasenameFromName(ii.name).equals(JSSharedData.EXTERN_INTERFACE_NAME)) |
| return true; |
| |
| for (Name iname : ii.interfaceNames) |
| { |
| if (JSGeneratingReducer.getBasenameFromName(iname).equals(JSSharedData.EXTERN_INTERFACE_NAME)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * private void emitPackageNames( String fullName ) { String packageName = |
| * ""; String[] parts = fullName.split("\\."); for( String part: parts ) { |
| * if( !packageName.isEmpty() ) packageName += "."; packageName += part; if( |
| * !m_sharedData.hasPackage(packageName) && |
| * packageName.equals(JSSharedData.JS_FRAMEWORK_NAME) ) { |
| * m_sharedData.registerPackage(packageName); |
| * writeString("/ * * @typedef { Object } * /\n"); if( |
| * !packageName.contains(".") ) writeString( "var " ); writeString( |
| * packageName + " = {};\n\n" ); } } } |
| */ |
| |
| protected void emitInterface(EmitterClassVisitor clz) throws Exception |
| { |
| |
| // only set emitExterns to true if you want to generate extern files, i.e. svg.js. |
| // if( !emitExterns ) |
| // return; |
| |
| InstanceInfo ii = clz.instanceInfo; |
| final String className = JSGeneratingReducer.getBasenameFromName(ii.name); |
| if (className.equals(JSSharedData.EXTERN_INTERFACE_NAME)) |
| return; |
| |
| final Boolean isExtern = isExtern(ii); |
| final Boolean isInterface = ii.isInterface(); |
| final IDefinition def = getDefinition(ii.name); |
| final String packageName = def.getPackageName(); |
| |
| // register class with super class |
| final Name superClassName = ii.interfaceNames.length == 0 ? ii.superName : ii.interfaceNames[0]; |
| IDefinition superClassDef = null; |
| if (superClassName != null) |
| { |
| superClassDef = getDefinition(superClassName); |
| JSSharedData.instance.registerClass(ii.name, superClassName); |
| } |
| |
| final String superClass = superClassDef == null || superClassDef instanceof AmbiguousDefinition ? "Object" : superClassDef.getQualifiedName(); |
| |
| /* |
| * final Name name = ii.interfaceNames[0]; nsset.add(ns); Multiname |
| * mname = Multiname.create(name.getQualifiers()., name.getBaseName() ); |
| * IDefinition def = null; // final ASDefinitionFilter filter = new |
| * ASDefinitionFilter(ClassificationValue.CLASSES_AND_INTERFACES, |
| * SearchScopeValue.ALL_SCOPES, AccessValue.ALL, def); def = |
| * m_project.getScope().findDefinitionByName(ii.interfaceNames[0], |
| * filter, new ASDefinitionCache(m_project) ); // Name name = |
| * ii.interfaceNames[0]; String name = |
| * JSGeneratingReducer.getFullNameFromName( ii.interfaceNames[0], false |
| * ); // def = |
| * m_project.getScope().findDefinitionByName(ii.interfaceNames[0],true); |
| * if( def != null ) superClass = |
| * JSGeneratingReducer.definitionToString(def); } |
| */ |
| |
| if (isExtern && emitExterns) |
| { |
| // IFilter out only the SVG interfaces. |
| if (!packageName.startsWith("org.w3c.dom.svg")) |
| return; |
| |
| // For now we are only interested in classes that are marked as IExtern |
| |
| String labelStr = "Extern"; |
| |
| // register class with super class |
| |
| writeString(EXTERN_PREFIX + "\n"); |
| writeString(EXTERN_PREFIX + "/**\n"); |
| writeString(EXTERN_PREFIX + " * " + JSGeneratingReducer.getTimeStampString()); |
| writeString(EXTERN_PREFIX + " *\n"); |
| writeString(EXTERN_PREFIX + " * " + labelStr + ": " + def.getQualifiedName() + "\n"); |
| writeString(EXTERN_PREFIX + " *\n"); |
| writeString(EXTERN_PREFIX + " * @typedef {" + className + "}\n"); |
| writeString(EXTERN_PREFIX + " */\n"); |
| writeString(EXTERN_PREFIX + "var " + className + ";\n\n"); |
| |
| final Boolean isPackageFunction = ii.name == null; |
| emitInstanceTraits(ii.traits, null, packageName, className, superClass, isExtern, isInterface, isPackageFunction); |
| |
| return; |
| } |
| |
| final String fullName = def.getQualifiedName(); |
| |
| // JSDoc |
| writeString("\n"); |
| writeString("/**\n"); |
| writeString(" * " + JSGeneratingReducer.getTimeStampString()); |
| writeString(" *\n"); |
| writeString(" * Interface: " + fullName + "\n"); |
| writeString(" *\n"); |
| writeString(" */\n"); |
| |
| // provide() |
| JSSharedData.instance.registerPackage(fullName); |
| // emitPackageNames( fullName ); |
| // writeString( "goog.provide(\"" + fullName + "\");\n\n" ); |
| |
| if (isExtern) |
| { |
| // TODO: |
| // writeString("/** @typedef {" + className + "} */\n"); |
| // writeString( fullName + " = typeof(" + className + ") == \"undefined\" ? function() {} : " + className + ";\n" ); |
| } |
| else |
| { |
| writeString("/** @typedef {" + fullName + "} */\n"); |
| writeString(fullName + " = function() {};\n"); |
| |
| final Boolean isDynamicClass = def != null && def.isDynamic(); |
| final Boolean isFinalClass = def != null && def.isFinal(); |
| emitClassInfo(packageName, className, superClass, isDynamicClass, isFinalClass); |
| } |
| |
| // finish with an extra line feed |
| writeString("\n"); |
| } |
| |
| /* |
| * protected IDefinition getDefinition(String packageName, String |
| * classOrInterfaceName) { IDefinition def = null; if( m_project != null ) { |
| * IASScope scope = m_project.getScope(); ASDefinitionFilter filter = new |
| * ASDefinitionFilter( ClassificationValue.MEMBERS_AND_TYPES, |
| * SearchScopeValue.ALL_SCOPES, AccessValue.ALL, scope.getDefinition()); |
| * ASDefinitionCache cache = new ASDefinitionCache(m_project); if( |
| * packageName.isEmpty() ) def = |
| * scope.findDefinitionByName(classOrInterfaceName, filter, cache); else def |
| * = scope.findDefinitionByName(packageName + "." + classOrInterfaceName, |
| * filter, cache); } return def; } |
| */ |
| protected void emitClass(EmitterClassVisitor clz) throws Exception |
| { |
| InstanceInfo ii = clz.instanceInfo; |
| |
| // Skipping classes that are "marked" as IExtern. |
| final Boolean isExtern = isExtern(ii); |
| if (isExtern) |
| return; |
| |
| final Boolean isInterface = ii.isInterface(); |
| final Boolean isPackageFunction = ii.name == null; |
| |
| String packageName; |
| String className; |
| if (ii.name != null) |
| { |
| final IDefinition def = getDefinition(ii.name); |
| packageName = def.getPackageName(); |
| className = JSGeneratingReducer.getBasenameFromName(ii.name); |
| } |
| else |
| { |
| packageName = m_packageName; |
| className = ""; |
| } |
| |
| ClassDefinition classDef = null; |
| |
| // set m_currentScope to classScope |
| if (m_project != null) |
| { |
| m_currentScope = null; |
| IASScope scope = m_project.getScope(); |
| IASScope classScope = null; |
| ASDefinitionFilter filter = new ASDefinitionFilter(ClassificationValue.MEMBERS_AND_TYPES, |
| SearchScopeValue.ALL_SCOPES, |
| AccessValue.ALL, |
| scope.getDefinition()); |
| IDefinition def; |
| if (packageName.isEmpty()) |
| def = ASScopeUtils.findDefinitionByName(scope, m_project, className, filter); |
| else |
| def = ASScopeUtils.findDefinitionByName(scope, m_project, packageName + "." + className, filter); |
| |
| if (def != null && def instanceof ClassDefinition) |
| { |
| classDef = (ClassDefinition)def; |
| classScope = classDef.getContainedScope(); |
| } |
| |
| /* |
| * for( int i = 0; i < m_scopes.length && classScope == null; ++i ) |
| * { IASScope scope = m_scopes[i]; ASDefinitionFilter filter = new |
| * ASDefinitionFilter( ClassificationValue.MEMBERS_AND_TYPES, |
| * SearchScopeValue.ALL_SCOPES, AccessValue.ALL, |
| * scope.getDefinition()); ASDefinitionCache cache = new |
| * ASDefinitionCache(m_project); IDefinition def = |
| * scope.findDefinitionByName(className, filter, cache); if( def != |
| * null && def instanceof ClassDefinition ) { final ClassDefinition |
| * classDef = (ClassDefinition)def; classScope = |
| * classDef.getContainedScope(); } } |
| */ |
| m_currentScope = classScope; |
| } |
| |
| final JSSharedData sharedData = JSSharedData.instance; |
| String provideName = null; |
| |
| // handle package functions, i.e. goog.events.listen. |
| if (ii.iInit == null) |
| { |
| if (ii.superName == null && |
| ii.traits.getTraitCount() == 0 && |
| clz.classInfo.cInit == null && |
| clz.classTraits.getTraitCount() == 1) |
| { |
| final Trait t = clz.classTraits.iterator().next(); |
| |
| if (t.getKind() == TRAIT_Method || |
| t.getKind() == TRAIT_Function || |
| t.getKind() == TRAIT_Getter || |
| t.getKind() == TRAIT_Setter) |
| { |
| if (t.getBooleanAttr(Trait.TRAIT_FINAL)) |
| { |
| final Name name = t.getName(); |
| /* |
| * provideName = |
| * JSGeneratingReducer.getPackageNameFromName |
| * (name,false) + "." |
| * +JSGeneratingReducer.getBasenameFromName(name); if( |
| * !JSSharedData.ROOT_NAME.isEmpty() && |
| * !provideName.startsWith(JSSharedData.ROOT_NAME)) |
| * provideName = JSSharedData.ROOT_NAME + provideName; |
| */ |
| |
| final IDefinition def = getDefinition(name); |
| provideName = def.getQualifiedName(); |
| } |
| } |
| } |
| else |
| { |
| return; |
| } |
| } |
| |
| if (provideName == null) |
| { |
| final Boolean emitClassPrologue = !sharedData.hasClassBeenEmitted(ii.name); |
| /* |
| * This is WRONG: if( emitClassPrologue && m_buildPhase == |
| * Operation.GET_ABC_BYTES ) because classes don't get registered if |
| * needsSecondPass is false. JSCompilationUnit returns the generated |
| * JS from the first pass if needsSecondPass is false and |
| * registerEmittedClass() will never be called for this class. |
| */ |
| if (emitClassPrologue) |
| { |
| // Register regardless of phase, since ABC phase might not happen (if JSGeneratingReducer.needsSecondPass == false) |
| // And registerEmittedClass() properly handles duplicate registrations, if we do happen to do both passes. |
| sharedData.registerEmittedClass(ii.name); |
| } |
| |
| provideName = createFullName(packageName, className); |
| } |
| |
| // register class with super class |
| final IDefinition superClassDef = getDefinition(ii.superName); |
| final String superClassName = superClassDef == null ? "Object" : superClassDef.getQualifiedName(); |
| sharedData.registerClass(ii.name, ii.superName); |
| |
| // TODO: Special case: className.equals( "Package" ) |
| |
| if (className.isEmpty()); |
| else if (classDef != null) |
| { |
| String classQName = classDef.getQualifiedName(); |
| writeString("goog.provide('" + classQName + "');\n\n"); |
| FlexJSProject project = (FlexJSProject)m_project; |
| ArrayList<String> deps = project.getRequires(m_generator.m_compilationUnit); |
| emitRequires(deps, classQName); |
| } |
| else |
| writeString("goog.provide('" + className + "');\n"); |
| |
| final MethodInfo ctor = clz.instanceInfo.name == null ? null : clz.instanceInfo.iInit; |
| // first check whether there is any code... |
| final String body = getCodeForConstructor(ctor); |
| |
| writeString("/**\n"); |
| if (!JSGeneratingReducer.isDataType(superClassName) || !body.isEmpty()) |
| { |
| writeString(" * @constructor\n"); |
| writeString(" * @extends {" + superClassName + "}\n"); |
| emitJSDocForParams(ctor, ""); |
| } |
| |
| writeString(" */\n"); |
| |
| if (ii.hasProtectedNs()) |
| writeString("// protected: " + ii.protectedNs + "\n"); |
| |
| for (Name i : ii.interfaceNames) |
| writeString("// interface: " + i.toString() + "\n"); |
| |
| // every class extends another class or Object except for the framework class. |
| if (!className.equals(JSSharedData.JS_FRAMEWORK_NAME)) |
| { |
| if (JSSharedData.m_useClosureLib) |
| { |
| /* |
| * TODO: redundant? if( provideName != null && |
| * !className.isEmpty() ) { emitPackageNames( provideName ); |
| * writeString( "goog.provide(\"" + provideName + "\");\n" ); } |
| */ |
| |
| emitInstanceTraits(clz.instanceTraits, ctor, packageName, className, superClassName, isExtern, isInterface, isPackageFunction); |
| } |
| else |
| { |
| // provide() |
| /* |
| * TODO: redundant? if( provideName != null ) { |
| * emitPackageNames( provideName ); writeString( |
| * JSSharedData.JS_FRAMEWORK_NAME + ".provide(\"" + provideName |
| * + "\");\n" ); } |
| */ |
| |
| final String fullName = createFullName(packageName, className); |
| writeString(fullName + " = function("); |
| String a_priori_insns = emitParameters(ctor); |
| writeString(") {\n"); |
| writeString(" " + superClassName + ".call(this);\n\n"); |
| if (!a_priori_insns.isEmpty()) |
| { |
| writeString(a_priori_insns); |
| } |
| emitInstanceTraits(clz.instanceTraits, ctor, packageName, className, superClassName, isExtern, isInterface, isPackageFunction); |
| } |
| } |
| |
| final Boolean isDynamicClass = classDef != null && classDef.isDynamic(); |
| final Boolean isFinalClass = classDef != null && classDef.isFinal(); |
| emitClassTraits(clz.classTraits, clz.classInfo.cInit, |
| packageName, className, superClassName, |
| isExtern, isPackageFunction, isDynamicClass, isFinalClass, |
| provideName != null); |
| |
| // constructor, ii.iInit is a MethodInfo |
| // w.writeU30(getMethodId(ii.iInit)); |
| |
| // finish with an extra line feed |
| writeString("\n"); |
| } |
| |
| protected void emitRequires(ArrayList<String> deps, String classQName) |
| { |
| HashMap<String, String> already = new HashMap<String, String>(); |
| for (String imp : deps) |
| { |
| if (imp.indexOf("__AS3__") != -1) |
| continue; |
| if (imp.equals(classQName)) |
| continue; |
| if (imp.equals("Array")) |
| continue; |
| if (imp.equals("Boolean")) |
| continue; |
| if (imp.equals("decodeURI")) |
| continue; |
| if (imp.equals("decodeURIComponent")) |
| continue; |
| if (imp.equals("encodeURI")) |
| continue; |
| if (imp.equals("encodeURIComponent")) |
| continue; |
| if (imp.equals("Error")) |
| continue; |
| if (imp.equals("Function")) |
| continue; |
| if (imp.equals("JSON")) |
| continue; |
| if (imp.equals("Number")) |
| continue; |
| if (imp.equals("int")) |
| continue; |
| if (imp.equals("Object")) |
| continue; |
| if (imp.equals("RegExp")) |
| continue; |
| if (imp.equals("String")) |
| continue; |
| if (imp.equals("uint")) |
| continue; |
| if (!already.containsKey(imp)) |
| writeString("goog.require('" + imp + "');\n"); |
| already.put(imp, imp); |
| } |
| writeString("\n"); |
| } |
| |
| private Boolean hasTrait(Traits traits, String baseName, int kind) |
| { |
| for (Trait t : traits) |
| { |
| if (t.getKind() == kind) |
| { |
| Name name = t.getNameAttr("name"); |
| if (JSGeneratingReducer.getBasenameFromName(name).equals(baseName)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public Boolean hasSetter(IDefinition def, String baseName) |
| { |
| for (EmitterClassVisitor clz : definedClasses) |
| { |
| if (hasTrait(clz.instanceInfo.traits, baseName, TRAIT_Setter)) |
| return true; |
| if (hasTrait(clz.classInfo.classTraits, baseName, TRAIT_Setter)) |
| return true; |
| } |
| return false; |
| } |
| |
| public String getCurrentClassName() |
| { |
| if (m_currentClassVisitor != null) |
| { |
| return JSGeneratingReducer.getBasenameFromName(m_currentClassVisitor.instanceInfo.name); |
| } |
| return null; |
| } |
| |
| public String getCurrentSuperClassName() |
| { |
| if (m_currentClassVisitor != null) |
| { |
| return JSGeneratingReducer.getBasenameFromName(m_currentClassVisitor.instanceInfo.superName); |
| } |
| return null; |
| } |
| |
| public String getCurrentSuperClassFullName() |
| { |
| if (m_currentClassVisitor != null) |
| { |
| final IDefinition def = getDefinition(m_currentClassVisitor.instanceInfo.superName); |
| return def.getQualifiedName(); |
| } |
| return null; |
| } |
| |
| public String getCurrentPackageName() |
| { |
| if (m_currentClassVisitor != null) |
| { |
| final IDefinition def = getDefinition(m_currentClassVisitor.instanceInfo.name); |
| if (def != null) |
| return def.getPackageName(); |
| return m_packageName; |
| } |
| return null; |
| } |
| |
| public IClassVisitor getCurrentClassVisitor() |
| { |
| return m_currentClassVisitor; |
| } |
| |
| public void visitImport(String importName, ImportKind importKind) |
| { |
| // we can skip implicit nodes. |
| if (importKind != ImportKind.IMPLICIT_IMPORT) |
| { |
| m_importNames.add(importName); |
| } |
| } |
| |
| public void visitUseNamespace(String namespaceName) |
| { |
| m_useNames.add(namespaceName); |
| } |
| |
| public void visitFunctionDefinition(MethodInfo mi, FunctionDefinition funcDef) |
| { |
| if (!m_methodInfoToDefinition.containsKey(mi)) |
| { |
| m_methodInfoToDefinition.put(mi, funcDef); |
| m_definitionToMethodInfo.put(funcDef, mi); |
| } |
| } |
| |
| public IClassVisitor visitPackage(String packageName) |
| { |
| if (packageName == null) |
| packageName = ""; |
| |
| m_packageName = packageName; |
| |
| InstanceInfo iinfo = new InstanceInfo(); |
| // iinfo.name = JSGeneratingReducer.makeName(packageName); |
| final ClassInfo ci = new ClassInfo(); |
| // ci.cInit = mi; |
| IClassVisitor cv = visitClass(iinfo, ci); |
| return cv; |
| } |
| } |