| /* |
| * |
| * 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 macromedia.abc; |
| import static macromedia.asc.parser.Tokens.EMPTY_TOKEN; |
| import static macromedia.asc.semantics.Slot.*; |
| |
| import macromedia.asc.embedding.avmplus.*; |
| import macromedia.asc.util.*; |
| import macromedia.asc.parser.*; |
| import macromedia.asc.semantics.*; |
| import macromedia.asc.semantics.QName; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.util.Map; |
| import java.util.HashMap; |
| import java.util.Set; |
| |
| /** |
| * @author Erik Tierney |
| */ |
| @SuppressWarnings("nls") // TODO: Remove |
| public final class AbcParser |
| { |
| private Context ctx; |
| private AbcData abcData; |
| private static final boolean debug = false; |
| |
| public AbcParser(Context cx, String name) throws IOException |
| { |
| this.ctx = cx; |
| this.abcData = new AbcData(""); |
| this.abcData.readAbc(new BytecodeBuffer(name)); |
| } |
| |
| public AbcParser(Context cx, byte[] bytes) |
| { |
| this.ctx = cx; |
| this.abcData = new AbcData(""); |
| this.abcData.readAbc(new BytecodeBuffer(bytes)); |
| } |
| |
| private final Map<String, Integer> fun_names = new HashMap<String, Integer>(); |
| |
| private final ObjectList<ObjectList<ClassDefinitionNode>> clsdefs_sets = new ObjectList<ObjectList<ClassDefinitionNode>>(); |
| private final ObjectList<String> region_name_stack = new ObjectList<String>(); |
| |
| // TODO: better dependency analysis - this is used to populate FA's ce_unresolved_sets which |
| // TODO: mxml uses to find dependencies - could be done earlier for abcs |
| private ObjectList<Set<ReferenceValue>> ce_unresolved_sets = new ObjectList<Set<ReferenceValue>>(); |
| |
| public ProgramNode parseAbc() |
| { |
| return parseAbc(false, false); |
| } |
| |
| // first parameter causes BinaryProgramNode's toplevelDefinitions instance variable to be populated |
| // second parameter turns on/off building full ast's - if you suspect an mxmlc problem caused by a lack of |
| // ast from ABCs, turn this on to see if it fixes it. |
| // if this is false, only BinaryProgramNode, and toplevel definition nodes are created (mostly BinaryClassDefNode). |
| // hopefully we can get rid of those someday too. |
| // both these parameters are set to true by Flash Authoring and left false by by mxmlc |
| public ProgramNode parseAbc(boolean collectTopLevel, boolean buildASTForClasses) |
| { |
| try |
| { |
| // Create a program node that will represent the data in the abc file. |
| // It won't actually be a parse tree, but it will contain the correct builder |
| // objects which are usually built up by the FlowAnalyzer |
| |
| BinaryProgramNode program = ctx.getNodeFactory().binaryProgram(ctx, ctx.getNodeFactory().statementList(null, (StatementListNode)null)); |
| GlobalBuilder b = new GlobalBuilder(); |
| b.is_in_package = true; // cn: necessary for proper slot creation for top level functions |
| program.frame = new ObjectValue(ctx, b, ctx.noType()); |
| ctx.pushScope(program.frame); |
| clsdefs_sets.add(new ObjectList<ClassDefinitionNode>()); |
| ce_unresolved_sets.push_back(program.ce_unresolved); |
| region_name_stack.push_back(""); |
| |
| for( int i = 0; i < this.abcData.getScriptInfoSize(); ++i ) |
| { |
| parseScript(i, program, collectTopLevel, buildASTForClasses); |
| } |
| |
| program.clsdefs = clsdefs_sets.last(); |
| clsdefs_sets.removeLast(); |
| ce_unresolved_sets.removeLast(); |
| region_name_stack.pop_back(); |
| ctx.popScope(); // global |
| |
| // For proper const and type enforcement of top level functions and vars, we need to |
| // process those def nodes during CE. These definitions need to be evaluated in the |
| // scope of the BinaryProgramNode's frame. |
| // Conversely, class definition nodes are maintained in the clsdefs list rather than ever |
| // being added to the binary program node's statement list. This is so that they get processed outside the |
| // BinaryProgramNode's frame... |
| |
| // {pmd} following builds <ProgramNode <StatementList <BinaryProgramNode ...>>> |
| // {pmd} I wondered why? --probably to match some undefined visitor pattern behavior... |
| |
| ProgramNode prog = ctx.getNodeFactory().program(ctx, ctx.getNodeFactory().statementList(null, program)); |
| |
| // Uncomment next line to get some debugging info to print to stdout. |
| // {pmd} ??? for this to be useful it really should iterate the clsdefs list...(and print the objectvalue hierarchy too.) |
| // dumpProgram(program); |
| |
| return prog; |
| } |
| catch(Exception t) |
| { |
| if(debug) t.printStackTrace(); |
| // Clean up any scope we may have pushed and didn't pop when the exception was thrown |
| ObjectValue scope = ctx.scope(); |
| ObjectValue global = ctx.globalScope(); |
| while( scope != global ) |
| { |
| ctx.popScope(); |
| scope = ctx.scope(); |
| } |
| return null; |
| } |
| } |
| |
| static private class DefAndSlot |
| { |
| public DefinitionNode def; |
| public Slot slot; |
| } |
| |
| /** |
| * Returns a TypeValue for the qname specified by typeID. If that type does not exist yet |
| * a new placeholder TypeValue is created and returned. The placeholder will be filled in later when |
| * we actually see that type. This allows us to avoid any forward reference problems when parsing an ABC |
| * @param typeID the index of the QName we want the TypeValue for |
| * @return the TypeValue representing the type specifed by typeID. |
| */ |
| private TypeValue getTypeFromQName(int typeID) |
| { |
| if( typeID == 0 ) |
| return ctx.noType(); |
| |
| QName typename = getFullName(getBinaryMNFromCPool(typeID)); |
| |
| if( ctx.isBuiltin(typename.toString()) ) |
| return ctx.builtin( typename.toString() ); |
| |
| TypeValue type = TypeValue.getTypeValue(ctx, typename); |
| if( !type.resolved ) |
| ce_unresolved_sets.last().add(new ReferenceValue(ctx, null, typename.name, typename.ns)); |
| |
| return TypeValue.getTypeValue(ctx, typename); |
| |
| } |
| |
| private DefAndSlot slotTrait(String name, ObjectValue ns, int slotID, int typeID, int valueID, int value_kind, boolean is_const, boolean build_ast) |
| { |
| ObjectValue obj = ctx.scope(); |
| DefAndSlot ret = new DefAndSlot(); |
| Namespaces nss = new Namespaces(); |
| nss.push_back(ns); |
| |
| TypeValue type = getTypeFromQName(typeID); |
| |
| int var_id = obj.builder.Variable(ctx, obj); |
| int slot_id = obj.builder.ExplicitVar(ctx,obj,name,nss,type,-1,-1,var_id); |
| |
| Slot slot = obj.getSlot(ctx,slot_id); |
| slot.setConst(is_const); |
| slot.setImported(true); |
| |
| ret.slot = slot; |
| |
| IdentifierNode id=null; |
| AttributeListNode attr=null; |
| |
| if( build_ast ) |
| { |
| id = identifierNode(name, ns); |
| attr = attributeList(false, false, false, ns, obj.builder); |
| } |
| if( value_kind == ActionBlockConstants.CONSTANT_Namespace ) |
| { |
| ObjectValue nsValue = getNamespace(valueID); |
| slot.setObjectValue(nsValue); |
| slot.setConst(true); |
| |
| if( build_ast ) |
| ret.def = ctx.getNodeFactory().namespaceDefinition(attr, id, ctx.getNodeFactory().literalString(nsValue.name)); |
| |
| return ret; |
| } |
| if( valueID != 0) |
| slot.setObjectValue(getInitValue(valueID, value_kind)); |
| |
| if( build_ast ) |
| { |
| MemberExpressionNode typeExpr = null; |
| if( typeID != 0 ) |
| { |
| AbcData.BinaryMN t = getBinaryMNFromCPool(typeID); |
| typeExpr = memberExprFromMN(t); |
| } |
| |
| Node init = getInitValueNode(valueID,value_kind); // (0,0) means undefined |
| TypedIdentifierNode ty = ctx.getNodeFactory().typedIdentifier(id, typeExpr); |
| int tok = is_const ? Tokens.CONST_TOKEN : Tokens.VAR_TOKEN; |
| VariableBindingNode bind = ctx.getNodeFactory().variableBinding(attr, tok, ty, init); |
| |
| bind.ref = id.ref; |
| if( typeExpr != null ) |
| { |
| bind.typeref = typeExpr.ref; |
| } |
| |
| ret.def = (DefinitionNode) ctx.getNodeFactory().variableDefinition(attr, tok, ctx.getNodeFactory().list(null, bind)); |
| } |
| return ret; |
| } |
| |
| private MemberExpressionNode memberExprFromMN(AbcData.BinaryMN mn) |
| { |
| MemberExpressionNode typeExpr = null; |
| QName typeName = getFullName(mn); |
| NodeFactory nf = ctx.getNodeFactory(); |
| |
| if( typeName instanceof ParameterizedName ) |
| { |
| ParameterizedName pn = (ParameterizedName)typeName; |
| IdentifierNode baseIdNode = identifierNode(pn.name, pn.ns); |
| MemberExpressionNode param_node = memberExprFromMN(getBinaryMNFromCPool(mn.params[0])); |
| ListNode list = nf.list(null, param_node); |
| Node apply = nf.applyTypeExpr(baseIdNode, list, -1); |
| typeExpr = nf.memberExpression(null, (SelectorNode)apply); |
| typeExpr.ref = baseIdNode.ref; |
| typeExpr.ref.addTypeParam(param_node.ref); |
| } |
| else |
| { |
| IdentifierNode typeIdNode = identifierNode(typeName.name, typeName.ns); |
| GetExpressionNode getNode = nf.getExpression(typeIdNode); |
| typeExpr = nf.memberExpression(null, getNode); |
| typeExpr.ref = typeIdNode.ref; |
| } |
| return typeExpr; |
| } |
| |
| /** |
| * Creates an identifier node, and fills in the ref with the correct reference value |
| * @param simpleName |
| * @param ns |
| * @return IdentifierNode |
| */ |
| private IdentifierNode identifierNode(String simpleName, ObjectValue ns) |
| { |
| IdentifierNode id = ctx.getNodeFactory().identifier(simpleName); |
| //IdentifierNode will clean this up |
| id.ref = new ReferenceValue(ctx, null, id.name, ns); |
| return id; |
| } |
| |
| private DefAndSlot methodTrait(String methodName, ObjectValue ns, int dispID, int methInfo, int attrs, int kind, boolean build_ast) |
| { |
| ObjectValue obj = ctx.scope(); |
| DefAndSlot ret = new DefAndSlot(); |
| boolean isFinal = (attrs & ActionBlockConstants.TRAIT_FLAG_final) != 0; |
| boolean isOverride = (attrs & ActionBlockConstants.TRAIT_FLAG_override) != 0; |
| Namespaces names = new Namespaces(ns); |
| int method_id = obj.builder.Method(ctx,obj,methodName,names,false); |
| int method_slot; |
| NodeFactory nf = ctx.getNodeFactory(); |
| |
| // Create the right type of method based on what kind it is. Either a setter, getter, or regular method |
| switch(kind) |
| { |
| case ActionBlockConstants.TRAIT_Setter: //Set property |
| method_slot = obj.builder.ExplicitSet(ctx,obj,methodName,names,ctx.noType(),isFinal,isOverride,-1,method_id,-1); |
| break; |
| |
| case ActionBlockConstants.TRAIT_Getter: //get property |
| method_slot = obj.builder.ExplicitGet(ctx,obj,methodName,names,ctx.noType(),isFinal,isOverride,-1,method_id,-1); |
| break; |
| |
| default: // regular method |
| method_slot = obj.builder.ExplicitCall(ctx,obj,methodName,names,ctx.noType(),isFinal,isOverride,-1,method_id,-1); |
| break; |
| } |
| |
| ObjectValue funcObj = getFunctionObject(); |
| Slot slot = obj.getSlot(ctx, method_slot); |
| slot.setValue(funcObj); |
| slot.setImported(true); |
| |
| // Calculate the internal name - this matter because with get/set properties you can end up with |
| // multiple methods with the same name, but they must each have different internal names. |
| StringBuilder internal_name = new StringBuilder(methodName.length() + 5); |
| if( !fun_names.containsKey(methodName) ) |
| { |
| fun_names.put(methodName,0); |
| } |
| internal_name.append(methodName).append('$'); |
| int num = fun_names.get(methodName); |
| internal_name.append(num); |
| num++; |
| fun_names.put(methodName, num); |
| |
| int slot_id = obj.getImplicitIndex(ctx,method_slot,Tokens.EMPTY_TOKEN); |
| Slot implied_slot = obj.getSlot(ctx, slot_id); |
| implied_slot.setImported(true); |
| |
| String n = internal_name.toString(); |
| implied_slot.setMethodName(n);//node->fexpr->internal_name; |
| ret.slot = implied_slot; |
| |
| // Make sure the FunctionBuilder gets cleaned up |
| int functionKind; |
| switch( kind ) |
| { |
| case ActionBlockConstants.TRAIT_Getter: |
| functionKind = Tokens.GET_TOKEN; |
| break; |
| case ActionBlockConstants.TRAIT_Setter: |
| functionKind = Tokens.SET_TOKEN; |
| break; |
| default: |
| functionKind = Tokens.EMPTY_TOKEN; |
| break; |
| } |
| |
| // Come up with the function signature |
| AbcData.Method m_info = this.abcData.getMethod(methInfo); |
| |
| int returnTypeID = m_info.getReturnType(); |
| int paramTypeIDs[] = m_info.getParamTypes(); |
| int paramCount = paramTypeIDs.length; |
| ObjectList<Node> optional_nodes = null; |
| int optional_count = 0; |
| if( m_info.getHasOptional() ) |
| { |
| if( build_ast ) |
| { |
| optional_nodes = parseOptionalParams(m_info); |
| } |
| optional_count = m_info.getOptionalParamTypes().length; |
| } |
| String[] param_names = m_info.getParamNames(); |
| |
| // Set return type |
| implied_slot.setType(getTypeFromQName(returnTypeID).getDefaultTypeInfo()); |
| |
| ParameterListNode paramList = null; |
| |
| ObjectList<TypeInfo> param_types = new ObjectList<TypeInfo>(paramCount); |
| ByteList decl_styles = new ByteList(1); |
| |
| for( int i = 0, cur_optional = 0; i < paramCount; ++i ) |
| { |
| ParameterNode param = null; |
| if( build_ast ) |
| { |
| AbcData.BinaryMN typeMN = null; |
| if( paramTypeIDs[i] != 0 ) |
| { |
| typeMN = getBinaryMNFromCPool(paramTypeIDs[i]); |
| // getFullName(typeMN); // for effect |
| } |
| |
| String simple_param_name = i < param_names.length && param_names[i] != null ? param_names[i] : ("param" + (i+1)).intern(); |
| param = parameterNode(simple_param_name, typeMN); |
| |
| paramList = nf.parameterList(paramList, param); |
| } |
| |
| param_types.push_back(getTypeFromQName(paramTypeIDs[i]).getDefaultTypeInfo()); |
| |
| if( i >= paramCount - optional_count ) |
| { |
| if( build_ast ) |
| param.init = optional_nodes.get(cur_optional++); |
| decl_styles.push_back((byte) Slot.PARAM_Optional); |
| } |
| else |
| { |
| decl_styles.push_back((byte) Slot.PARAM_Required); |
| } |
| } |
| |
| if( m_info.getNeedsRest() ) |
| { |
| if( build_ast ) |
| { |
| ParameterNode param = parameterNode("rest", ctx.arrayType().name); |
| RestParameterNode restNode = ctx.getNodeFactory().restParameter(param , -1); |
| restNode.ref = param.ref; |
| restNode.typeref = param.typeref; |
| paramList = nf.parameterList(paramList, restNode); |
| |
| // rsun 11.22.05 porting over a fix made to the 8ball_AS3 branch a long time |
| // ago, but only to the C++ compiler. has_rest needs to be reset here since |
| // MovieClipMaker is a special entry point that creates function nodes itself. |
| // otherwise, has_rest is unnecessarily true for all functions processed after |
| // this. |
| ctx.getNodeFactory().has_rest = false; |
| } |
| |
| param_types.push_back(ctx.arrayType().getDefaultTypeInfo()); |
| decl_styles.push_back((byte) Slot.PARAM_Rest); |
| } |
| |
| if( param_types.size() > 0 ) |
| { |
| implied_slot.setTypes(param_types); |
| implied_slot.setDeclStyles(decl_styles); |
| } |
| else |
| { |
| param_types.push_back(ctx.voidType().getDefaultTypeInfo()); |
| implied_slot.setTypes(param_types); |
| implied_slot.addDeclStyle(Slot.PARAM_Void); |
| } |
| |
| if( build_ast ) |
| { |
| MemberExpressionNode retTypeNode = null; |
| if( returnTypeID != 0 ) |
| { |
| AbcData.BinaryMN retMN = getBinaryMNFromCPool(returnTypeID); |
| retTypeNode = memberExprFromMN(retMN); |
| } |
| FunctionSignatureNode fsn = nf.functionSignature(paramList,retTypeNode); |
| if( retTypeNode != null ) |
| { |
| fsn.typeref = retTypeNode.ref; |
| } |
| |
| IdentifierNode id = identifierNode(methodName,ns); |
| FunctionCommonNode fcn = nf.functionCommon(ctx, id, fsn, null); |
| fcn.kind = functionKind; |
| fcn.ref = id.ref; |
| |
| FunctionNameNode fn = nf.functionName(functionKind, id); |
| AttributeListNode attr = attributeList(isFinal, isOverride, false, ns, obj.builder); |
| FunctionDefinitionNode fdn = nf.binaryFunctionDefinition(ctx, attr, fn, fcn, -1); |
| |
| ret.def = fdn; |
| } |
| return ret; |
| } |
| |
| private static ObjectValue dummyFunc = null; |
| |
| /** |
| * Helper so we only allocate 1 ObjectValue to represent a method, instead |
| * of 1 for each method. This is safe because the OV is only used as the value |
| * of a MethodSlot, and we only ever check for the existance of the value, never anything else |
| * @return an ObjectValue that can be used for a function object |
| */ |
| private ObjectValue getFunctionObject() |
| { |
| if( dummyFunc == null ) |
| dummyFunc = new ObjectValue(ctx,new FunctionBuilder(),ctx.functionType()); |
| |
| return dummyFunc; |
| } |
| |
| private Node getInitValueNode(int valueID,int kind) |
| { |
| Node t; |
| NodeFactory nf = ctx.getNodeFactory(); |
| |
| switch (kind) |
| { |
| case ActionBlockConstants.CONSTANT_Void: |
| t = nf.unaryExpression(Tokens.VOID_TOKEN,nf.literalNumber("0",-1),-1); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Integer: |
| case ActionBlockConstants.CONSTANT_UInteger: |
| case ActionBlockConstants.CONSTANT_Double: |
| double val = getNumberFromCPool(valueID, kind); |
| t = nf.literalNumber(String.valueOf(val)); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Decimal: |
| Decimal128 dval = getDecimalFromCPool(valueID); |
| String sval = dval.toString(); |
| if (ctx.statics.es4_numerics) |
| sval += "m"; |
| t = nf.literalNumber(sval); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_True: |
| t = nf.literalBoolean(true); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_False: |
| t = nf.literalBoolean(false); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Utf8: |
| t = nf.literalString(getStringFromCPool(valueID)); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Null: |
| t = nf.literalNull(); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Namespace: |
| ObjectValue ns = getNamespace(valueID); |
| t = nf.literalString(ns.name); |
| break; |
| |
| default: |
| t = nf.literalString(""); |
| break; |
| } |
| return t; |
| } |
| |
| private ObjectValue getInitValue(int valueID,int kind) |
| { |
| ObjectValue ov; |
| switch (kind) |
| { |
| case ActionBlockConstants.CONSTANT_Void: |
| ov = ctx.voidType().prototype; |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Integer: |
| double intval = getNumberFromCPool(valueID, kind); |
| ov = new ObjectValue(String.valueOf(intval), ctx.intType()); |
| break; |
| case ActionBlockConstants.CONSTANT_UInteger: |
| double uintval = getNumberFromCPool(valueID, kind); |
| ov = new ObjectValue(String.valueOf(uintval), ctx.uintType()); |
| break; |
| case ActionBlockConstants.CONSTANT_Double: |
| double val = getNumberFromCPool(valueID, kind); |
| ov = new ObjectValue(String.valueOf(val), ctx.doubleType()); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Decimal: |
| Decimal128 dval = getDecimalFromCPool(valueID); |
| String sval = dval.toString(); |
| if (ctx.statics.es4_numerics) |
| sval += "m"; |
| ov = new ObjectValue(sval, ctx.decimalType()); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_True: |
| ov = ctx.booleanTrue(); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_False: |
| ov = ctx.booleanFalse(); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Utf8: |
| ov = new ObjectValue(getStringFromCPool(valueID), ctx.stringType()); |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Null: |
| ov = ctx.nullType().prototype; |
| break; |
| |
| case ActionBlockConstants.CONSTANT_Namespace: |
| ObjectValue ns = getNamespace(valueID); |
| ov = ns; |
| break; |
| |
| default: |
| ov = null; |
| break; |
| } |
| return ov; |
| } |
| |
| private ObjectList<Node> parseOptionalParams(AbcData.Method method_info) |
| { |
| int[] optional_values = method_info.getOptionalParamTypes(); |
| int[] optional_kinds = method_info.getOptionalParamKinds(); |
| |
| ObjectList<Node> optionals = new ObjectList<Node>(optional_values.length); |
| for( int i = 0; i < optional_values.length; ++i) |
| { |
| int value_index = optional_values[i]; |
| int value_kind = optional_kinds[i]; |
| Node current_node = getInitValueNode(value_index,value_kind); |
| optionals.push_back(current_node); |
| } |
| return optionals; |
| } |
| |
| |
| private ParameterNode parameterNode(String simpleParamName, QName typeName) |
| { |
| IdentifierNode id = identifierNode(simpleParamName, ctx.publicNamespace()); |
| IdentifierNode ty = null; |
| MemberExpressionNode paramType = null; |
| |
| if( typeName != null ) |
| { |
| ty = identifierNode(typeName.name, typeName.ns); |
| GetExpressionNode getNode = ctx.getNodeFactory().getExpression(ty); |
| paramType = ctx.getNodeFactory().memberExpression(null, getNode); |
| } |
| ParameterNode param = ctx.getNodeFactory().parameter(Tokens.VAR_TOKEN, id, paramType); |
| if( ty != null ) |
| { |
| param.typeref = ty.ref; |
| } |
| param.ref = id.ref; |
| |
| return param; |
| } |
| |
| private ParameterNode parameterNode(String simpleParamName, AbcData.BinaryMN typeMN) |
| { |
| IdentifierNode id = identifierNode(simpleParamName, ctx.publicNamespace()); |
| |
| MemberExpressionNode paramType = null; |
| if( typeMN != null ) |
| { |
| paramType = memberExprFromMN(typeMN); |
| } |
| ParameterNode param = ctx.getNodeFactory().parameter(Tokens.VAR_TOKEN, id, paramType); |
| if( paramType != null ) |
| { |
| param.typeref = paramType.ref; |
| } |
| param.ref = id.ref; |
| |
| return param; |
| } |
| |
| private AttributeListNode attributeList(boolean isFinal, boolean isOverride, boolean isDynamic, ObjectValue ns, Builder builder) |
| { |
| AttributeListNode attr = ctx.getNodeFactory().attributeList(ctx.getNodeFactory().literalString(""), null); |
| |
| attr.hasFinal = isFinal; |
| attr.hasOverride = isOverride; |
| attr.hasDynamic = isDynamic; |
| |
| if( builder instanceof ClassBuilder ) //Statics are built by classbuilder, non statics by instancebuilder |
| { |
| attr.hasStatic = true; |
| } |
| if ( ns == null ) |
| return attr; |
| |
| if( ns == ctx.publicNamespace() || (ns.getNamespaceKind() == Context.NS_PUBLIC && ns.isPackage()) ) |
| { |
| // Only package public namespaces will have been qualified with the 'public' access specifier |
| // user defined namespaces may also be public namespaces in the cpool, but they will not be package namespaces |
| attr.hasPublic = true; |
| } |
| else if( ns.isInternal() ) |
| attr.hasInternal = true; |
| else if( ns.isProtected() ) |
| attr.hasProtected = true; |
| else if( ns.isPrivate() ) |
| attr.hasPrivate = true; |
| else |
| { |
| if( builder.classname != null && ns == builder.classname.ns) |
| attr.hasPublic = true; |
| else |
| attr.namespaces.add(ns); |
| } |
| return attr; |
| } |
| |
| // Utility to get the full name based on a qname (ns + "/" + name) |
| private QName getFullName(AbcData.BinaryMN mn) |
| { |
| if( mn.kind == ActionBlockConstants.CONSTANT_TypeName ) |
| { |
| AbcData.BinaryMN base = getBinaryMNFromCPool(mn.baseMN); |
| QName base_qn = getFullName(base); |
| ObjectList<QName> params = new ObjectList<QName>(mn.params.length); |
| |
| for( int i = 0; i < mn.params.length; ++i) { |
| params.add(getFullName(getBinaryMNFromCPool(mn.params[i]))); |
| } |
| ParameterizedName pn = new ParameterizedName(params, base_qn.ns, base_qn.name); |
| return pn; |
| } |
| else |
| { |
| assert (mn.nsIsSet != true):"expected a single namespace"; |
| |
| String fullName = getStringFromCPool(mn.nameID); |
| ObjectValue ns = getNamespace(mn.nsID); |
| return ctx.computeQualifiedName("", fullName, ns, EMPTY_TOKEN); |
| } |
| } |
| |
| private DefAndSlot classTrait(String className, ObjectValue ns, int slotID, int classID, boolean build_ast) |
| { |
| if( classID >= this.abcData.getClassInfoSize() || classID < 0 ) |
| return null; |
| |
| DefAndSlot ret = new DefAndSlot(); |
| ObjectValue current_scope = ctx.scope(); |
| |
| String region_name = region_name_stack.back(); |
| if( region_name.length() > 0 ) |
| { |
| region_name += "/"; |
| ns = ctx.getNamespace(region_name + ns.name); |
| } |
| |
| NodeFactory nf = ctx.getNodeFactory(); |
| |
| // Instance info |
| AbcData.InstanceInfo iinfo = this.abcData.getInstanceInfo(classID); |
| |
| AbcData.BinaryMN instanceMN = getBinaryMNFromCPool(iinfo.getInstanceNameID()); |
| QName fullName = getFullName(instanceMN); |
| String fullNameString = fullName.toString(); |
| |
| int superID = iinfo.getSuperID(); |
| boolean hasSuper = superID != 0; |
| QName superName = null; |
| String simpleSuperName =""; |
| ObjectValue superNamespace = null; |
| if( hasSuper ) |
| { |
| AbcData.BinaryMN superMN = getBinaryMNFromCPool(superID); |
| |
| assert (superMN.nsIsSet != true):"expected a single namespace"; |
| |
| superNamespace = getNamespace(superMN.nsID); |
| superName = getFullName(superMN); |
| simpleSuperName = getStringFromCPool(superMN.nameID); |
| } |
| |
| int flags = iinfo.getFlags(); |
| |
| int[] interfaces = iinfo.getInterfaces(); |
| ListNode interface_nodes = null; |
| if(debug&&interfaces.length>0) System.out.println("parsing " + interfaces); |
| for( int i = 0; i < interfaces.length; ++i ) |
| { |
| int int_index = interfaces[i]; |
| AbcData.BinaryMN intMN = getBinaryMNFromCPool(int_index); |
| String simpleIntName = getStringFromCPool(intMN.nameID); |
| Namespaces intNamespaces; |
| if(intMN.nsIsSet) |
| intNamespaces = getNamespaces(intMN.nsID); |
| else { |
| intNamespaces = new Namespaces(1); |
| intNamespaces.add(getNamespace(intMN.nsID)); |
| } |
| |
| IdentifierNode ident = nf.identifier(simpleIntName); |
| |
| ident.ref = new ReferenceValue(ctx, null, simpleIntName, intNamespaces); |
| GetExpressionNode getNode = nf.getExpression(ident); |
| MemberExpressionNode interface_node = nf.memberExpression(null, getNode); |
| interface_node.ref = ident.ref; |
| interface_nodes = nf.list(interface_nodes, interface_node); |
| interface_nodes.values.push_back(ident.ref); |
| } |
| |
| boolean isFinal = (flags & ActionBlockConstants.CLASS_FLAG_final) != 0; |
| boolean isDynamic = ( flags & ActionBlockConstants.CLASS_FLAG_sealed ) == 0; |
| boolean isInterface = (flags & ActionBlockConstants.CLASS_FLAG_interface) != 0; |
| |
| ClassDefinitionNode cdn = null; |
| IdentifierNode idNode = nf.identifier(className); |
| idNode.ref = new ReferenceValue(ctx, null, idNode.name, ns); |
| |
| AttributeListNode attr = attributeList(isFinal, false, isDynamic, ns, current_scope.builder); |
| StatementListNode stmtList = nf.statementList(null, (StatementListNode)null); |
| |
| if (isInterface) |
| { |
| cdn = nf.binaryInterfaceDefinition(ctx, attr, idNode, null, stmtList); |
| } |
| else |
| { |
| cdn = nf.binaryClassDefinition(ctx, attr, idNode, null, stmtList); |
| } |
| cdn.ref = idNode.ref; |
| cdn.interfaces = interface_nodes; |
| cdn.public_namespace = ctx.publicNamespace(); |
| cdn.protected_namespace = ctx.getNamespace(fullNameString, Context.NS_PROTECTED); |
| cdn.static_protected_namespace = ctx.getNamespace(fullNameString, Context.NS_STATIC_PROTECTED); |
| cdn.private_namespace = ctx.getNamespace(fullNameString, Context.NS_PRIVATE); |
| cdn.default_namespace = ctx.getNamespace(fullName.ns.name, Context.NS_INTERNAL); |
| |
| boolean is_builtin = ctx.isBuiltin(fullNameString); |
| TypeValue cframe; |
| ObjectValue iframe; |
| if( is_builtin ) |
| { |
| cframe = ctx.builtin(fullNameString); |
| iframe = cframe.prototype; |
| } |
| else |
| { |
| ClassBuilder cb = new ClassBuilder(fullName,cdn.protected_namespace,cdn.static_protected_namespace); |
| cframe = TypeValue.defineTypeValue(ctx,cb,fullName,RuntimeConstants.TYPE_object); |
| |
| InstanceBuilder ib = new InstanceBuilder(fullName); |
| ib.canEarlyBind = false; |
| iframe = new ObjectValue(ctx,ib,cframe); |
| cframe.prototype = iframe; |
| |
| cdn.debug_name = fullNameString; |
| // Make sure the ClassBuilder and InstanceBuilders get cleaned up |
| cdn.owns_cframe = true; |
| } |
| cdn.cframe = cframe; |
| cdn.iframe = iframe; |
| |
| if( isInterface ) |
| { |
| ((ClassBuilder)cframe.builder).is_interface = true; |
| } |
| cframe.builder.is_dynamic = iframe.builder.is_dynamic = isDynamic; |
| cframe.builder.is_final = iframe.builder.is_final = isFinal; |
| |
| clsdefs_sets.last().add(cdn); |
| |
| if( hasSuper ) |
| { |
| TypeValue superType; |
| cdn.baseclass = nf.literalString(superName.toString(), -1); |
| if( ctx.isBuiltin(superName.toString()) ) |
| { |
| // Set the super type, this important for int/uint which derive from Number |
| superType = ctx.builtin(superName.toString()); |
| cframe.baseclass = superType; |
| cdn.baseref = new ReferenceValue(ctx, null, superName.toString(), ctx.publicNamespace()); |
| } |
| else |
| { |
| //cdn.baseclass = nf.literalString(superName.toString(), -1); |
| cdn.baseref = new ReferenceValue(ctx, null, simpleSuperName, superNamespace); |
| cdn.baseref.getSlot(ctx); |
| |
| cframe.baseclass = getTypeFromQName((int)superID) ; |
| } |
| } |
| else if (cdn.cframe != ctx.objectType()) |
| { |
| cdn.baseclass = nf.memberExpression(null, nf.getExpression(nf.identifier("Object"))); |
| cframe.baseclass = ctx.objectType(); |
| } |
| else |
| { |
| cdn.baseclass = null; |
| cframe.baseclass = null; |
| } |
| |
| int var_id = current_scope.builder.Variable(ctx,current_scope); |
| int slot_id = current_scope.builder.ExplicitVar(ctx,current_scope,className,ns,ctx.typeType(),-1,-1,var_id); |
| Slot slot = current_scope.getSlot(ctx,slot_id); |
| slot.setObjectValue(cframe); |
| slot.setImported(true); |
| slot.setConst(true); // all class definitions are const. |
| |
| ret.slot = slot; |
| |
| current_scope.builder.ImplicitCall(ctx,current_scope,slot_id,cframe,CALL_Method,-1,-1); |
| current_scope.builder.ImplicitConstruct(ctx,current_scope,slot_id,cframe,CALL_Method,-1,-1); |
| |
| if( isInterface ) |
| { |
| ((ClassBuilder)cframe.builder).is_interface = true; |
| slot.setImplNode(cdn); |
| } |
| |
| clsdefs_sets.add(new ObjectList<ClassDefinitionNode>()); |
| |
| region_name_stack.push_back(fullNameString); |
| |
| ctx.pushStaticClassScopes(cdn); // class |
| { |
| ctx.pushScope(iframe); // instance |
| { |
| StatementListNode instance_stmts = nf.statementList(null, (StatementListNode)null); |
| parseTraits(iinfo.getITraits(), instance_stmts, build_ast, null, build_ast); // Traits for the instance |
| cdn.instanceinits = new ObjectList<Node>(instance_stmts.items.size()); |
| if( instance_stmts.items.size() > 0) |
| { |
| cdn.instanceinits.addAll(instance_stmts.items); |
| } |
| // Add nodes for the constructor, which doesn't have a traits entry. |
| DefAndSlot d = methodTrait("$construct", ctx.publicNamespace(),0,iinfo.getInitIndex(),0,0, build_ast); |
| DefinitionNode iinit_node = d.def; |
| if( build_ast ) |
| cdn.instanceinits.add(iinit_node); |
| |
| int implied_idx = current_scope.getImplicitIndex(ctx, slot_id, Tokens.NEW_TOKEN); |
| Slot class_slot = current_scope.getSlot(ctx, implied_idx); |
| if( class_slot != null) |
| { |
| //class_slot.setType(d.slot.getType()); |
| class_slot.setTypes(d.slot.getTypes()); |
| class_slot.setDeclStyles(d.slot.getDeclStyles()); |
| } |
| } |
| ctx.popScope(); // instance |
| |
| AbcData.ClassInfo cinfo = this.abcData.getClassInfo(classID); |
| parseTraits(cinfo.getCTraits(), cdn.statements, build_ast, null, build_ast); // Traits for the class (statics) |
| } |
| ctx.popStaticClassScopes(cdn); //class |
| |
| clsdefs_sets.removeLast(); |
| region_name_stack.pop_back(); |
| |
| ret.def = cdn; |
| return ret; |
| } |
| |
| // parsetraits - if prog is non-null we will populate prog.toplevelDefinitions |
| private void parseTraits(AbcData.Trait[] traits, StatementListNode statements, boolean build_ast, BinaryProgramNode prog, boolean buildASTForClasses) |
| { |
| for (AbcData.Trait t: traits) |
| { |
| AbcData.BinaryMN mn = getBinaryMNFromCPool(t.getNameIndex()); |
| String name = getStringFromCPool(mn.nameID); |
| ObjectValue ns; |
| ns = getNamespaceFromMultiname(mn); |
| |
| DefAndSlot d = null; |
| switch (t.getTag()) |
| { |
| case ActionBlockConstants.TRAIT_Var: |
| case ActionBlockConstants.TRAIT_Const: |
| d = slotTrait(name, ns, t.getSlotId(), t.getTypeName(), t.getValueIndex(), t.getValueKind(), t.isConstTrait(), build_ast); |
| if( build_ast ) |
| statements.items.add(d.def); |
| |
| if( prog != null ) |
| prog.toplevelDefinitions.add(new QName(ns, name)); |
| |
| break; |
| |
| case ActionBlockConstants.TRAIT_Method: |
| case ActionBlockConstants.TRAIT_Getter: |
| case ActionBlockConstants.TRAIT_Setter: |
| d = methodTrait(name, ns, t.getDispId(), t.getMethodId(), t.getAttrs(), t.getTag(), build_ast); |
| if( build_ast ) |
| statements.items.add(d.def); |
| |
| if( prog != null ) |
| prog.toplevelDefinitions.add(new QName(ns, name)); |
| |
| break; |
| |
| case ActionBlockConstants.TRAIT_Class: |
| d = classTrait(name, ns, t.getSlotId(), t.getClassId(), buildASTForClasses); |
| |
| if( prog != null ) |
| prog.toplevelDefinitions.add(new QName(ns, name)); |
| |
| break; |
| |
| case ActionBlockConstants.TRAIT_Function: // currently unused |
| break; |
| |
| default: |
| break; |
| //throw new DecoderException("Invalid trait kind: " + kind); |
| } |
| |
| if( t.hasMetadata() && d!= null ) |
| { |
| int[] metadata = t.getMetadata(); |
| for( int metaIndex: metadata ) |
| { |
| MetaDataNode metaNode = parseMetadataInfo(metaIndex); |
| |
| if ( d.def != null ){ |
| metaNode.def = d.def; |
| d.def.addMetaDataNode(metaNode); |
| if( build_ast ) |
| statements.items.add(metaNode); //{pmd} now hasn't this just been hung off the def node? yup, but fails if not here too. |
| } |
| |
| if( d.slot != null ) |
| { |
| String md_id = metaNode.getId(); |
| if( (MetaDataEvaluator.GO_TO_CTOR_DEFINITION_HELP != md_id) && |
| (MetaDataEvaluator.GO_TO_DEFINITION_HELP != md_id) ) // Should be ok since the cpool entries are interned |
| { |
| // Don't add the go_to_def metadata - its never needed by the compiler (its just a hint for tools) |
| // and it takes up a ton of memory if we add it. |
| //??? Used to only add 'deprecated' metadata...???ask Erik whats up here? |
| d.slot.addMetadata(metaNode); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Extract a namespace from a multiname. |
| * @param mn - the multiname. It should only have one namespace if it's a set. |
| * @return the multiname's namespace. |
| */ |
| private ObjectValue getNamespaceFromMultiname(AbcData.BinaryMN mn) |
| { |
| ObjectValue ns; |
| if( mn.nsIsSet ) |
| { |
| Namespaces ns_set = getNamespaces(mn.nsID); |
| if (ns_set.size() > 1) { // they should all have the same URI modulo version maker |
| ns = null; |
| for (ObjectValue t : ns_set) { |
| if (ns == null || t.name.length() < ns.name.length()) { |
| ns = t; // the unmarked uri is always the shortest, so return it |
| } |
| } |
| } |
| else { |
| ns = ns_set.at(0); |
| } |
| } |
| else |
| { |
| ns = getNamespace(mn.nsID); |
| } |
| return ns; |
| } |
| |
| private void parseScript(int scriptIndex, BinaryProgramNode program, boolean collectTopLevel, boolean buildASTForClasses) |
| { |
| if( scriptIndex < 0 || scriptIndex >= this.abcData.getScriptInfoSize() ) |
| { |
| return; |
| } |
| |
| AbcData.ScriptInfo s = this.abcData.getScriptInfo(scriptIndex); |
| // at top level always want to set build_ast param to true |
| parseTraits(s.getScriptTraits(), program.statements, true, collectTopLevel ? program : null, buildASTForClasses); |
| } |
| |
| |
| private MetaDataNode parseMetadataInfo( int index ) |
| { |
| // TODO: Cache this data more aggressively. |
| MetaDataNode metaNode = ctx.getNodeFactory().metaData(null, -1); |
| metaNode.setMetadata(this.abcData.getMetadata(index, metaNode)); |
| return metaNode; |
| } |
| |
| |
| |
| private String getStringFromCPool(int id) |
| { |
| return this.abcData.getString(id); |
| } |
| |
| private Decimal128 getDecimalFromCPool(int id) |
| { |
| return this.abcData.getDecimal(id); |
| } |
| |
| private double getNumberFromCPool(int id, int kind) |
| { |
| double ret = 0.0; |
| switch(kind) |
| { |
| case ActionBlockConstants.CONSTANT_Integer: |
| ret = this.abcData.getInt(id); |
| return ret; |
| |
| case ActionBlockConstants.CONSTANT_UInteger: |
| ret = this.abcData.getUint(id) & 0xFFFFFFFFL; |
| return ret; |
| |
| case ActionBlockConstants.CONSTANT_Double: |
| ret = this.abcData.getDouble(id); |
| return ret; |
| } |
| // {pmd} silent fail here...is this really a good idea? |
| return ret; |
| } |
| |
| |
| private AbcData.BinaryMN getBinaryMNFromCPool(int index) |
| { |
| return this.abcData.getName(index); |
| } |
| |
| Namespaces getNamespaces(int namespaceSetID) |
| { |
| int[] ns_ids = this.abcData.getNamespaceSet(namespaceSetID).getNamespaceIds(); |
| Namespaces val = new Namespaces(ns_ids.length); |
| for(int i = 0; i < ns_ids.length; ++i) |
| { |
| val.add(getNamespace(ns_ids[i])); |
| } |
| return val; |
| } |
| |
| ObjectValue getNamespace(int namespaceID) |
| { |
| ObjectValue ns = null; |
| if( namespaceID == 0 ) |
| { |
| ns = ctx.anyNamespace(); |
| } |
| else |
| { |
| int kind = this.abcData.getNamespace(namespaceID).nsKind; |
| String uri = this.abcData.getNamespace(namespaceID).getName(); |
| int ver = -1; |
| byte ns_kind; |
| |
| switch(kind) |
| { |
| case ActionBlockConstants.CONSTANT_Namespace: |
| //vers.add(getVersion(uri)); |
| ns_kind = Context.NS_PUBLIC; |
| ns = ctx.getNamespace(uri); |
| break; |
| case ActionBlockConstants.CONSTANT_PackageNamespace: |
| //vers.add(getVersion(uri)); |
| ns_kind = Context.NS_PUBLIC; |
| ns = ctx.getNamespace(uri); |
| ns.setPackage(true); |
| break; |
| case ActionBlockConstants.CONSTANT_ProtectedNamespace: |
| ns_kind = Context.NS_PROTECTED; |
| ns = ctx.getNamespace(uri, ns_kind); |
| break; |
| case ActionBlockConstants.CONSTANT_PackageInternalNs: |
| ns_kind = Context.NS_INTERNAL; |
| ns = ctx.getNamespace(uri, ns_kind); |
| break; |
| case ActionBlockConstants.CONSTANT_PrivateNamespace: |
| ns_kind = Context.NS_PRIVATE; |
| ns = ctx.getNamespace(uri, ns_kind); |
| break; |
| case ActionBlockConstants.CONSTANT_ExplicitNamespace: |
| ns_kind = Context.NS_EXPLICIT; |
| ns = ctx.getNamespace(uri, ns_kind); |
| break; |
| case ActionBlockConstants.CONSTANT_StaticProtectedNs: |
| ns_kind = Context.NS_STATIC_PROTECTED; |
| ns = ctx.getNamespace(uri, ns_kind); |
| break; |
| default: |
| throw new IllegalStateException("Invalid kind:" + Integer.toString(kind)); |
| } |
| } |
| return ns; |
| } |
| |
| |
| /* |
| // These dump functions below are just some debugging aids. |
| private void dumpProgram(BinaryProgramNode program) |
| { |
| |
| if( program.statements != null) |
| { |
| dumpStatementList(program.statements); |
| } |
| } |
| |
| private void dumpClass(BinaryClassDefNode bincls) |
| { |
| if( bincls.attrs != null) |
| System.out.print(attrsToString(bincls.attrs)); |
| System.out.print("class " + bincls.cframe.name); |
| if( bincls.baseref != null) |
| { |
| System.out.print(" extends " + typeRefToString(bincls.baseref)); |
| } |
| if( bincls.interfaces != null ) |
| { |
| System.out.print(" implements " ); |
| for( int i = 0; i < bincls.interfaces.size(); ++ i) |
| { |
| Node n = bincls.interfaces.items.get(i); |
| if( n instanceof MemberExpressionNode ) |
| { |
| System.out.print( typeRefToString(((MemberExpressionNode)n).ref)); |
| } |
| } |
| } |
| System.out.println(""); |
| System.out.println("{"); |
| if( bincls.statements != null) |
| { |
| dumpStatementList(bincls.statements); |
| } |
| |
| if( bincls.instanceinits != null) |
| { |
| dumpNodeList(bincls.instanceinits); |
| } |
| |
| System.out.println("} //"+bincls.cframe.name); |
| } |
| |
| private void dumpStatementList(StatementListNode stmts) |
| { |
| dumpNodeList(stmts.items); |
| } |
| private void dumpNodeList(ObjectList<Node> stmts) |
| { |
| for( Node n : stmts) |
| { |
| if( n instanceof FunctionDefinitionNode ) |
| dumpFunction((FunctionDefinitionNode)n); |
| else if( n instanceof VariableDefinitionNode ) |
| dumpVar((VariableDefinitionNode)n); |
| else if( n instanceof BinaryClassDefNode) |
| dumpClass((BinaryClassDefNode)n); |
| else if( n instanceof NamespaceDefinitionNode) |
| dumpNamespace((NamespaceDefinitionNode)n); |
| } |
| } |
| private void dumpFunction(FunctionDefinitionNode funcDef) |
| { |
| |
| if( funcDef.attrs != null) |
| System.out.print(attrsToString(funcDef.attrs)); |
| System.out.print("function "); |
| if( funcDef.fexpr.kind == Tokens.GET_TOKEN) |
| System.out.print("get "); |
| else if (funcDef.fexpr.kind == Tokens.SET_TOKEN) |
| System.out.print("set "); |
| System.out.print( typeRefToString(funcDef.fexpr.identifier.ref) ); |
| System.out.print("("); |
| if(funcDef.fexpr.signature.parameter != null) |
| { |
| ParameterListNode params = funcDef.fexpr.signature.parameter; |
| for(int i = 0; i < params.size(); ++ i) |
| { |
| ParameterNode param = params.items.get(i); |
| if(param != null && param.typeref != null) |
| { |
| System.out.print(typeRefToString(param.typeref) ); |
| } |
| System.out.print(", "); |
| } |
| } |
| System.out.print(")"); |
| if( funcDef.fexpr.signature.result != null) |
| { |
| System.out.println( " : " + typeRefToString( ((MemberExpressionNode)funcDef.fexpr.signature.result).ref) ); |
| } |
| } |
| |
| private void dumpVar(VariableDefinitionNode varDef) |
| { |
| if(varDef.attrs != null) |
| System.out.print(attrsToString(varDef.attrs)); |
| Node first = varDef.list.items.at(0); |
| System.out.print(" " + typeRefToString( ((VariableBindingNode)first).ref)); |
| System.out.println(" : " + typeRefToString( ((VariableBindingNode)first).typeref) ); |
| // /System.out.println(" Name: " + varDef.); |
| } |
| |
| private void dumpNamespace(NamespaceDefinitionNode nsDef) |
| { |
| System.out.print( attrsToString(nsDef.attrs) ); |
| System.out.println( " namespace " + nsDef.name.name + " = \"" + ((LiteralStringNode)nsDef.value).value + "\""); |
| |
| } |
| private String attrsToString(AttributeListNode attr) |
| { |
| String ret = ""; |
| if( attr.hasStatic ) |
| ret += "static "; |
| if( attr.hasPublic) |
| ret += "public "; |
| else if( attr.hasPrivate) |
| ret += "private "; |
| if(attr.hasOverride) |
| ret += " override "; |
| if(attr.hasFinal) |
| ret += " final "; |
| if(attr.namespaces.size() > 0) |
| ret += attr.namespaces.at(0).name + " "; |
| |
| return ret; |
| } |
| private String typeRefToString(ReferenceValue typeref) |
| { |
| String value = ""; |
| if( typeref.getImmutableNamespaces().size() == 1) |
| { |
| ObjectValue ns = typeref.getImmutableNamespaces().first(); |
| if( ns != null && !ns.name.equals("") ) |
| { |
| value = ns.name + ":"; |
| } |
| value += typeref.name; |
| } |
| else |
| { |
| value += "{"; |
| for( int i = 0; i < typeref.getImmutableNamespaces().size(); ++i ) |
| { |
| ObjectValue ns = typeref.getImmutableNamespaces().get(i); |
| value += ns.name + ", "; |
| } |
| value += "}:" + typeref.name; |
| } |
| return value; |
| } |
| */ |
| |
| public static void main(String[] args) throws Throwable |
| { |
| File abcFile = new File(args[0]); |
| byte[] bytes = getBytesFromFile(abcFile); |
| |
| TypeValue.init(); |
| ObjectValue.init(); |
| Context ctx = new Context(new ContextStatics()); |
| AbcParser abcParser = new AbcParser(ctx, bytes); |
| @SuppressWarnings("unused") |
| ProgramNode programNode = abcParser.parseAbc(); |
| |
| //System.out.println(programNode.toString()); |
| } |
| |
| private static byte[] getBytesFromFile(File file) throws IOException |
| { |
| FileInputStream is = new FileInputStream(file); |
| |
| // Get the size of the file |
| long length = file.length(); |
| |
| // Create the byte array to hold the data |
| byte[] bytes = new byte[(int)length]; |
| |
| // Read in the bytes |
| int offset = 0; |
| int numRead = 0; |
| while (offset < bytes.length |
| && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) |
| { |
| offset += numRead; |
| } |
| |
| // Ensure all the bytes have been read in |
| if (offset < bytes.length) |
| { |
| throw new IOException("Could not completely read file "+file.getName()); |
| } |
| |
| // Close the input stream and return bytes |
| is.close(); |
| return bytes; |
| } |
| |
| } |
| |