The Labs \ Source Viewer \ SSCLI \ Microsoft.JScript \ FunctionDeclaration

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace Microsoft.JScript
  16. {
  17.     using Microsoft.JScript.Vsa;
  18.     using System;
  19.     using System.Reflection;
  20.     using System.Reflection.Emit;
  21.     using System.Text;
  22.     using System.Globalization;
  23.    
  24.     public sealed class FunctionDeclaration : AST
  25.     {
  26.         internal FunctionObject func;
  27.         private Member declaringObject = null;
  28.         private TypeExpression ifaceId = null;
  29.         private string name;
  30.         internal bool isMethod;
  31.         private bool inFastScope = false;
  32.         private JSVariableField field = null;
  33.         internal JSProperty enclosingProperty = null;
  34.         private Completion completion = new Completion();
  35.        
  36.         internal FunctionDeclaration(Context context, AST ifaceId, IdentifierLiteral id, ParameterDeclaration[] formal_parameters, TypeExpression return_type, Block body, FunctionScope own_scope, FieldAttributes attributes, bool isMethod, bool isGetter,
  37.         bool isSetter, bool isAbstract, bool isFinal, CustomAttributeList customAttributes) : base(context)
  38.         {
  39.             MethodAttributes methodAttributes = (MethodAttributes)0;
  40.             if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public)
  41.                 methodAttributes = MethodAttributes.Public;
  42.             else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private)
  43.                 methodAttributes = MethodAttributes.Private;
  44.             else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly)
  45.                 methodAttributes = MethodAttributes.Assembly;
  46.             else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family)
  47.                 methodAttributes = MethodAttributes.Family;
  48.             else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem)
  49.                 methodAttributes = MethodAttributes.FamORAssem;
  50.             else
  51.                 methodAttributes = MethodAttributes.Public;
  52.            
  53.             if ((attributes & FieldAttributes.Static) != 0 || !isMethod)
  54.                 methodAttributes |= MethodAttributes.Static;
  55.             else
  56.                 methodAttributes |= MethodAttributes.Virtual | MethodAttributes.NewSlot;
  57.            
  58.             if (isAbstract)
  59.                 methodAttributes |= MethodAttributes.Abstract;
  60.             if (isFinal)
  61.                 methodAttributes |= MethodAttributes.Final;
  62.            
  63.             this.name = id.ToString();
  64.             this.isMethod = isMethod;
  65.             if (ifaceId != null) {
  66.                 if (isMethod) {
  67.                     this.ifaceId = new TypeExpression(ifaceId);
  68.                     methodAttributes &= ~MethodAttributes.MemberAccessMask;
  69.                     methodAttributes |= MethodAttributes.Private | MethodAttributes.Final;
  70.                 }
  71.                 else {
  72.                     this.declaringObject = new Member(ifaceId.context, ifaceId, id);
  73.                     this.name = this.declaringObject.ToString();
  74.                 }
  75.             }
  76.             ScriptObject enclosingScope = Globals.ScopeStack.Peek();
  77.             if (attributes == 0 && !isAbstract && !isFinal) {
  78.                 if (enclosingScope is ClassScope)
  79.                     attributes |= FieldAttributes.Public;
  80.             }
  81.             else {
  82.                 if (!(enclosingScope is ClassScope)) {
  83.                     this.context.HandleError(JSError.NotInsideClass);
  84.                     attributes = (FieldAttributes)0;
  85.                     methodAttributes = MethodAttributes.Public;
  86.                 }
  87.             }
  88.             if (enclosingScope is ActivationObject) {
  89.                 this.inFastScope = ((ActivationObject)enclosingScope).fast;
  90.                 // if later on originalName != this.name this is a property getter/setter
  91.                 string originalName = this.name;
  92.                 // mangle the name
  93.                 if (isGetter) {
  94.                     methodAttributes |= MethodAttributes.SpecialName;
  95.                     this.name = "get_" + this.name;
  96.                     if (return_type == null)
  97.                         return_type = new TypeExpression(new ConstantWrapper(Typeob.Object, context));
  98.                 }
  99.                 else if (isSetter) {
  100.                     methodAttributes |= MethodAttributes.SpecialName;
  101.                     this.name = "set_" + this.name;
  102.                     return_type = new TypeExpression(new ConstantWrapper(Typeob.Void, context));
  103.                 }
  104.                 attributes &= FieldAttributes.FieldAccessMask;
  105.                
  106.                 MethodAttributes access = methodAttributes & MethodAttributes.MemberAccessMask;
  107.                 if ((methodAttributes & MethodAttributes.Virtual) != (MethodAttributes)0 && (methodAttributes & MethodAttributes.Final) == (MethodAttributes)0 && (access == MethodAttributes.Private || access == MethodAttributes.Assembly || access == MethodAttributes.FamANDAssem))
  108.                     methodAttributes |= MethodAttributes.CheckAccessOnOverride;
  109.                
  110.                 // create the function object
  111.                 this.func = new FunctionObject(this.name, formal_parameters, return_type, body, own_scope, enclosingScope, this.context, methodAttributes, customAttributes, this.isMethod
  112.                 );
  113.                 if (this.declaringObject != null)
  114.                     return;
  115.                 // check whether the function name (possibly mangled) is in use already
  116.                 string fieldName = this.name;
  117.                 if (this.ifaceId != null)
  118.                     fieldName = ifaceId.ToString() + "." + fieldName;
  119.                 JSVariableField localField = (JSVariableField)((ActivationObject)enclosingScope).name_table[fieldName];
  120.                 if (localField != null && (!(localField is JSMemberField) || !(((JSMemberField)localField).value is FunctionObject) || this.func.isExpandoMethod)) {
  121.                     if (originalName != this.name)
  122.                         localField.originalContext.HandleError(JSError.ClashWithProperty);
  123.                     else {
  124.                         id.context.HandleError(JSError.DuplicateName, this.func.isExpandoMethod);
  125.                         if (localField.value is FunctionObject)
  126.                             ((FunctionObject)localField.value).suppressIL = true;
  127.                     }
  128.                 }
  129.                 // create or update the proper field
  130.                 if (this.isMethod) {
  131.                     if (!(localField is JSMemberField) || !(((JSMemberField)localField).value is FunctionObject) || originalName != this.name) {
  132.                         this.field = ((ActivationObject)enclosingScope).AddNewField(fieldName, this.func, attributes | FieldAttributes.Literal);
  133.                         if (originalName == this.name)
  134.                             // if it is a property do not assign the type
  135.                             ((JSVariableField)this.field).type = new TypeExpression(new ConstantWrapper(Typeob.FunctionWrapper, this.context));
  136.                     }
  137.                     else
  138.                         this.field = ((JSMemberField)localField).AddOverload(this.func, attributes | FieldAttributes.Literal);
  139.                 }
  140.                 else if (enclosingScope is FunctionScope) {
  141.                     if (this.inFastScope)
  142.                         attributes |= FieldAttributes.Literal;
  143.                     this.field = ((FunctionScope)enclosingScope).AddNewField(this.name, attributes, this.func);
  144.                     if (this.field is JSLocalField) {
  145.                         JSLocalField locField = (JSLocalField)this.field;
  146.                         if (this.inFastScope) {
  147.                             locField.type = new TypeExpression(new ConstantWrapper(Typeob.ScriptFunction, this.context));
  148.                             locField.attributeFlags |= FieldAttributes.Literal;
  149.                         }
  150.                         locField.debugOn = this.context.document.debugOn;
  151.                         locField.isDefined = true;
  152.                     }
  153.                 }
  154.                 else if (this.inFastScope) {
  155.                     this.field = ((ActivationObject)enclosingScope).AddNewField(this.name, this.func, attributes | FieldAttributes.Literal);
  156.                     ((JSVariableField)this.field).type = new TypeExpression(new ConstantWrapper(Typeob.ScriptFunction, this.context));
  157.                     //Do not use typeof(Closure) for the field, since that has the arguments and callee properties, which are not
  158.                     //accessible in fast mode
  159.                 }
  160.                 else
  161.                     //enclosingScope is GlobalObject
  162.                     this.field = ((ActivationObject)enclosingScope).AddNewField(this.name, this.func, attributes | FieldAttributes.Static);
  163.                 ((JSVariableField)this.field).originalContext = context;
  164.                
  165.                 // if it is a property create/update the PropertyInfo and assign the getter/setter
  166.                 if (originalName != this.name) {
  167.                     string propertyFieldName = originalName;
  168.                     if (this.ifaceId != null)
  169.                         propertyFieldName = ifaceId.ToString() + "." + originalName;
  170.                     FieldInfo prop = (FieldInfo)((ClassScope)enclosingScope).name_table[propertyFieldName];
  171.                     if (prop != null) {
  172.                         // check whether a property was defined already
  173.                         if (prop.IsLiteral) {
  174.                             object val = ((JSVariableField)prop).value;
  175.                             if (val is JSProperty)
  176.                                 this.enclosingProperty = (JSProperty)val;
  177.                         }
  178.                         if (this.enclosingProperty == null)
  179.                             id.context.HandleError(JSError.DuplicateName, true);
  180.                         // the matching name was not a property
  181.                     }
  182.                     if (this.enclosingProperty == null) {
  183.                         this.enclosingProperty = new JSProperty(originalName);
  184.                         prop = ((ActivationObject)enclosingScope).AddNewField(propertyFieldName, this.enclosingProperty, attributes | FieldAttributes.Literal);
  185.                         ((JSMemberField)prop).originalContext = this.context;
  186.                     }
  187.                     else {
  188.                         if ((isGetter && this.enclosingProperty.getter != null) || (isSetter && this.enclosingProperty.setter != null))
  189.                             id.context.HandleError(JSError.DuplicateName, true);
  190.                         // duplicated setter or getter
  191.                     }
  192.                     if (isGetter)
  193.                         this.enclosingProperty.getter = new JSFieldMethod(this.field, enclosingScope);
  194.                     else
  195.                         this.enclosingProperty.setter = new JSFieldMethod(this.field, enclosingScope);
  196.                 }
  197.             }
  198.             else {
  199.                 //Might get here if function declaration is inside of an eval.
  200.                 this.inFastScope = false;
  201.                 this.func = new FunctionObject(this.name, formal_parameters, return_type, body, own_scope, enclosingScope, this.context, MethodAttributes.Public, null, false
  202.                 );
  203.                 this.field = ((StackFrame)enclosingScope).AddNewField(this.name, new Closure(this.func), attributes | FieldAttributes.Static);
  204.             }
  205.         }
  206.        
  207.         internal override object Evaluate()
  208.         {
  209.             if (this.declaringObject != null)
  210.                 this.declaringObject.SetValue(this.func);
  211.             return this.completion;
  212.         }
  213.        
  214.         public static Closure JScriptFunctionDeclaration(RuntimeTypeHandle handle, string name, string method_name, string[] formal_parameters, JSLocalField[] fields, bool must_save_stack_locals, bool hasArgumentsObject, string text, object declaringObject, VsaEngine engine
  215.         )
  216.         {
  217.             Type t = Type.GetTypeFromHandle(handle);
  218.             FunctionObject func = new FunctionObject(t, name, method_name, formal_parameters, fields, must_save_stack_locals, hasArgumentsObject, text, engine);
  219.             return new Closure(func, declaringObject);
  220.         }
  221.        
  222.         internal override Context GetFirstExecutableContext()
  223.         {
  224.             return null;
  225.         }
  226.        
  227.         internal override AST PartiallyEvaluate()
  228.         {
  229.             if (this.ifaceId != null) {
  230.                 this.ifaceId.PartiallyEvaluate();
  231.                 this.func.implementedIface = this.ifaceId.ToIReflect();
  232.                 Type t = this.func.implementedIface as Type;
  233.                 ClassScope csc = this.func.implementedIface as ClassScope;
  234.                 if (t != null && !t.IsInterface || csc != null && !csc.owner.isInterface) {
  235.                     this.ifaceId.context.HandleError(JSError.NeedInterface);
  236.                     this.func.implementedIface = null;
  237.                 }
  238.                 if ((this.func.attributes & MethodAttributes.Abstract) != 0)
  239.                     this.func.funcContext.HandleError(JSError.AbstractCannotBePrivate);
  240.             }
  241.             else if (this.declaringObject != null)
  242.                 this.declaringObject.PartiallyEvaluateAsCallable();
  243.             this.func.PartiallyEvaluate();
  244.             if (this.inFastScope && this.func.isExpandoMethod && this.field != null && this.field.type != null)
  245.                 this.field.type.expression = new ConstantWrapper(Typeob.ScriptFunction, null);
  246.             if ((this.func.attributes & MethodAttributes.Abstract) != 0 && !((ClassScope)this.func.enclosing_scope).owner.isAbstract) {
  247.                 ((ClassScope)this.func.enclosing_scope).owner.attributes |= TypeAttributes.Abstract;
  248.                 ((ClassScope)this.func.enclosing_scope).owner.context.HandleError(JSError.CannotBeAbstract, this.name);
  249.             }
  250.             if (this.enclosingProperty != null)
  251.                 if (!this.enclosingProperty.GetterAndSetterAreConsistent())
  252.                     this.context.HandleError(JSError.GetAndSetAreInconsistent);
  253.             return this;
  254.         }
  255.        
  256.         private void TranslateToILClosure(ILGenerator il)
  257.         {
  258.             if (!this.func.isStatic)
  259.                 il.Emit(OpCodes.Ldarg_0);
  260.             il.Emit(OpCodes.Ldtoken, this.func.classwriter != null ? this.func.classwriter : compilerGlobals.classwriter);
  261.             il.Emit(OpCodes.Ldstr, this.name);
  262.             il.Emit(OpCodes.Ldstr, this.func.GetName());
  263.             int n = this.func.formal_parameters.Length;
  264.             ConstantWrapper.TranslateToILInt(il, n);
  265.             il.Emit(OpCodes.Newarr, Typeob.String);
  266.             for (int i = 0; i < n; i++) {
  267.                 il.Emit(OpCodes.Dup);
  268.                 ConstantWrapper.TranslateToILInt(il, i);
  269.                 il.Emit(OpCodes.Ldstr, this.func.formal_parameters[i]);
  270.                 il.Emit(OpCodes.Stelem_Ref);
  271.             }
  272.             n = this.func.fields.Length;
  273.             ConstantWrapper.TranslateToILInt(il, n);
  274.             il.Emit(OpCodes.Newarr, Typeob.JSLocalField);
  275.             for (int i = 0; i < n; i++) {
  276.                 JSLocalField field = this.func.fields[i];
  277.                 il.Emit(OpCodes.Dup);
  278.                 ConstantWrapper.TranslateToILInt(il, i);
  279.                 il.Emit(OpCodes.Ldstr, field.Name);
  280.                 il.Emit(OpCodes.Ldtoken, field.FieldType);
  281.                 ConstantWrapper.TranslateToILInt(il, field.slotNumber);
  282.                 il.Emit(OpCodes.Newobj, CompilerGlobals.jsLocalFieldConstructor);
  283.                 il.Emit(OpCodes.Stelem_Ref);
  284.             }
  285.             if (this.func.must_save_stack_locals)
  286.                 il.Emit(OpCodes.Ldc_I4_1);
  287.             else
  288.                 il.Emit(OpCodes.Ldc_I4_0);
  289.             if (this.func.hasArgumentsObject)
  290.                 il.Emit(OpCodes.Ldc_I4_1);
  291.             else
  292.                 il.Emit(OpCodes.Ldc_I4_0);
  293.             il.Emit(OpCodes.Ldstr, this.func.ToString());
  294.             if (!this.func.isStatic)
  295.                 il.Emit(OpCodes.Ldarg_0);
  296.             else
  297.                 il.Emit(OpCodes.Ldnull);
  298.             this.EmitILToLoadEngine(il);
  299.             il.Emit(OpCodes.Call, CompilerGlobals.jScriptFunctionDeclarationMethod);
  300.         }
  301.        
  302.         internal override void TranslateToIL(ILGenerator il, Type rtype)
  303.         {
  304.             Debug.PreCondition(rtype == Typeob.Void);
  305.             return;
  306.         }
  307.        
  308.         internal override void TranslateToILInitializer(ILGenerator il)
  309.         {
  310.             if (this.func.suppressIL)
  311.                 return;
  312.             this.func.TranslateToIL(compilerGlobals);
  313.             if (this.declaringObject != null) {
  314.                 this.declaringObject.TranslateToILInitializer(il);
  315.                 this.declaringObject.TranslateToILPreSet(il);
  316.                 this.TranslateToILClosure(il);
  317.                 this.declaringObject.TranslateToILSet(il);
  318.                 return;
  319.             }
  320.             object tok = ((JSVariableField)this.field).metaData;
  321.             if (this.func.isMethod) {
  322.                 if (tok is FunctionDeclaration) {
  323.                     ((JSVariableField)this.field).metaData = null;
  324.                     return;
  325.                 }
  326.                 this.TranslateToILSourceTextProvider();
  327.                 return;
  328.             }
  329.             if (tok == null)
  330.                 return;
  331.             this.TranslateToILClosure(il);
  332.             if (tok is LocalBuilder)
  333.                 il.Emit(OpCodes.Stloc, (LocalBuilder)tok);
  334.             else if (this.func.isStatic)
  335.                 il.Emit(OpCodes.Stsfld, (FieldInfo)tok);
  336.             else
  337.                 il.Emit(OpCodes.Stfld, (FieldInfo)tok);
  338.         }
  339.        
  340.         private void TranslateToILSourceTextProvider()
  341.         {
  342.             if (this.Engine.doFast)
  343.                 return;
  344.            
  345.             // If the field name doesn't match this name, then don't emit the stub for source
  346.             // text provider. The function is a private method impl and there is no way to get
  347.             // a handle to the function. i.e. name="Bar", field.name="InterfaceA.Bar"
  348.             if (0 != String.Compare(this.name, this.field.Name, StringComparison.Ordinal))
  349.                 return;
  350.            
  351.             StringBuilder sb = new StringBuilder(this.func.ToString());
  352.             JSMemberField mf = ((JSMemberField)this.field).nextOverload;
  353.             while (mf != null) {
  354.                 mf.metaData = this;
  355.                 sb.Append('\n');
  356.                 sb.Append(mf.value.ToString());
  357.                 mf = mf.nextOverload;
  358.             }
  359.             MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Static;
  360.             MethodBuilder mb = ((ClassScope)(this.func.enclosing_scope)).GetTypeBuilder().DefineMethod(this.name + " source", attr, Typeob.String, new Type[0]);
  361.             ILGenerator il = mb.GetILGenerator();
  362.             il.Emit(OpCodes.Ldstr, sb.ToString());
  363.             il.Emit(OpCodes.Ret);
  364.         }
  365.     }
  366. }

Developer Fusion