Files
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

1081 lines
51 KiB
C#

// ==++==
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
namespace Microsoft.JScript
{
using Microsoft.JScript.Vsa;
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
public sealed class FunctionObject : ScriptFunction{
internal ParameterDeclaration[] parameter_declarations;
internal String[] formal_parameters;
internal TypeExpression return_type_expr;
private Block body;
private MethodInfo method;
private ParameterInfo[] parameterInfos;
internal Context funcContext;
private int argumentsSlotNumber;
internal JSLocalField[] fields;
internal FunctionScope own_scope;
internal ScriptObject enclosing_scope;
internal bool must_save_stack_locals; //because it has a nested function (that accesses one of its locals) or an eval statement
internal bool hasArgumentsObject;
internal IReflect implementedIface;
internal MethodInfo implementedIfaceMethod;
internal bool isMethod;
internal bool isExpandoMethod;
internal bool isConstructor;
internal bool isImplicitCtor;
internal bool isStatic;
internal bool noVersionSafeAttributeSpecified;
internal bool suppressIL;
internal String text;
private MethodBuilder mb;
private ConstructorBuilder cb;
internal TypeBuilder classwriter;
internal MethodAttributes attributes;
internal Globals globals;
private ConstructorInfo superConstructor;
internal ConstructorCall superConstructorCall;
internal CustomAttributeList customAttributes; // assigned from within the FunctionDeclaration constructor
private CLSComplianceSpec clsCompliance;
private LocalBuilder engineLocal;
private bool partiallyEvaluated;
internal Label returnLabel;
internal FunctionObject(String name, ParameterDeclaration[] parameter_declarations, TypeExpression return_type_expr, Block body, FunctionScope own_scope,
ScriptObject enclosing_scope, Context funcContext, MethodAttributes attributes)
: this(name, parameter_declarations, return_type_expr, body, own_scope, enclosing_scope, funcContext, attributes, null, false){
}
internal FunctionObject(String name, ParameterDeclaration[] parameter_declarations, TypeExpression return_type_expr, Block body, FunctionScope own_scope,
ScriptObject enclosing_scope, Context funcContext, MethodAttributes attributes, CustomAttributeList customAttributes, bool isMethod)
: base(body.Globals.globalObject.originalFunction.originalPrototype, name, parameter_declarations.Length) {
this.parameter_declarations = parameter_declarations;
int n = parameter_declarations.Length;
this.formal_parameters = new String[n];
for (int i = 0; i < n; i++)
this.formal_parameters[i] = parameter_declarations[i].identifier;
this.argumentsSlotNumber = 0;
this.return_type_expr = return_type_expr;
if (this.return_type_expr != null)
own_scope.AddReturnValueField();
this.body = body;
this.method = null;
this.parameterInfos = null;
this.funcContext = funcContext;
this.own_scope = own_scope;
this.own_scope.owner = this;
if ((!(enclosing_scope is ActivationObject) || !((ActivationObject)(enclosing_scope)).fast) && !isMethod){
this.argumentsSlotNumber = this.own_scope.GetNextSlotNumber();
JSLocalField argsf = (JSLocalField)this.own_scope.AddNewField("arguments", null, (FieldAttributes)0);
argsf.type = new TypeExpression(new ConstantWrapper(Typeob.Object, funcContext));
argsf.isDefined = true;
this.hasArgumentsObject = true;
}else
this.hasArgumentsObject = false;
this.implementedIface = null;
this.implementedIfaceMethod = null;
this.isMethod = isMethod;
this.isExpandoMethod = customAttributes == null ? false : customAttributes.ContainsExpandoAttribute();
this.isStatic = this.own_scope.isStatic = (attributes&MethodAttributes.Static) != (MethodAttributes)0;
this.suppressIL = false;
this.noVersionSafeAttributeSpecified = true; // PartiallyEvaluate may change this value
this.fields = this.own_scope.GetLocalFields();
this.enclosing_scope = enclosing_scope;
this.must_save_stack_locals = false; //This is set to true, if need be by PartiallyEvaluate.
this.text = null;
this.mb = null;
this.cb = null;
this.attributes = attributes;
if (!this.isStatic) this.attributes |= MethodAttributes.HideBySig;
this.globals = body.Globals;
this.superConstructor = null;
this.superConstructorCall = null;
this.customAttributes = customAttributes;
this.noExpando = false;
this.clsCompliance = CLSComplianceSpec.NotAttributed;
this.engineLocal = null;
this.partiallyEvaluated = false;
}
internal FunctionObject(Type t, String name, String method_name, String[] formal_parameters, JSLocalField[] fields,
bool must_save_stack_locals, bool hasArgumentsObject, String text, VsaEngine engine)
: base(engine.Globals.globalObject.originalFunction.originalPrototype, name, formal_parameters.Length){
this.engine = engine;
this.formal_parameters = formal_parameters;
this.argumentsSlotNumber = 0;
this.body = null;
TypeReflector tr = TypeReflector.GetTypeReflectorFor(t);
this.method = tr.GetMethod(method_name, BindingFlags.Public|BindingFlags.Static);
this.parameterInfos = this.method.GetParameters();
if (!this.method.IsDefined(Typeob.JSFunctionAttribute, false))
this.isMethod = true;
else{
Object[] attrs = method.GetCustomAttributes(Typeob.JSFunctionAttribute, false);
JSFunctionAttributeEnum attr = ((JSFunctionAttribute)attrs[0]).attributeValue;
this.isExpandoMethod = (attr & JSFunctionAttributeEnum.IsExpandoMethod) != 0;
}
this.funcContext = null;
this.own_scope = null;
this.fields = fields;
this.must_save_stack_locals = must_save_stack_locals;
this.hasArgumentsObject = hasArgumentsObject;
this.text = text;
this.attributes = MethodAttributes.Public;
this.globals = engine.Globals;
this.superConstructor = null;
this.superConstructorCall = null;
this.enclosing_scope = this.globals.ScopeStack.Peek();
this.noExpando = false;
this.clsCompliance = CLSComplianceSpec.NotAttributed;
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal override Object Call(Object[] args, Object thisob){
return this.Call(args, thisob, null, null);
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal override Object Call(Object[] args, Object thisob, Binder binder, CultureInfo culture){
if (this.body == null) //Calling a compiled function directly (not via a Closure). Happens when constructor property is used in new expression.
return this.Call(args, thisob, this.enclosing_scope, new Closure(this), binder, culture);
//Get here from the Evaluator when in fast mode and the function is in the global scope.
StackFrame sf = new StackFrame(thisob is JSObject ? (JSObject)thisob : this.enclosing_scope, this.fields, new Object[this.fields.Length], thisob);
if (this.isConstructor){
sf.closureInstance = thisob;
if (this.superConstructor != null){
if (this.superConstructorCall == null){
if (this.superConstructor is JSConstructor)
this.superConstructor.Invoke(thisob, new Object[0]);
}else{
ASTList argASTs = this.superConstructorCall.arguments;
int n = argASTs.count;
Object[] supArgs = new Object[n];
for (int i = 0; i < n; i++) supArgs[i] = argASTs[i].Evaluate();
this.superConstructor.Invoke(thisob, (BindingFlags)0, binder, supArgs, culture);
}
}
globals.ScopeStack.GuardedPush(thisob is JSObject ? (JSObject)thisob : this.enclosing_scope);
try{
((ClassScope)this.enclosing_scope).owner.body.EvaluateInstanceVariableInitializers();
}finally{
globals.ScopeStack.Pop();
}
}else if (this.isMethod && !this.isStatic)
if (((ClassScope)this.enclosing_scope).HasInstance(thisob))
sf.closureInstance = thisob;
else
throw new JScriptException(JSError.TypeMismatch);
globals.ScopeStack.GuardedPush(sf);
try{
this.own_scope.CloseNestedFunctions(sf);
this.ConvertArguments(args, sf.localVars, 0, args.Length, this.formal_parameters.Length, binder, culture);
Completion result = (Completion)(this.body.Evaluate());
if (result.Return)
return result.value;
else
return null;
}finally{
globals.ScopeStack.Pop();
}
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal override Object Call(Object[] args, Object thisob, ScriptObject enclosing_scope, Closure calleeClosure, Binder binder, CultureInfo culture){
if (this.body != null)
return CallASTFunc(args, thisob, enclosing_scope, calleeClosure, binder, culture);
Object saved_caller = calleeClosure.caller;
calleeClosure.caller = this.globals.caller;
this.globals.caller = calleeClosure;
Object saved_args = calleeClosure.arguments;
ScriptObject calling_scope = globals.ScopeStack.Peek();
ArgumentsObject caller_arguments = (calling_scope is StackFrame) ? ((StackFrame)calling_scope).caller_arguments : null;
StackFrame sf = new StackFrame(enclosing_scope, this.fields, this.must_save_stack_locals ? new Object[this.fields.Length] : null, thisob);
globals.ScopeStack.GuardedPush(sf);
ArgumentsObject arguments = new ArgumentsObject(globals.globalObject.originalObjectPrototype, args, this, calleeClosure, sf, caller_arguments);
sf.caller_arguments = arguments;
calleeClosure.arguments = arguments;
try{
try{
int n = this.formal_parameters.Length;
int m = args.Length;
if (this.hasArgumentsObject){
Object[] newargs = new Object[n+3];
newargs[0] = thisob;
newargs[1] = this.engine;
newargs[2] = arguments;
this.ConvertArguments(args, newargs, 3, m, n, binder, culture);
return this.method.Invoke(thisob, BindingFlags.SuppressChangeType, null, newargs, null);
}else if (!this.isMethod){
Object[] newargs = new Object[n+2];
newargs[0] = thisob;
newargs[1] = this.engine;
this.ConvertArguments(args, newargs, 2, m, n, binder, culture);
return this.method.Invoke(thisob, BindingFlags.SuppressChangeType, null, newargs, null);
}else if (n == m){
this.ConvertArguments(args, args, 0, m, n, binder, culture);
return this.method.Invoke(thisob, BindingFlags.SuppressChangeType, null, args, null);
}else{
Object[] newargs = new Object[n];
this.ConvertArguments(args, newargs, 0, m, n, binder, culture);
return this.method.Invoke(thisob, BindingFlags.SuppressChangeType, null, newargs, null);
}
}catch(TargetInvocationException e){
throw e.InnerException;
}
}finally{
globals.ScopeStack.Pop();
calleeClosure.arguments = saved_args;
this.globals.caller = calleeClosure.caller;
calleeClosure.caller = saved_caller;
}
}
private Object CallASTFunc(Object[] args, Object thisob, ScriptObject enclosing_scope, Closure calleeClosure, Binder binder, CultureInfo culture){
Object saved_caller = calleeClosure.caller;
calleeClosure.caller = this.globals.caller;
this.globals.caller = calleeClosure;
Object saved_args = calleeClosure.arguments;
ScriptObject calling_scope = globals.ScopeStack.Peek();
ArgumentsObject caller_arguments = (calling_scope is StackFrame) ? ((StackFrame)calling_scope).caller_arguments : null;
StackFrame sf = new StackFrame(enclosing_scope, this.fields, new Object[this.fields.Length], thisob);
if (this.isMethod && !this.isStatic)
sf.closureInstance = thisob;
globals.ScopeStack.GuardedPush(sf);
try{
this.own_scope.CloseNestedFunctions(sf);
ArgumentsObject arguments = null;
if (this.hasArgumentsObject){
arguments = new ArgumentsObject(this.globals.globalObject.originalObjectPrototype, args, this, calleeClosure, sf, caller_arguments);
sf.localVars[this.argumentsSlotNumber] = arguments;
}
sf.caller_arguments = arguments;
calleeClosure.arguments = arguments;
this.ConvertArguments(args, sf.localVars, 0, args.Length, this.formal_parameters.Length, binder, culture);
Completion result = (Completion)(this.body.Evaluate());
if (result.Return)
return result.value;
else
return null;
}finally{
this.globals.ScopeStack.Pop();
calleeClosure.arguments = saved_args;
this.globals.caller = calleeClosure.caller;
calleeClosure.caller = saved_caller;
}
}
//assume this code is called after PartiallyEvaluate
internal void CheckCLSCompliance(bool classIsCLSCompliant){
if (classIsCLSCompliant){
if (this.clsCompliance != CLSComplianceSpec.NonCLSCompliant){
for (int i = 0, length = this.parameter_declarations.Length; i < length; i++){
IReflect ir = this.parameter_declarations[i].ParameterIReflect;
if (ir != null && !TypeExpression.TypeIsCLSCompliant(ir)){
this.clsCompliance = CLSComplianceSpec.NonCLSCompliant;
this.funcContext.HandleError(JSError.NonCLSCompliantMember);
return;
}
}
if (this.return_type_expr != null && !this.return_type_expr.IsCLSCompliant()){
this.clsCompliance = CLSComplianceSpec.NonCLSCompliant;
this.funcContext.HandleError(JSError.NonCLSCompliantMember);
}
}
}else if (this.clsCompliance == CLSComplianceSpec.CLSCompliant)
this.funcContext.HandleError(JSError.MemberTypeCLSCompliantMismatch);
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal Object Construct(JSObject thisob, Object[] args){
JSObject newob = new JSObject(null, false);
newob.SetParent(this.GetPrototypeForConstructedObject());
newob.outer_class_instance = thisob;
Object result = this.Call(args, newob);
if (result is ScriptObject) return result;
return newob;
}
private void ConvertArguments(Object[] args, Object[] newargs, int offset, int length, int n, Binder binder, CultureInfo culture){
ParameterInfo[] pars = this.parameterInfos;
if (pars != null){
for (int i = 0, j = offset; i < n; i++, j++){
Type t = pars[j].ParameterType;
if (i == n-1 && pars[j].IsDefined(Typeob.ParamArrayAttribute, false)){
int k = length-i; if (k < 0) k = 0;
newargs[j] = FunctionObject.CopyToNewParamArray(t.GetElementType(), k, args, i, binder, culture);
return;
}
Object argval = i < length ? args[i] : null;
if (t == Typeob.Object)
newargs[j] = argval;
else if (binder != null)
newargs[j] = binder.ChangeType(argval, t, culture);
else
newargs[j] = Convert.CoerceT(argval, t);
}
}else{
ParameterDeclaration[] pds = this.parameter_declarations;
for (int i = 0, j = offset; i < n; i++, j++){
IReflect ir = pds[i].ParameterIReflect;
if (i == n-1 && pds[j].IsDefined(Typeob.ParamArrayAttribute, false)){
int k = length-i; if (k < 0) k = 0;
newargs[j] = FunctionObject.CopyToNewParamArray(((TypedArray)ir).elementType, k, args, i);
return;
}
Object argval = i < length ? args[i] : null;
if (ir == Typeob.Object){
newargs[j] = argval; continue;
}
if (ir is ClassScope)
newargs[j] = Convert.Coerce(argval, ir);
else if (binder != null)
newargs[j] = binder.ChangeType(argval, Convert.ToType(ir), culture);
else
newargs[j] = Convert.CoerceT(argval, Convert.ToType(ir));
}
}
}
private static Object[] CopyToNewParamArray(IReflect ir, int n, Object[] args, int offset){
Object[] paramArray = new Object[n];
for (int i = 0; i < n; i++)
paramArray[i] = Convert.Coerce(args[i+offset], ir);
return paramArray;
}
private static System.Array CopyToNewParamArray(Type t, int n, Object[] args, int offset, Binder binder, CultureInfo culture){
System.Array paramArray = System.Array.CreateInstance(t, n);
for (int i = 0; i < n; i++)
paramArray.SetValue(binder.ChangeType(args[i+offset], t, culture), i);
return paramArray;
}
internal void EmitLastLineInfo(ILGenerator il){
if (!this.isImplicitCtor){
// this is the only case so far where debug info does not go through Context and it's explicitely called here using DocumentContext
int endLine = this.body.context.EndLine;
int endColumn = this.body.context.EndColumn;
this.body.context.document.EmitLineInfo(il, endLine, endColumn, endLine, endColumn + 1);
}
}
internal String GetName(){
return this.name;
}
internal override int GetNumberOfFormalParameters(){
return this.formal_parameters.Length;
}
internal ConstructorInfo GetConstructorInfo(CompilerGlobals compilerGlobals){
return (ConstructorInfo)this.GetMethodBase(compilerGlobals);
}
internal MethodInfo GetMethodInfo(CompilerGlobals compilerGlobals){
return (MethodInfo)this.GetMethodBase(compilerGlobals);
}
internal MethodBase GetMethodBase(CompilerGlobals compilerGlobals){
if (this.mb != null)
return this.mb;
if (this.cb != null)
return this.cb;
JSFunctionAttributeEnum jsFunctionAttributes = 0;
int extras = 3;
if (this.isMethod){
if (this.isConstructor && ((ClassScope)this.enclosing_scope).outerClassField != null){
extras = 1;
jsFunctionAttributes |= JSFunctionAttributeEnum.IsInstanceNestedClassConstructor;
}else
extras = 0;
}else if (!this.hasArgumentsObject)
extras = 2;
int np = this.formal_parameters.Length+extras;
Type[] ptypes = new Type[np];
Type rtype = Convert.ToType(this.ReturnType(null));
if (extras > 0){
if (this.isConstructor)
ptypes[np-1] = ((ClassScope)this.enclosing_scope).outerClassField.FieldType;
else
ptypes[0] = Typeob.Object;
jsFunctionAttributes |= JSFunctionAttributeEnum.HasThisObject;
}
if (extras > 1){
ptypes[1] = Typeob.VsaEngine;
jsFunctionAttributes |= JSFunctionAttributeEnum.HasEngine;
}
if (extras > 2){
ptypes[2] = Typeob.Object;
jsFunctionAttributes |= JSFunctionAttributeEnum.HasArguments;
}
if (this.must_save_stack_locals)
jsFunctionAttributes |= JSFunctionAttributeEnum.HasStackFrame;
if (this.isExpandoMethod)
jsFunctionAttributes |= JSFunctionAttributeEnum.IsExpandoMethod;
if (this.isConstructor){
for (int i = 0; i < np-extras; i++)
ptypes[i] = this.parameter_declarations[i].ParameterType;
}else{
for (int i = extras; i < np; i++)
ptypes[i] = this.parameter_declarations[i-extras].ParameterType;
}
if (this.enclosing_scope is ClassScope)
if (this.isConstructor){
this.cb = ((ClassScope)(this.enclosing_scope)).GetTypeBuilder().DefineConstructor(this.attributes&MethodAttributes.MemberAccessMask,
CallingConventions.Standard, ptypes);
}else{
String name = this.name;
if (this.implementedIfaceMethod != null){
JSMethod jsm = this.implementedIfaceMethod as JSMethod;
if (jsm != null) this.implementedIfaceMethod = jsm.GetMethodInfo(compilerGlobals);
name = this.implementedIfaceMethod.DeclaringType.FullName + "." + name;
}
TypeBuilder tb = ((ClassScope)(this.enclosing_scope)).GetTypeBuilder();
if (this.mb != null) return this.mb; //Might have been defined as a result of the above call
this.mb = tb.DefineMethod(name, this.attributes, rtype, ptypes);
if (this.implementedIfaceMethod != null)
((ClassScope)(this.enclosing_scope)).GetTypeBuilder().DefineMethodOverride(this.mb, this.implementedIfaceMethod);
}
else{
if (enclosing_scope is FunctionScope){ //dealing with a nested function/function expression. Mangle the name.
if (((FunctionScope)enclosing_scope).owner != null){
this.name = ((FunctionScope)enclosing_scope).owner.name+ "." + this.name;
jsFunctionAttributes |= JSFunctionAttributeEnum.IsNested;
}else{ //dealing with the dummy outer scope of a function expression
ScriptObject escope = enclosing_scope;
while (escope != null){
if (escope is FunctionScope && ((FunctionScope)escope).owner != null){
this.name = ((FunctionScope)escope).owner.name+ "." + this.name;
jsFunctionAttributes |= JSFunctionAttributeEnum.IsNested;
break;
}
escope = escope.GetParent();
}
}
}
//The name could still be a duplicate, so check against a table of names already used.
if (compilerGlobals.usedNames[this.name] != null)
this.name = this.name + ":" + (compilerGlobals.usedNames.count).ToString();
compilerGlobals.usedNames[this.name] = this;
this.mb = (this.classwriter = compilerGlobals.classwriter).DefineMethod(this.name, this.attributes, rtype, ptypes);
}
// the method is defined, we can now put debug info about the parameters
if (extras > 0){
if (this.mb != null)
this.mb.DefineParameter(1, (ParameterAttributes)0, "this");
else{
ParameterBuilder pb = this.cb.DefineParameter(np, (ParameterAttributes)0, "this");
pb.SetConstant(null);
extras = 0;
np--;
}
}
if (extras > 1)
this.mb.DefineParameter(2, (ParameterAttributes)0, "vsa Engine");
if (extras > 2)
this.mb.DefineParameter(3, (ParameterAttributes)0, "arguments");
for (int i = extras; i < np; i++){
ParameterBuilder pb = this.mb != null ?
this.mb.DefineParameter(i + 1, (ParameterAttributes)0, this.parameter_declarations[i-extras].identifier) :
this.cb.DefineParameter(i + 1, (ParameterAttributes)0, this.parameter_declarations[i-extras].identifier);
CustomAttributeList paramCustAttList = this.parameter_declarations[i-extras].customAttributes;
if (paramCustAttList != null){
CustomAttributeBuilder[] paramCustomAttributes = paramCustAttList.GetCustomAttributeBuilders(false);
for (int attIndex = 0; attIndex < paramCustomAttributes.Length; attIndex++)
pb.SetCustomAttribute(paramCustomAttributes[attIndex]);
}
}
if (jsFunctionAttributes > 0){
CustomAttributeBuilder cab = new CustomAttributeBuilder(CompilerGlobals.jsFunctionAttributeConstructor, new Object[1] {jsFunctionAttributes});
if (this.mb != null)
this.mb.SetCustomAttribute(cab);
else
this.cb.SetCustomAttribute(cab);
}
// deal with custom attributes
if (this.customAttributes != null){
CustomAttributeBuilder[] custAtt = this.customAttributes.GetCustomAttributeBuilders(false);
for (int j = 0; j < custAtt.Length; j++){
if (this.mb != null)
this.mb.SetCustomAttribute(custAtt[j]);
else
this.cb.SetCustomAttribute(custAtt[j]);
}
}
if (this.clsCompliance == CLSComplianceSpec.CLSCompliant)
if (this.mb != null)
this.mb.SetCustomAttribute(new CustomAttributeBuilder(CompilerGlobals.clsCompliantAttributeCtor, new Object[]{true}));
else
this.cb.SetCustomAttribute(new CustomAttributeBuilder(CompilerGlobals.clsCompliantAttributeCtor, new Object[]{true}));
else if (this.clsCompliance == CLSComplianceSpec.NonCLSCompliant)
if (this.mb != null)
this.mb.SetCustomAttribute(new CustomAttributeBuilder(CompilerGlobals.clsCompliantAttributeCtor, new Object[]{false}));
else
this.cb.SetCustomAttribute(new CustomAttributeBuilder(CompilerGlobals.clsCompliantAttributeCtor, new Object[]{false}));
if (this.mb != null){
this.mb.InitLocals = true;
return this.mb;
}else{
this.cb.InitLocals = true;
return this.cb;
}
}
private static bool IsPresentIn(FieldInfo field, FieldInfo[] fields){
for (int i = 0, n = fields.Length; i < n; i++)
if (field == fields[i]) return true;
return false;
}
internal bool Must_save_stack_locals{
get{
if (!this.partiallyEvaluated)
this.PartiallyEvaluate();
return this.must_save_stack_locals;
}
}
internal void PartiallyEvaluate(){
if (this.partiallyEvaluated)
return;
this.partiallyEvaluated = true;
// deal with custom attributes
this.clsCompliance = CLSComplianceSpec.NotAttributed;
if (this.customAttributes != null){
this.customAttributes.PartiallyEvaluate();
CustomAttribute clsAttr = this.customAttributes.GetAttribute(Typeob.CLSCompliantAttribute);
if (clsAttr != null){
this.clsCompliance = clsAttr.GetCLSComplianceValue();
this.customAttributes.Remove(clsAttr);
}
clsAttr = this.customAttributes.GetAttribute(typeof(Override));
if (clsAttr != null){
if (this.isStatic)
clsAttr.context.HandleError(JSError.StaticMethodsCannotOverride);
else
this.attributes &= ~MethodAttributes.NewSlot;
this.noVersionSafeAttributeSpecified = false;
this.customAttributes.Remove(clsAttr);
}
clsAttr = this.customAttributes.GetAttribute(typeof(Hide));
if (clsAttr != null){
if (!this.noVersionSafeAttributeSpecified){
clsAttr.context.HandleError(JSError.OverrideAndHideUsedTogether);
this.attributes |= MethodAttributes.NewSlot;
this.noVersionSafeAttributeSpecified = true;
}else{
if (this.isStatic)
clsAttr.context.HandleError(JSError.StaticMethodsCannotHide);
this.noVersionSafeAttributeSpecified = false;
}
this.customAttributes.Remove(clsAttr);
}
CustomAttribute expAttr = this.customAttributes.GetAttribute(typeof(Expando));
if (expAttr != null){
if (!this.noVersionSafeAttributeSpecified && (this.attributes & MethodAttributes.NewSlot) == 0){
expAttr.context.HandleError(JSError.ExpandoPrecludesOverride);
this.attributes |= MethodAttributes.NewSlot;
this.noVersionSafeAttributeSpecified = true;
}
if (this.isConstructor)
expAttr.context.HandleError(JSError.NotValidForConstructor);
else if ((this.attributes & MethodAttributes.Abstract) != 0)
expAttr.context.HandleError(JSError.ExpandoPrecludesAbstract);
else if ((this.attributes & MethodAttributes.Static) != 0)
expAttr.context.HandleError(JSError.ExpandoPrecludesStatic);
else if ((this.attributes & MethodAttributes.MemberAccessMask) != MethodAttributes.Public)
expAttr.context.HandleError(JSError.ExpandoMustBePublic);
else{
this.own_scope.isMethod = false;
this.isMethod = false;
this.isExpandoMethod = true;
this.isStatic = true;
this.attributes &= ~MethodAttributes.Virtual;
this.attributes &= ~MethodAttributes.NewSlot;
this.attributes |= MethodAttributes.Static;
}
}
}
for (int i = 0, n = this.parameter_declarations.Length; i < n; i++){
this.parameter_declarations[i].PartiallyEvaluate();
JSLocalField pfield = (JSLocalField)this.own_scope.name_table[this.formal_parameters[i]];
pfield.type = this.parameter_declarations[i].type;
if (pfield.type == null)
pfield.type = new TypeExpression(new ConstantWrapper(Typeob.Object, this.parameter_declarations[i].context));
pfield.isDefined = true;
}
if (this.return_type_expr != null){
this.return_type_expr.PartiallyEvaluate();
this.own_scope.returnVar.type = this.return_type_expr;
if (this.own_scope.returnVar.type.ToIReflect() == Typeob.Void){
this.own_scope.returnVar.type = null;
this.own_scope.returnVar = null; //This does not completely get rid of the returnVar value, but acts as flag, hence the above.
}
}
globals.ScopeStack.Push(this.own_scope);
if (!this.own_scope.isKnownAtCompileTime) //Function contains an eval. Type inference is impossible for local vars
for (int i = 0, n = this.fields.Length; i < n; i++)
this.fields[i].SetInferredType(Typeob.Object, null);
if (!this.isConstructor)
this.body.PartiallyEvaluate();
else{
this.body.MarkSuperOKIfIsFirstStatement();
this.body.PartiallyEvaluate(); //super constructor calls that have not been marked OK will generate errors
ClassScope cscope = (ClassScope)this.enclosing_scope;
int n = this.superConstructorCall == null ? 0 : this.superConstructorCall.arguments.count;
Type[] argTypes = n == 0 ? Type.EmptyTypes : new Type[n];
IReflect[] argIRs = new IReflect[n];
for (int i = 0; i < n; i++)
argIRs[i] = this.superConstructorCall.arguments[i].InferType(null);
Context errorContext = this.superConstructorCall == null ? this.funcContext : this.superConstructorCall.context;
try{
if (this.superConstructorCall != null && !this.superConstructorCall.isSuperConstructorCall)
this.superConstructor = JSBinder.SelectConstructor(cscope.constructors, argIRs);
else
this.superConstructor = cscope.owner.GetSuperConstructor(argIRs);
if (this.superConstructor == null)
errorContext.HandleError(JSError.SuperClassConstructorNotAccessible);
else{
ConstructorInfo cons = (ConstructorInfo)this.superConstructor;
if (!cons.IsPublic && !cons.IsFamily &&
!(this.superConstructor is JSConstructor && ((JSConstructor)this.superConstructor).IsAccessibleFrom(this.enclosing_scope))){
errorContext.HandleError(JSError.SuperClassConstructorNotAccessible);
this.superConstructor = null;
}else if (n > 0)
if (!Binding.CheckParameters(cons.GetParameters(), argIRs, this.superConstructorCall.arguments, this.superConstructorCall.context))
this.superConstructor = null;
}
}catch(AmbiguousMatchException){
errorContext.HandleError(JSError.AmbiguousConstructorCall);
}
}
this.own_scope.HandleUnitializedVariables();
globals.ScopeStack.Pop();
this.must_save_stack_locals = this.own_scope.mustSaveStackLocals;
this.fields = this.own_scope.GetLocalFields();
}
internal IReflect ReturnType(JSField inference_target){
if (!this.partiallyEvaluated)
this.PartiallyEvaluate();
if (this.own_scope.returnVar == null)
return Typeob.Void;
else if (this.return_type_expr != null)
return this.return_type_expr.ToIReflect();
else
return this.own_scope.returnVar.GetInferredType(inference_target);
}
public override String ToString(){
if (this.text != null)
return this.text;
else
return this.funcContext.GetCode();
}
internal void TranslateBodyToIL(ILGenerator il, CompilerGlobals compilerGlobals){
this.returnLabel = il.DefineLabel();
// Emit all the namespaces used by this function
if (this.body.Engine.GenerateDebugInfo){
ScriptObject ns = this.enclosing_scope.GetParent();
while (ns != null){
if (ns is PackageScope)
il.UsingNamespace(((PackageScope)ns).name);
else if (ns is WrappedNamespace && !((WrappedNamespace)ns).name.Equals(""))
il.UsingNamespace(((WrappedNamespace)ns).name);
ns = ns.GetParent();
}
}
int startLine = this.body.context.StartLine;
int startCol = this.body.context.StartColumn;
this.body.context.document.EmitLineInfo(il, startLine, startCol, startLine, startCol + 1);
if (this.body.context.document.debugOn)
il.Emit(OpCodes.Nop);
//set up the compiler to emit direct accesses to the locals and pars.
int n = this.fields.Length;
for (int i = 0; i < n; i++){
if (!this.fields[i].IsLiteral || this.fields[i].value is FunctionObject){
Type t = this.fields[i].FieldType;
LocalBuilder tok = il.DeclareLocal(t);
if (this.fields[i].debugOn)
tok.SetLocalSymInfo(this.fields[i].debuggerName);
this.fields[i].metaData = tok;
}
}
globals.ScopeStack.Push(this.own_scope);
try{
if (this.must_save_stack_locals){
this.TranslateToMethodWithStackFrame(il, compilerGlobals, true);
return;
}
this.body.TranslateToILInitializer(il);
this.body.TranslateToIL(il, Typeob.Void);
il.MarkLabel(this.returnLabel);
}finally{
globals.ScopeStack.Pop();
}
}
internal void TranslateToIL(CompilerGlobals compilerGlobals){
if (this.suppressIL) return;
globals.ScopeStack.Push(this.own_scope);
try{
if (this.mb == null && this.cb == null)
this.GetMethodBase(compilerGlobals);
int offset = (this.attributes&MethodAttributes.Static) == MethodAttributes.Static ? 0 : 1;
int extras = 3;
if (this.isMethod)
extras = 0;
else if (!this.hasArgumentsObject)
extras = 2;
ILGenerator il = this.mb != null ? this.mb.GetILGenerator() : this.cb.GetILGenerator();
this.returnLabel = il.DefineLabel();
// Emit all the namespaces used by this function
if (this.body.Engine.GenerateDebugInfo){
ScriptObject ns = this.enclosing_scope.GetParent();
while (ns != null){
if (ns is PackageScope)
il.UsingNamespace(((PackageScope)ns).name);
else if (ns is WrappedNamespace && !((WrappedNamespace)ns).name.Equals(""))
il.UsingNamespace(((WrappedNamespace)ns).name);
ns = ns.GetParent();
}
}
if (!this.isImplicitCtor && this.body != null){
int startLine = this.body.context.StartLine;
int startCol = this.body.context.StartColumn;
this.body.context.document.EmitLineInfo(il, startLine, startCol, startLine, startCol + 1);
if (this.body.context.document.debugOn)
il.Emit(OpCodes.Nop);
}
//set up the compiler to emit direct accesses to the locals and pars.
int n = this.fields.Length;
for (int i = 0; i < n; i++){
int j = System.Array.IndexOf(this.formal_parameters, this.fields[i].Name);
if (j >= 0)
this.fields[i].metaData = (short)(j+extras+offset);
else if (this.hasArgumentsObject && this.fields[i].Name.Equals("arguments"))
this.fields[i].metaData = (short)(2+offset);
else if (!this.fields[i].IsLiteral || this.fields[i].value is FunctionObject){
Type t = this.fields[i].FieldType;
LocalBuilder tok = il.DeclareLocal(t);
if (this.fields[i].debugOn)
tok.SetLocalSymInfo(this.fields[i].debuggerName);
this.fields[i].metaData = tok;
}else if (this.own_scope.mustSaveStackLocals){ //Need to emit a field. FunctionDeclaration will initialize it with a closure
LocalBuilder tok = il.DeclareLocal(this.fields[i].FieldType);
this.fields[i].metaData = tok;
}
}
if (this.isConstructor){
int pn = formal_parameters.Length+1;
ClassScope cscope = (ClassScope)this.enclosing_scope;
//Call the default super constructor followed by the field initializers
if (this.superConstructor == null)
cscope.owner.EmitInitialCalls(il, null, null, null, 0);
else{
ParameterInfo[] pars = this.superConstructor.GetParameters();
if (this.superConstructorCall != null)
cscope.owner.EmitInitialCalls(il, this.superConstructor, pars, this.superConstructorCall.arguments, pn);
else
cscope.owner.EmitInitialCalls(il, this.superConstructor, pars, null, pn);
}
}
if ((this.isMethod || this.isConstructor) && this.must_save_stack_locals){
this.TranslateToMethodWithStackFrame(il, compilerGlobals, false);
return;
}
this.TranslateToILToCopyOuterScopeLocals(il, true, null);
bool savedInsideProtectedRegion = compilerGlobals.InsideProtectedRegion;
compilerGlobals.InsideProtectedRegion = false;
bool savedInsideFinally = compilerGlobals.InsideFinally;
int savedFinallyStackTop = compilerGlobals.FinallyStackTop;
compilerGlobals.InsideFinally = false;
this.body.TranslateToILInitializer(il);
this.body.TranslateToIL(il, Typeob.Void);
compilerGlobals.InsideProtectedRegion = savedInsideProtectedRegion;
compilerGlobals.InsideFinally = savedInsideFinally;
compilerGlobals.FinallyStackTop = savedFinallyStackTop;
il.MarkLabel(this.returnLabel);
if (this.body.context.document.debugOn){
this.EmitLastLineInfo(il);
il.Emit(OpCodes.Nop);
}
this.TranslateToILToSaveLocals(il); //put locals on heap so that nested functions can get to them
if (this.own_scope.returnVar != null)
il.Emit(OpCodes.Ldloc, (LocalBuilder)this.own_scope.returnVar.GetMetaData());
il.Emit(OpCodes.Ret);
}finally{
globals.ScopeStack.Pop();
}
}
internal void TranslateToILToLoadEngine(ILGenerator il){
this.TranslateToILToLoadEngine(il, false);
}
private void TranslateToILToLoadEngine(ILGenerator il, bool allocateLocal){
if (this.isMethod)
if (this.isStatic)
if (this.body.Engine.doCRS)
il.Emit(OpCodes.Ldsfld, CompilerGlobals.contextEngineField);
else{
if (this.engineLocal == null){
if (allocateLocal)
this.engineLocal = il.DeclareLocal(typeof(VsaEngine));
if (this.body.Engine.PEFileKind == PEFileKinds.Dll) {
il.Emit(OpCodes.Ldtoken, ((ClassScope)this.own_scope.GetParent()).GetTypeBuilder());
il.Emit(OpCodes.Call, CompilerGlobals.createVsaEngineWithType);
}else
il.Emit(OpCodes.Call, CompilerGlobals.createVsaEngine);
if (allocateLocal)
il.Emit(OpCodes.Stloc, this.engineLocal);
else
return;
}
il.Emit(OpCodes.Ldloc, this.engineLocal);
}
else{
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, CompilerGlobals.getEngineMethod);
}
else
il.Emit(OpCodes.Ldarg_1); //get it from a parameter when inside a non-method
}
private void TranslateToMethodWithStackFrame(ILGenerator il, CompilerGlobals compilerGlobals, bool staticInitializer){
//Can get here if method contains a nested function or if method contains an eval
if (this.isStatic)
il.Emit(OpCodes.Ldtoken, ((ClassScope)this.own_scope.GetParent()).GetTypeBuilder());
else
il.Emit(OpCodes.Ldarg_0);
int n = this.fields.Length;
ConstantWrapper.TranslateToILInt(il, n);
il.Emit(OpCodes.Newarr, CompilerGlobals.jsLocalFieldClass);
for (int i = 0; i < n; i++){
JSLocalField field = this.fields[i];
il.Emit(OpCodes.Dup);
ConstantWrapper.TranslateToILInt(il, i);
il.Emit(OpCodes.Ldstr, field.Name);
il.Emit(OpCodes.Ldtoken, field.FieldType);
ConstantWrapper.TranslateToILInt(il, field.slotNumber);
il.Emit(OpCodes.Newobj, CompilerGlobals.jsLocalFieldConstructor);
il.Emit(OpCodes.Stelem_Ref);
}
this.TranslateToILToLoadEngine(il, true);
if (this.isStatic)
il.Emit(OpCodes.Call, CompilerGlobals.pushStackFrameForStaticMethod);
else
il.Emit(OpCodes.Call, CompilerGlobals.pushStackFrameForMethod);
bool savedInsideProtectedRegion = compilerGlobals.InsideProtectedRegion;
compilerGlobals.InsideProtectedRegion = true;
il.BeginExceptionBlock();
this.body.TranslateToILInitializer(il);
this.body.TranslateToIL(il, Typeob.Void);
il.MarkLabel(this.returnLabel);
this.TranslateToILToSaveLocals(il); //put locals on heap so that nested functions can get to them
Label retLabel = il.DefineLabel();
il.Emit(OpCodes.Leave, retLabel);
il.BeginFinallyBlock();
this.TranslateToILToLoadEngine(il);
il.Emit(OpCodes.Call, CompilerGlobals.popScriptObjectMethod);
il.Emit(OpCodes.Pop);
il.EndExceptionBlock();
il.MarkLabel(retLabel);
if (!staticInitializer){
if (this.body.context.document.debugOn){
this.EmitLastLineInfo(il);
il.Emit(OpCodes.Nop);
}
if (this.own_scope.returnVar != null)
il.Emit(OpCodes.Ldloc, (LocalBuilder)this.own_scope.returnVar.GetMetaData());
il.Emit(OpCodes.Ret);
}
compilerGlobals.InsideProtectedRegion = savedInsideProtectedRegion;
}
//Parameters and local variables might be accessed from eval or from nested functions.
//These cannot get to the locals via IL, so the locals are saved to an array of Objects before every eval or call out of the function.
//Upon return, they are restored from the array to the locals.
internal void TranslateToILToRestoreLocals(ILGenerator il){
this.TranslateToILToRestoreLocals(il, null);
}
internal void TranslateToILToRestoreLocals(ILGenerator il, JSLocalField[] notToBeRestored){
this.TranslateToILToCopyOuterScopeLocals(il, true, notToBeRestored);
if (!this.must_save_stack_locals)
return;
int offset = (this.attributes&MethodAttributes.Static) == MethodAttributes.Static ? 0 : 1;
int extras = 3;
if (this.isMethod)
extras = 0;
else if (!this.hasArgumentsObject)
extras = 2;
int n = this.fields.Length;
this.TranslateToILToLoadEngine(il);
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
ScriptObject scope = globals.ScopeStack.Peek();
while (scope is WithObject || scope is BlockScope){
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
scope = scope.GetParent();
}
il.Emit(OpCodes.Castclass, CompilerGlobals.stackFrameClass);
il.Emit(OpCodes.Ldfld, CompilerGlobals.localVarsField);
for (int i = 0; i < n; i++){
if (notToBeRestored != null && IsPresentIn(this.fields[i], notToBeRestored)) continue;
if (this.fields[i].IsLiteral) continue;
il.Emit(OpCodes.Dup);
int j = System.Array.IndexOf(this.formal_parameters, this.fields[i].Name);
ConstantWrapper.TranslateToILInt(il, this.fields[i].slotNumber);
il.Emit(OpCodes.Ldelem_Ref);
Convert.Emit(this.body, il, Typeob.Object, this.fields[i].FieldType);
if (j >= 0 || (this.fields[i].Name.Equals("arguments") && this.hasArgumentsObject)){
il.Emit(OpCodes.Starg, (short)(j+extras+offset));
}else{
il.Emit(OpCodes.Stloc, (LocalBuilder)this.fields[i].metaData);
}
}
il.Emit(OpCodes.Pop);
}
internal void TranslateToILToSaveLocals(ILGenerator il){
this.TranslateToILToCopyOuterScopeLocals(il, false, null);
if (!this.must_save_stack_locals)
return;
int offset = (this.attributes&MethodAttributes.Static) == MethodAttributes.Static ? 0 : 1;
int extras = 3;
if (this.isMethod)
extras = 0;
else if (!this.hasArgumentsObject)
extras = 2;
int n = this.fields.Length;
this.TranslateToILToLoadEngine(il);
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
ScriptObject scope = globals.ScopeStack.Peek();
while (scope is WithObject || scope is BlockScope){
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
scope = scope.GetParent();
}
il.Emit(OpCodes.Castclass, CompilerGlobals.stackFrameClass);
il.Emit(OpCodes.Ldfld, CompilerGlobals.localVarsField);
for (int i = 0; i < n; i++){
JSLocalField f = this.fields[i];
if (f.IsLiteral && !(f.value is FunctionObject))
continue;
il.Emit(OpCodes.Dup);
ConstantWrapper.TranslateToILInt(il, f.slotNumber);
int j = System.Array.IndexOf(this.formal_parameters, f.Name);
if (j >= 0 || (f.Name.Equals("arguments") && this.hasArgumentsObject)){
Convert.EmitLdarg(il, (short)(j+extras+offset));
}else{
il.Emit(OpCodes.Ldloc, (LocalBuilder)f.metaData);
}
Convert.Emit(this.body, il, f.FieldType, Typeob.Object);
il.Emit(OpCodes.Stelem_Ref);
}
il.Emit(OpCodes.Pop);
}
//Nested functions can access the locals of their containing functions. There is no way to do this in IL.
//The work around is to copy the values of the outer scope locals to true locals at function entry and
//then to copy the modified values back at function exit.
//The outer scopes save their locals in an array before making calls, hence they are accessible.
private void TranslateToILToCopyOuterScopeLocals(ILGenerator il, bool copyToNested, JSLocalField[] notToBeRestored){
if (this.own_scope.ProvidesOuterScopeLocals == null || this.own_scope.ProvidesOuterScopeLocals.count == 0)
return;
this.TranslateToILToLoadEngine(il);
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
ScriptObject scope = globals.ScopeStack.Peek();
while (scope is WithObject || scope is BlockScope){
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
scope = scope.GetParent();
}
scope = this.enclosing_scope;
while (scope != null){
//Run through the scope chain, copying locals from outer scopes that provide them.
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
if (scope is FunctionScope && ((FunctionScope)scope).owner != null && this.own_scope.ProvidesOuterScopeLocals[scope] != null){
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Castclass, CompilerGlobals.stackFrameClass);
il.Emit(OpCodes.Ldfld, CompilerGlobals.localVarsField);
if (copyToNested)
((FunctionScope)scope).owner.TranslateToILToCopyLocalsToNestedScope(il, this.own_scope, notToBeRestored);
else
((FunctionScope)scope).owner.TranslateToILToCopyLocalsFromNestedScope(il, this.own_scope);
}else if (scope is GlobalScope || scope is ClassScope)
break;
scope = scope.GetParent();
}
il.Emit(OpCodes.Pop);
}
private void TranslateToILToCopyLocalsToNestedScope(ILGenerator il, FunctionScope nestedScope, JSLocalField[] notToBeRestored){
int offset = (this.attributes&MethodAttributes.Static) == MethodAttributes.Static ? 0 : 1;
int n = this.fields.Length;
for (int i = 0; i < n; i++){
JSLocalField field = nestedScope.GetOuterLocalField(this.fields[i].Name);
if (field == null || field.outerField != this.fields[i] || (notToBeRestored != null && IsPresentIn(field, notToBeRestored)))
continue;
il.Emit(OpCodes.Dup);
ConstantWrapper.TranslateToILInt(il, this.fields[i].slotNumber);
il.Emit(OpCodes.Ldelem_Ref);
Convert.Emit(this.body, il, Typeob.Object, this.fields[i].FieldType);
il.Emit(OpCodes.Stloc, (LocalBuilder)field.metaData);
}
il.Emit(OpCodes.Pop);
}
private void TranslateToILToCopyLocalsFromNestedScope(ILGenerator il, FunctionScope nestedScope){
int offset = (this.attributes&MethodAttributes.Static) == MethodAttributes.Static ? 0 : 1;
int n = this.fields.Length;
for (int i = 0; i < n; i++){
JSLocalField field = nestedScope.GetOuterLocalField(this.fields[i].Name);
if (field == null || field.outerField != this.fields[i])
continue;
il.Emit(OpCodes.Dup);
ConstantWrapper.TranslateToILInt(il, this.fields[i].slotNumber);
il.Emit(OpCodes.Ldloc, (LocalBuilder)field.metaData);
Convert.Emit(this.body, il, this.fields[i].FieldType, Typeob.Object);
il.Emit(OpCodes.Stelem_Ref);
}
il.Emit(OpCodes.Pop);
}
}
}