mirror of
https://github.com/SSCLI/sscli_20021101
synced 2026-06-08 12:28:57 +00:00
9fa3874800
Moved the original file to the archive subfolder.
1081 lines
51 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|