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.
319 lines
14 KiB
C#
319 lines
14 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.Reflection;
|
|
using System.Reflection.Emit;
|
|
|
|
public sealed class Try : AST{
|
|
private AST body;
|
|
private TypeExpression type;
|
|
private AST handler;
|
|
private AST finally_block;
|
|
private BlockScope handler_scope;
|
|
private FieldInfo field;
|
|
private String fieldName;
|
|
private bool finallyHasControlFlowOutOfIt;
|
|
private Context tryEndContext;
|
|
|
|
internal Try(Context context, AST body, AST identifier, TypeExpression type, AST handler, AST finally_block, bool finallyHasControlFlowOutOfIt, Context tryEndContext)
|
|
: base(context) {
|
|
this.body = body;
|
|
this.type = type;
|
|
this.handler = handler;
|
|
this.finally_block = finally_block;
|
|
ScriptObject current_scope = (ScriptObject)Globals.ScopeStack.Peek();
|
|
while (current_scope is WithObject) //Can only happen at run time and only if there is an eval
|
|
current_scope = current_scope.GetParent();
|
|
this.handler_scope = null;
|
|
this.field = null;
|
|
if (identifier != null){
|
|
this.fieldName = identifier.ToString();
|
|
this.field = current_scope.GetField(this.fieldName, BindingFlags.Public|BindingFlags.Instance|BindingFlags.Static);
|
|
if (this.field != null){
|
|
if (type == null && (field is JSVariableField && field.IsStatic && ((JSVariableField)field).type == null) && !field.IsLiteral && !field.IsInitOnly)
|
|
return; //preserve legacy semantics by using the existing variable
|
|
if (((IActivationObject)current_scope).GetLocalField(this.fieldName) != null)
|
|
identifier.context.HandleError(JSError.DuplicateName, false);
|
|
}
|
|
this.handler_scope = new BlockScope(current_scope);
|
|
this.handler_scope.catchHanderScope = true;
|
|
JSVariableField f = this.handler_scope.AddNewField(identifier.ToString(), Missing.Value, FieldAttributes.Public); // must be a local
|
|
this.field = f; f.originalContext = identifier.context;
|
|
if (identifier.context.document.debugOn && this.field is JSLocalField){
|
|
this.handler_scope.AddFieldForLocalScopeDebugInfo((JSLocalField)this.field);
|
|
}
|
|
}
|
|
this.finallyHasControlFlowOutOfIt = finallyHasControlFlowOutOfIt;
|
|
this.tryEndContext = tryEndContext;
|
|
}
|
|
|
|
internal override Object Evaluate(){
|
|
int i = Globals.ScopeStack.Size();
|
|
int j = Globals.CallContextStack.Size();
|
|
Completion bc = null;
|
|
Completion fc = null;
|
|
try{
|
|
try{
|
|
bc = (Completion)this.body.Evaluate();
|
|
}catch(Exception e){
|
|
if (this.handler == null) throw;
|
|
Object eValue = e;
|
|
if (this.type != null){
|
|
Type ht = this.type.ToType();
|
|
if (typeof(Exception).IsAssignableFrom(ht)){
|
|
if (!ht.IsInstanceOfType(e)) throw;
|
|
}else if (!ht.IsInstanceOfType(eValue = JScriptExceptionValue(e, this.Engine)))
|
|
throw;
|
|
}else
|
|
eValue = JScriptExceptionValue(e, this.Engine);
|
|
|
|
Globals.ScopeStack.TrimToSize(i);
|
|
Globals.CallContextStack.TrimToSize(j);
|
|
if (this.handler_scope != null){
|
|
this.handler_scope.SetParent(Globals.ScopeStack.Peek());
|
|
Globals.ScopeStack.Push(this.handler_scope);
|
|
}
|
|
if (this.field != null)
|
|
this.field.SetValue(Globals.ScopeStack.Peek(), eValue);
|
|
bc = (Completion)this.handler.Evaluate();
|
|
}
|
|
}finally{
|
|
Globals.ScopeStack.TrimToSize(i);
|
|
Globals.CallContextStack.TrimToSize(j);
|
|
if (this.finally_block != null){
|
|
fc = (Completion)this.finally_block.Evaluate();
|
|
}
|
|
}
|
|
if (bc == null || fc != null && (fc.Exit > 0 || fc.Continue > 0 || fc.Return))
|
|
bc = fc;
|
|
else{
|
|
if (fc != null && fc.value is Missing)
|
|
bc.value = fc.value;
|
|
}
|
|
Completion result = new Completion();
|
|
result.Continue = bc.Continue - 1;
|
|
result.Exit = bc.Exit - 1;
|
|
result.Return = bc.Return;
|
|
result.value = bc.value;
|
|
return result;
|
|
}
|
|
|
|
internal override Context GetFirstExecutableContext(){
|
|
return this.body.GetFirstExecutableContext();
|
|
}
|
|
|
|
public static Object JScriptExceptionValue(Object e, VsaEngine engine){
|
|
if (engine == null){
|
|
engine = new VsaEngine(true);
|
|
engine.InitVsaEngine("JS7://Microsoft.JScript.Vsa.VsaEngine", new DefaultVsaSite());
|
|
}
|
|
ErrorConstructor originalError = engine.Globals.globalObject.originalError;
|
|
if (e is JScriptException){
|
|
Object value = ((JScriptException)e).value;
|
|
if (value is Exception || value is Missing || (((JScriptException)e).Number&0xFFFF) != (int)JSError.UncaughtException)
|
|
return originalError.Construct((Exception)e);
|
|
return value; //The exception wraps a non-exception value
|
|
}else if (e is StackOverflowException)
|
|
return originalError.Construct(new JScriptException(JSError.OutOfStack));
|
|
else if (e is OutOfMemoryException)
|
|
return originalError.Construct(new JScriptException(JSError.OutOfMemory));
|
|
return originalError.Construct(e);
|
|
}
|
|
|
|
internal override AST PartiallyEvaluate(){
|
|
if (this.type != null){
|
|
this.type.PartiallyEvaluate();
|
|
((JSVariableField)this.field).type = type;
|
|
}else if (this.field is JSLocalField)
|
|
((JSLocalField)this.field).SetInferredType(Typeob.Object, null); //This should never give an error
|
|
ScriptObject current_scope = Globals.ScopeStack.Peek();
|
|
while (current_scope is WithObject) current_scope = current_scope.GetParent();
|
|
FunctionScope scope = null;
|
|
BitArray before = null;
|
|
if (current_scope is FunctionScope){
|
|
scope = (FunctionScope)current_scope;
|
|
before = scope.DefinedFlags;
|
|
}
|
|
this.body = this.body.PartiallyEvaluate();
|
|
if (this.handler != null){
|
|
if (this.handler_scope != null)
|
|
Globals.ScopeStack.Push(this.handler_scope);
|
|
if (this.field is JSLocalField)
|
|
((JSLocalField)this.field).isDefined = true;
|
|
this.handler = this.handler.PartiallyEvaluate();
|
|
if (this.handler_scope != null)
|
|
Globals.ScopeStack.Pop();
|
|
}
|
|
if (this.finally_block != null)
|
|
this.finally_block = this.finally_block.PartiallyEvaluate();
|
|
if (scope != null)
|
|
scope.DefinedFlags = before;
|
|
return this;
|
|
}
|
|
|
|
public static void PushHandlerScope(VsaEngine engine, String id, int scopeId){
|
|
engine.PushScriptObject(new BlockScope(engine.ScriptObjectStackTop(), id, scopeId));
|
|
}
|
|
|
|
internal override void TranslateToIL(ILGenerator il, Type rtype){
|
|
//This assumes that rtype == Void.class.
|
|
bool savedInsideProtectedRegion = compilerGlobals.InsideProtectedRegion;
|
|
compilerGlobals.InsideProtectedRegion = true;
|
|
compilerGlobals.BreakLabelStack.Push(compilerGlobals.BreakLabelStack.Peek(0));
|
|
compilerGlobals.ContinueLabelStack.Push(compilerGlobals.ContinueLabelStack.Peek(0));
|
|
il.BeginExceptionBlock();
|
|
if (this.finally_block != null){
|
|
if (this.finallyHasControlFlowOutOfIt)
|
|
il.BeginExceptionBlock();
|
|
if (this.handler != null)
|
|
il.BeginExceptionBlock();
|
|
}
|
|
this.body.TranslateToIL(il, Typeob.Void);
|
|
if (this.tryEndContext != null)
|
|
this.tryEndContext.EmitLineInfo(il);
|
|
if (this.handler != null){
|
|
if (this.type == null){
|
|
il.BeginCatchBlock(typeof(Exception));
|
|
this.handler.context.EmitLineInfo(il);
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.jScriptExceptionValueMethod);
|
|
}else{
|
|
Type filterType = this.type.ToType();
|
|
if (typeof(Exception).IsAssignableFrom(filterType)){
|
|
il.BeginCatchBlock(filterType);
|
|
this.handler.context.EmitLineInfo(il);
|
|
}
|
|
else{
|
|
il.BeginExceptFilterBlock();
|
|
this.handler.context.EmitLineInfo(il);
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.jScriptExceptionValueMethod);
|
|
il.Emit(OpCodes.Isinst, filterType);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.Emit(OpCodes.Cgt_Un);
|
|
il.BeginCatchBlock(null);
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.jScriptExceptionValueMethod);
|
|
Convert.Emit(this, il, Typeob.Object, filterType);
|
|
}
|
|
}
|
|
Object tok = this.field is JSVariableField ? ((JSVariableField)this.field).GetMetaData() : this.field;
|
|
if (tok is LocalBuilder)
|
|
il.Emit(OpCodes.Stloc, (LocalBuilder)tok);
|
|
else if (tok is FieldInfo)
|
|
il.Emit(OpCodes.Stsfld, (FieldInfo)tok);
|
|
else
|
|
Convert.EmitLdarg(il, (short)tok);
|
|
if (this.handler_scope != null){
|
|
if (!this.handler_scope.isKnownAtCompileTime){ //I.e. eval or nested func
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Ldstr, this.fieldName);
|
|
ConstantWrapper.TranslateToILInt(il, this.handler_scope.scopeId);
|
|
il.Emit(OpCodes.Call, typeof(Try).GetMethod("PushHandlerScope"));
|
|
Globals.ScopeStack.Push(this.handler_scope);
|
|
}
|
|
il.BeginScope(); // so that we can emit local scoped information for the handler variable
|
|
if (this.context.document.debugOn)
|
|
this.handler_scope.EmitLocalInfoForFields(il);
|
|
}
|
|
this.handler.TranslateToIL(il, Typeob.Void);
|
|
if (this.handler_scope != null){
|
|
il.EndScope();
|
|
if (!this.handler_scope.isKnownAtCompileTime){ //I.e. eval or nested func
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.popScriptObjectMethod);
|
|
il.Emit(OpCodes.Pop);
|
|
Globals.ScopeStack.Pop();
|
|
}
|
|
}
|
|
il.EndExceptionBlock();
|
|
}
|
|
if (this.finally_block != null){
|
|
bool savedInsideFinally = compilerGlobals.InsideFinally;
|
|
int savedFinallyStackTop = compilerGlobals.FinallyStackTop;
|
|
compilerGlobals.InsideFinally = true;
|
|
compilerGlobals.FinallyStackTop = compilerGlobals.BreakLabelStack.Size();
|
|
il.BeginFinallyBlock();
|
|
this.finally_block.TranslateToIL(il, Typeob.Void);
|
|
il.EndExceptionBlock();
|
|
compilerGlobals.InsideFinally = savedInsideFinally;
|
|
compilerGlobals.FinallyStackTop = savedFinallyStackTop;
|
|
if (this.finallyHasControlFlowOutOfIt){
|
|
il.BeginCatchBlock(typeof(BreakOutOfFinally));
|
|
il.Emit(OpCodes.Ldfld, typeof(BreakOutOfFinally).GetField("target"));
|
|
// don't need to go to 0 in the loop because 0 is the outmost block (i.e. function body)
|
|
// and that would generate a JIT assert because the jump is sometimes outside the function
|
|
for (int i = compilerGlobals.BreakLabelStack.Size()-1, n = i; i > 0; i--){
|
|
il.Emit(OpCodes.Dup);
|
|
ConstantWrapper.TranslateToILInt(il, i);
|
|
Label lab = il.DefineLabel();
|
|
il.Emit(OpCodes.Blt_S, lab);
|
|
il.Emit(OpCodes.Pop);
|
|
if (savedInsideFinally && i < savedFinallyStackTop)
|
|
il.Emit(OpCodes.Rethrow);
|
|
else
|
|
il.Emit(OpCodes.Leave, (Label)compilerGlobals.BreakLabelStack.Peek(n-i));
|
|
il.MarkLabel(lab);
|
|
}
|
|
il.Emit(OpCodes.Pop);
|
|
il.BeginCatchBlock(typeof(ContinueOutOfFinally));
|
|
il.Emit(OpCodes.Ldfld, typeof(ContinueOutOfFinally).GetField("target"));
|
|
// don't need to go to 0 in the loop because 0 is the outmost block (i.e. function body)
|
|
for (int i = compilerGlobals.ContinueLabelStack.Size()-1, n = i; i > 0; i--){
|
|
il.Emit(OpCodes.Dup);
|
|
ConstantWrapper.TranslateToILInt(il, i);
|
|
Label lab = il.DefineLabel();
|
|
il.Emit(OpCodes.Blt_S, lab);
|
|
il.Emit(OpCodes.Pop);
|
|
if (savedInsideFinally && i < savedFinallyStackTop)
|
|
il.Emit(OpCodes.Rethrow);
|
|
else
|
|
il.Emit(OpCodes.Leave, (Label)compilerGlobals.ContinueLabelStack.Peek(n-i));
|
|
il.MarkLabel(lab);
|
|
}
|
|
il.Emit(OpCodes.Pop);
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
while (scope != null && !(scope is FunctionScope))
|
|
scope = scope.GetParent();
|
|
if (scope != null && !savedInsideFinally){
|
|
il.BeginCatchBlock(typeof(ReturnOutOfFinally));
|
|
il.Emit(OpCodes.Pop);
|
|
il.Emit(OpCodes.Leave, ((FunctionScope)scope).owner.returnLabel);
|
|
}
|
|
il.EndExceptionBlock();
|
|
}
|
|
}
|
|
compilerGlobals.InsideProtectedRegion = savedInsideProtectedRegion;
|
|
compilerGlobals.BreakLabelStack.Pop();
|
|
compilerGlobals.ContinueLabelStack.Pop();
|
|
}
|
|
|
|
internal override void TranslateToILInitializer(ILGenerator il){
|
|
this.body.TranslateToILInitializer(il);
|
|
if (this.handler != null)
|
|
this.handler.TranslateToILInitializer(il);
|
|
if (this.finally_block != null)
|
|
this.finally_block.TranslateToILInitializer(il);
|
|
}
|
|
|
|
}
|
|
|
|
}
|