public ReadIdentifierExpression(JSSymbol symbol, int offset = 0) :base(symbol) { SourceOffset = offset; symbol.Readers.Add(this); }
public VariableDeclaration(JSSymbol identifier, WriteIdentifierExpression initialization) { Symbol = identifier; Initialization = initialization; Use(Initialization); }
internal LocalBuilder Get(JSSymbol symbol) { LocalBuilder local; _symbolLocals.TryGetValue(symbol, out local); return(local); }
internal LocalBuilder Declare(LocalBuilder local, JSSymbol symbol) { //Get(symbol) is not cheap, we want to write the following ASSERT in a way that it goes away in release Debug.Assert(Get(symbol) == null, "Symbol {0} already has a local variable of type {1}", symbol.Name, ((Get(symbol) != null) ? Get(symbol).LocalType.ToString() : "")); _symbolLocals.Add(symbol, local); return(local); }
public void Execute(JSFunctionMetadata funcMetadata) { if (funcMetadata.IsAnalyzed) { return; } if (!funcMetadata.Scope.IsEvalFunction) { return; //This is not the body in an eval() } _currFuncMetadata = funcMetadata; _implicitReturn = _currFuncMetadata.Scope.GetOrAddSymbol("_implicit_eval_return"); _implicitReturn.SymbolType = JSSymbol.SymbolTypes.HiddenLocal; _currBlock = null; _currStatementIndex = -1; _currExpressionStatement = null; VisitNode(_currFuncMetadata.FunctionIR.Statement); _currFuncMetadata.FunctionIR.Statement.Statements.Add( new ReturnStatement( new ReadIdentifierExpression(_implicitReturn) ) ); }
public ReadIdentifierExpression(JSSymbol symbol, int offset = 0) : base(symbol) { SourceOffset = offset; symbol.Readers.Add(this); }
private void Release(JSSymbol symbol) { //Technically we should never call this function, it will be unsafe! var local = Get(symbol); Debug.Assert(local != null, "Symbol {0} does not have a local variable assigned to", symbol.Name); _symbolLocals.Remove(symbol); Release(symbol.Name); }
public WriteIdentifierExpression(JSSymbol symbol, Expression value) : base(symbol) { Debug.Assert(value != null, "Value cannot be null"); symbol.Writers.Add(this); Value = value; SourceOffset = value.SourceOffset; Use(Value); }
protected override void DeclareNonParamSymbol(JSSymbol symbol, mdr.ValueTypes symbolValueType, LocalBuilder contextMap) { if (symbol.SymbolType == JSSymbol.SymbolTypes.Global && _currFuncMetadata.EnableInlineCache) { if (_globalContext == null) { _globalContext = _localVars.Declare(Types.DObject.TypeOf, "__gc"); _ilGen.LoadRuntimeInstance(); _ilGen.Ldfld(Types.Runtime.GlobalContext); _ilGen.Stloc(_globalContext); } } else base.DeclareNonParamSymbol(symbol, symbolValueType, contextMap); }
/// <summary> /// In javascript all "var" declarations must be hoisted to the top of the function. /// in other words, they are local symbols of the function scope, even if they are /// defined in any inner scope. /// this function will take care of that. /// </summary> void DeclareHoistedLocal(JSSymbol symbol) { var scope = symbol.ContainerScope; if (scope.IsFunction) { symbol.SymbolType = JSSymbol.SymbolTypes.Local; } else { while (!scope.IsFunction) scope = scope.OuterScope; var resolvedSymbol = scope.GetOrAddSymbol(symbol.Name); resolvedSymbol.SymbolType = JSSymbol.SymbolTypes.Local; symbol.SymbolType = JSSymbol.SymbolTypes.OuterDuplicate; symbol.ResolvedSymbol = resolvedSymbol; } }
protected override void DeclareNonParamSymbol(JSSymbol symbol, mdr.ValueTypes symbolValueType, LocalBuilder contextMap) { if (symbol.SymbolType == JSSymbol.SymbolTypes.Global && _currFuncMetadata.EnableInlineCache) { if (_globalContext == null) { _globalContext = _localVars.Declare(Types.DObject.TypeOf, "__gc"); _ilGen.LoadRuntimeInstance(); _ilGen.Ldfld(Types.Runtime.GlobalContext); _ilGen.Stloc(_globalContext); } } else { base.DeclareNonParamSymbol(symbol, symbolValueType, contextMap); } }
void InlineInvocation(CallExpression invocation, JSFunctionMetadata targetFuncMetadata) { _targetFuncMetadata = targetFuncMetadata; _call = invocation; Debug.WriteLine("Trying to inline function {0}", _targetFuncMetadata.Declaration); _targetFuncMetadata.Analyze(); //Just to make sure it is analyzed if (JSRuntime.Instance.Configuration.ProfileStats) { JSRuntime.Instance.Counters.GetCounter("Attempted Inline").Count++; } if (!CanInline(_targetFuncMetadata)) { return; } if (JSRuntime.Instance.Configuration.ProfileStats) { JSRuntime.Instance.Counters.GetCounter("Succeeded Inline").Count++; } _functionsBeingInlined.AddLast(_targetFuncMetadata); _round++; throw new NotImplementedException(); //TODO: we need to update this algorithm based on the recent changes to the scope _newScope = new Scope(_currentTargetScope); _returnValueSymbol = _newScope.AddSymbol(RenameSymbol("retVal")); _returnValueSymbol.SymbolType = JSSymbol.SymbolTypes.HiddenLocal; _call.InlinedIR = new InlinedInvocation( _targetFuncMetadata , _newScope , BuildInlinedBody() , new ReadIdentifierExpression(_returnValueSymbol) ); _call.InlinedIR.AddUser(_call); Debug.WriteLine("Inlined function {0}", _targetFuncMetadata.Declaration); _functionsBeingInlined.RemoveLast(); }
public override void Visit(ForEachInStatement node) { JSSymbol symbol = null; var vds = node.Initialization as VariableDeclarationStatement; if (vds != null && vds.Declarations.Count > 0) { symbol = vds.Declarations[0].Symbol;// _cgInfo.FuncMetadata.GetSymbol(vds.Identifier); } else { //TODO: Make sure that other cases are covered! } if (symbol != null) { SetType(symbol, mdr.ValueTypes.String); } }
/// <summary> /// In javascript all "var" declarations must be hoisted to the top of the function. /// in other words, they are local symbols of the function scope, even if they are /// defined in any inner scope. /// this function will take care of that. /// </summary> void DeclareHoistedLocal(JSSymbol symbol) { var scope = symbol.ContainerScope; if (scope.IsFunction) { symbol.SymbolType = JSSymbol.SymbolTypes.Local; } else { while (!scope.IsFunction) { scope = scope.OuterScope; } var resolvedSymbol = scope.GetOrAddSymbol(symbol.Name); resolvedSymbol.SymbolType = JSSymbol.SymbolTypes.Local; symbol.SymbolType = JSSymbol.SymbolTypes.OuterDuplicate; symbol.ResolvedSymbol = resolvedSymbol; } }
private JSSymbol GetRenamedSymbolOf(JSSymbol s) { JSSymbol newSymbol = null; switch (s.SymbolType) { case JSSymbol.SymbolTypes.Local: newSymbol = _newScope.GetOrAddSymbol(RenameSymbol(s.Name)); newSymbol.SymbolType = JSSymbol.SymbolTypes.Local; break; case JSSymbol.SymbolTypes.Global: newSymbol = _newScope.GetOrAddSymbol(s.Name); newSymbol.SymbolType = JSSymbol.SymbolTypes.Global; break; default: Trace.Fail("Cannot support symbol type {0}", s.SymbolType); break; } return(newSymbol); }
void UpdateType(JSSymbol symbol, mdr.ValueTypes type) { if (type == mdr.ValueTypes.Unknown) { return; } var oldType = GetType(symbol); var newType = TypeCalculator.ResolveType(oldType, type); switch (newType) { case mdr.ValueTypes.Undefined: case mdr.ValueTypes.Null: case mdr.ValueTypes.Unknown: newType = mdr.ValueTypes.DValueRef; //All of these cases are handled by a DValue break; case mdr.ValueTypes.DValue: if (symbol.IsParameter) { //We already have a DValue as argument, so just need to point to that and use its storage newType = mdr.ValueTypes.DValueRef; } break; } if (oldType != newType) { m.Util.Diagnose.Debug.WriteLine("---->> Type of the symbol {0} changed to {1}", symbol.Name.ToString(), newType.ToString()); SetType(symbol, newType); //symbol.Types.Add(type); foreach (var r in symbol.Readers) { Enqueue(r); } } }
public void Execute(JSFunctionMetadata funcMetadata) { if (funcMetadata.IsAnalyzed) return; if (!funcMetadata.Scope.IsEvalFunction) return; //This is not the body in an eval() _currFuncMetadata = funcMetadata; _implicitReturn = _currFuncMetadata.Scope.GetOrAddSymbol("_implicit_eval_return"); _implicitReturn.SymbolType = JSSymbol.SymbolTypes.HiddenLocal; _currBlock = null; _currStatementIndex = -1; _currExpressionStatement = null; VisitNode(_currFuncMetadata.FunctionIR.Statement); _currFuncMetadata.FunctionIR.Statement.Statements.Add( new ReturnStatement( new ReadIdentifierExpression(_implicitReturn) ) ); }
public Identifier(JSSymbol symbol) { Debug.Assert(symbol != null, "Symbold cannot be null"); Symbol = symbol; }
private JSSymbol GetRenamedSymbolOf(JSSymbol s) { JSSymbol newSymbol = null; switch (s.SymbolType) { case JSSymbol.SymbolTypes.Local: newSymbol = _newScope.GetOrAddSymbol(RenameSymbol(s.Name)); newSymbol.SymbolType = JSSymbol.SymbolTypes.Local; break; case JSSymbol.SymbolTypes.Global: newSymbol = _newScope.GetOrAddSymbol(s.Name); newSymbol.SymbolType = JSSymbol.SymbolTypes.Global; break; default: Trace.Fail("Cannot support symbol type {0}", s.SymbolType); break; } return newSymbol; }
public mdr.ValueTypes GetType(JSSymbol symbol) { return symbol.ValueType; }
internal LocalBuilder Declare(LocalBuilder local, JSSymbol symbol) { //Get(symbol) is not cheap, we want to write the following ASSERT in a way that it goes away in release Debug.Assert(Get(symbol) == null, "Symbol {0} already has a local variable of type {1}", symbol.Name, ((Get(symbol) != null) ? Get(symbol).LocalType.ToString() : "")); _symbolLocals.Add(symbol, local); return local; }
public mdr.ValueTypes GetType(JSSymbol symbol) { return(_cgInfo.GetType(symbol)); }
//############################################################################################################################### // Symbols' local handling internal LocalBuilder Declare(mdr.ValueTypes type, JSSymbol symbol) { return Declare(Types.TypeOf(type), symbol); }
public mdr.ValueTypes GetType(JSSymbol symbol) { return(symbol.ValueType); }
protected virtual void DeclareNonParamSymbol(JSSymbol symbol, mdr.ValueTypes symbolValueType, LocalBuilder contextMap) { Debug.Assert(!symbol.IsParameter, "This function must not be called for parameter symbols"); var scope = symbol.ContainerScope; switch (symbol.SymbolType) { case JSSymbol.SymbolTypes.HiddenLocal: case JSSymbol.SymbolTypes.Local: { switch (symbolValueType) { case mdr.ValueTypes.Unknown: case mdr.ValueTypes.DValueRef: case mdr.ValueTypes.DValue: _localVars.Declare(Types.DValue.TypeOf, symbol); break; default: _localVars.Declare(Types.TypeOf(symbolValueType), symbol); break; } break; } case JSSymbol.SymbolTypes.ClosedOnLocal: { Debug.Assert(scope.IsFunction, "closed on symbols are only supported at the function level at this time"); Debug.Assert(symbolValueType == mdr.ValueTypes.Unknown || symbolValueType == mdr.ValueTypes.DValueRef, "Invalid symbol type {0} for symbol {1}", symbolValueType, symbol.Name); symbol.AssignFieldId(); ///It is guaranteed that we will have the property in the _context if (scope.HasEval || scope.IsEvalFunction) { ///In this case, the _context.Fields may change; so we cannot hold on to its refrences! _ilGen.Ldloc(contextMap); _ilGen.Ldc_I4(symbol.FieldId); _ilGen.Call(Types.PropertyMap.GetPropertyDescriptorByFieldId); _localVars.Declare(Types.PropertyDescriptor.TypeOf, symbol); } else { ///In this case, as an optmization, we can just store the ref to the actual field in the context _ilGen.Ldloc(_context); _ilGen.Ldfld(Types.DObject.Fields); _ilGen.Ldloc(contextMap); _ilGen.Ldc_I4(symbol.FieldId); _ilGen.Call(Types.PropertyMap.GetPropertyDescriptorByFieldId); _ilGen.Ldfld(Types.PropertyDescriptor.Index); _ilGen.Ldelema(Types.DValue.TypeOf); _localVars.Declare(Types.DValue.RefOf, symbol); } _ilGen.Stloc(_localVars.Get(symbol)); break; } case JSSymbol.SymbolTypes.ParentLocal: { Debug.Assert(symbolValueType == mdr.ValueTypes.Unknown || symbolValueType == mdr.ValueTypes.DValueRef, "Invalid symbol type {0} for symbol {1}", symbolValueType, symbol.Name); symbol.AssignFieldId(); _ilGen.Ldloc(contextMap); _ilGen.Ldc_I4(symbol.FieldId); _ilGen.Call(Types.PropertyMap.GetPropertyDescriptorByFieldId); //We know this will return an inherited PD var closedOnScope = symbol.ResolvedSymbol.ContainerScope.ContainerFunction.FunctionIR.Scope; if (true || ////TODO: this optimizations is disabled for now to find a better approach! closedOnScope.HasEval //eval can make any changs to the context, so no clever trick || closedOnScope.IsEvalFunction //eval depends on the caller function's context which is not known at compile time ) { //In this case, the .Fields of the context may change, so we cannot rely on the references _localVars.Declare(Types.PropertyDescriptor.TypeOf, symbol); _ilGen.Stloc(_localVars.Get(symbol)); } else { //In this case, we can do the lookup once, and then just use the reference to the symbol's storage var tmp = _localVars.PushTemporary(Types.PropertyDescriptor.TypeOf); _ilGen.Stloc(tmp); var loadContainer = _ilGen.DefineLabel(); var loadedContainer = _ilGen.DefineLabel(); _ilGen.Ldloc(tmp); _ilGen.Call(Types.PropertyDescriptor.IsInherited); _ilGen.Brtrue(loadContainer); _ilGen.Ldloc(_context); _ilGen.Br(loadedContainer); _ilGen.MarkLabel(loadContainer); _ilGen.Ldloc(tmp); _ilGen.Ldfld(Types.PropertyDescriptor.Container); _ilGen.MarkLabel(loadedContainer); _ilGen.Ldfld(Types.DObject.Fields); _ilGen.Ldloc(tmp); _ilGen.Ldfld(Types.PropertyDescriptor.Index); _ilGen.Ldelema(Types.DValue.TypeOf); _localVars.Declare(Types.DValue.RefOf, symbol); _localVars.PopTemporary(tmp); _ilGen.Stloc(_localVars.Get(symbol)); } break; } case JSSymbol.SymbolTypes.Global: { ///We know it is in the global state already, but we cannot use the reference bacause: ///- .Fields array may change during execution ///- the actuall property have accessor in the global scope. Debug.Assert(symbolValueType == mdr.ValueTypes.Unknown || symbolValueType == mdr.ValueTypes.DValueRef, "Invalid symbol type {0} for symbol {1}", symbolValueType, symbol.Name); symbol.AssignFieldId(); //The following case is ready taken care of with the OuterDuplicate case //var globalSymbol = // (scope != _currFuncMetadata.Scope) // ? _currFuncMetadata.Scope.GetSymbol(symbol.Name) // : null; //if (globalSymbol != null) //{ // //It should be already initialized, and we don't need to do it. // var globalVar = _localVars.Get(globalSymbol); // Debug.Assert(globalVar != null, "Invalid situation, global symbol {0} must already have been declared", globalSymbol.Name); // _localVars.Declare(globalVar, symbol); // break; //} //else _localVars.Declare(Types.PropertyDescriptor.TypeOf, symbol); _ilGen.LoadRuntimeInstance(); _ilGen.Ldfld(Types.Runtime.GlobalContext); _ilGen.Ldc_I4(symbol.FieldId); _ilGen.Call(Types.DObject.GetPropertyDescriptorByFieldId); _ilGen.Stloc(_localVars.Get(symbol)); break; } case JSSymbol.SymbolTypes.Arguments: { if (symbolValueType == mdr.ValueTypes.Array) { ///In this case, the arguments symbols must have been only read from Debug.Assert(_arguments != null, "Invalid situation, the _arguments variable must be already assigned"); var local = _localVars.Declare(Types.DArray.TypeOf, symbol); _ilGen.Ldloc(_arguments); _ilGen.Stloc(local); } else if (symbolValueType == mdr.ValueTypes.DValueRef || symbolValueType == mdr.ValueTypes.Unknown) { ///In this case, we either don't know the type, or there has been assignments to the arguments symbol var local = _localVars.Declare(Types.DValue.TypeOf, symbol); _ilGen.Ldloca(local); _ilGen.Ldloc(_arguments); _ilGen.Call(Types.DValue.Set.Get(mdr.ValueTypes.Object)); } else { Debug.Fail("Invalid symbol type {0} for 'arguments'", symbolValueType); } break; } case JSSymbol.SymbolTypes.Unknown: { ///This symbol could not be resolved during analysis, and hence can be added to the context chain at runtime ///The context chain is: func.Context->func.Parent.Context->...GlobalContext->Object.prototype ///Therefore we should generate code to look it up at the access site Debug.Assert(symbolValueType == mdr.ValueTypes.Unknown || symbolValueType == mdr.ValueTypes.DValueRef, "Invalid symbol type"); symbol.AssignFieldId(); break; } case JSSymbol.SymbolTypes.OuterDuplicate: //we don't need to do anything since the outer symbols is going to be used. break; default: Trace.Fail("cannot process symbol type {0} in {1}", symbol.SymbolType, _currFuncMetadata.FullName); break; } }
public mdr.ValueTypes GetType(JSSymbol symbol) { return(SymbolTypes[symbol]); }
public void SetType(JSSymbol symbol, mdr.ValueTypes type) { SymbolTypes[symbol] = type; }
void WriteResults(JSSymbol symbol, bool pushBackResult) { switch (symbol.SymbolType) { case JSSymbol.SymbolTypes.Unknown: case JSSymbol.SymbolTypes.Local: case JSSymbol.SymbolTypes.ClosedOnLocal: case JSSymbol.SymbolTypes.ParentLocal: case JSSymbol.SymbolTypes.Global: case JSSymbol.SymbolTypes.Arguments: if (symbol.IsParameter && symbol.SymbolType != JSSymbol.SymbolTypes.ClosedOnLocal) { Ldarg_CallFrame(); _ilGen.Ldc_I4(symbol.ParameterIndex); _ilGen.Ldc_I4(pushBackResult); Call(Types.Operations.Stack.StoreArg, 1, pushBackResult ? 1 : 0); } else { _ilGen.Ldloc(_context); _ilGen.Ldc_I4(symbol.FieldId); //_ilGen.Ldc_I4(symbol.AncestorDistance); _ilGen.Ldc_I4(pushBackResult); Call(Types.Operations.Stack.StoreVariable, 1, pushBackResult ? 1 : 0); } break; case JSSymbol.SymbolTypes.HiddenLocal: _ilGen.Ldloca(_localVars.Get(symbol)); if (pushBackResult) LoadStackItem(-1); else LoadStackPop(); _ilGen.Call(Types.DValue.Set.Get(mdr.ValueTypes.DValueRef)); break; default: Trace.Fail("Cannot write to symbol {0}:{1}", symbol.Name, symbol.SymbolType); break; } }
public void SetType(JSSymbol symbol, mdr.ValueTypes type) { _cgInfo.SetType(symbol, type); }
public mdr.ValueTypes GetType(JSSymbol symbol) { return SymbolTypes[symbol]; }
private IIdentifier MakeIdentifier(JSSymbol symbol, int offset) { return(MakeReadIdentifierExpression(symbol, offset)); }
private ReadIdentifierExpression MakeReadIdentifierExpression(JSSymbol symbol, int offset) { return(new ReadIdentifierExpression(symbol, offset)); }
private static void ResolveMethod(JSSymbol symbol) { var containerFunction = symbol.ContainerScope.ContainerFunction; bool shouldResolveAtRuntime = false; for (var outerScope = symbol.ContainerScope.OuterScope; outerScope != null; outerScope = outerScope.OuterScope) { var resolvedSymbol = outerScope.GetSymbol(symbol.Name); if (resolvedSymbol != null) { if (outerScope.ContainerFunction == containerFunction) { ///We just need to know everywhere instead of symbol, we should refer to resolved symbol ///the rest will be taken care of as we keep analyzing the upper scopes symbol.SymbolType = JSSymbol.SymbolTypes.OuterDuplicate; if (resolvedSymbol.SymbolType == JSSymbol.SymbolTypes.OuterDuplicate) { Debug.Assert(resolvedSymbol.ResolvedSymbol != null, "symbol {0} must have already been resolved!"); symbol.ResolvedSymbol = resolvedSymbol.ResolvedSymbol; } else { symbol.ResolvedSymbol = resolvedSymbol; } } else { //here we are sure the symbol is found in a parent function scope switch (resolvedSymbol.SymbolType) { case JSSymbol.SymbolTypes.Local: case JSSymbol.SymbolTypes.ClosedOnLocal: symbol.ResolvedSymbol = resolvedSymbol; if (outerScope.IsProgram) { symbol.SymbolType = JSSymbol.SymbolTypes.Global; } else { symbol.SymbolType = JSSymbol.SymbolTypes.ParentLocal; } symbol.ContainerScope.HasParentLocalSymbol = true; //We need this anyways since we want to know if a context is needed at runtime at all lock (resolvedSymbol) //In case sub-functions are processed in parallel { resolvedSymbol.SymbolType = JSSymbol.SymbolTypes.ClosedOnLocal; resolvedSymbol.NonLocalWritersCount += symbol.Writers.Count; resolvedSymbol.AssignFieldId(); } symbol.AssignFieldId(); break; case JSSymbol.SymbolTypes.Unknown: if (outerScope.HasEval) { shouldResolveAtRuntime = true; outerScope = null; //to stop the loop } //we will take care of this after this loop break; case JSSymbol.SymbolTypes.ParentLocal: ///We could have made a shortcut here and setup the symbol info, but we would still need to walk up until we find the actual symbol declaration ///So, the optimization here would be just not calling GetSymbol int some parent functions. ///To simplify code, for now, we just ignore this until we reach the declared symbol Debug.Assert( !outerScope.HasEval && !outerScope.IsProgram && resolvedSymbol.ResolvedSymbol != null , "symbol {0} in {1} must resolve at runtime", resolvedSymbol.Name, resolvedSymbol.ContainerScope.ContainerFunction.Declaration); Debug.Assert(resolvedSymbol.ResolvedSymbol != null, "symbol {0} in {1} must have been already resolved", resolvedSymbol.Name, resolvedSymbol.ContainerScope.ContainerFunction.Declaration); symbol.ResolvedSymbol = resolvedSymbol.ResolvedSymbol; symbol.SymbolType = JSSymbol.SymbolTypes.ParentLocal; symbol.ContainerScope.HasParentLocalSymbol = true; symbol.AssignFieldId(); outerScope = null; //stop the loop break; case JSSymbol.SymbolTypes.Global: Debug.Assert( outerScope.IsProgram || !outerScope.HasEval , "symbol {0} in {1} must resolve at runtime", resolvedSymbol.Name, resolvedSymbol.ContainerScope.ContainerFunction.Declaration); //symbol is a global that ParentFunction is also using! symbol.SymbolType = JSSymbol.SymbolTypes.Global; if (!outerScope.IsProgram) { symbol.ResolvedSymbol = resolvedSymbol.ResolvedSymbol; //may or maynot be null! } symbol.AssignFieldId(); outerScope = null; //end the loop break; default: throw new InvalidOperationException(string.Format("{0} has unexpected symbol type {1} in {2}. This case should not have happened.", resolvedSymbol.Name, resolvedSymbol.SymbolType, resolvedSymbol.ContainerScope.ContainerFunction.Declaration)); } } break; } else if (outerScope.HasEval && outerScope.IsFunction) { ///We did not find this symbol in the parent scopes all the way to the function scope and this scope has eval, so should not search any more ///note that if any of the inner scopes of a function scope has eval, then the function scope itself will also has eval shouldResolveAtRuntime = true; break; //The symbol may be added later by the eval!, end the loop } } if ( symbol.SymbolType == JSSymbol.SymbolTypes.Unknown && !shouldResolveAtRuntime ) { //This could be a builtin name if (JSRuntime.Instance.GlobalContext.HasOwnProperty(symbol.Name)) { symbol.SymbolType = JSSymbol.SymbolTypes.Global; symbol.AssignFieldId(); } } }
public void AnalyzeSymbol(JSSymbol symbol) { ///Symbol resolution is very critical and can be complicated considering parallel analyze and inlining. ///Therefore, we have to be conservative and assert every assumption we make ///We should consider: /// - In normal case, symbols of a function are resolved AFTER its sub-functions are analyzed. Therefore, some cases should not occure. However, if lazy parsing kick in /// the sub-function is parsed-analyzed way after the parent function was even executed. So, this will create a new set of assumptions. /// - Processing the top leve program script may have special cases which we will go through before ending the analysis of the program var symbolScope = symbol.ContainerScope; ///<Mehrdad> ///A function may just reference the "arguments" symbol, or even define its own. ///in any case, such a symbol is first initialized with the arctual arguments value, ///and may be then overwritten in the function. ///In anycase, existance of such a symbol will be costly. ///</Mehrdad> if (symbol.Name == JSFunctionArguments.Name) { var argumentsSymbol = symbol; var functionScope = symbolScope; if (!symbolScope.IsFunction) { functionScope = symbolScope.ContainerFunction.FunctionIR.Scope; argumentsSymbol = functionScope.GetOrAddSymbol(JSFunctionArguments.Name); } functionScope.HasArgumentsSymbol = true; Debug.Assert( argumentsSymbol.SymbolType == JSSymbol.SymbolTypes.Arguments || argumentsSymbol.SymbolType == JSSymbol.SymbolTypes.Local || argumentsSymbol.SymbolType == JSSymbol.SymbolTypes.Unknown , "invalide arguments type {0}", argumentsSymbol.SymbolType); argumentsSymbol.SymbolType = JSSymbol.SymbolTypes.Arguments; if (symbol != argumentsSymbol) { Debug.Assert(functionScope != symbolScope, "Invalid situation!"); symbol.SymbolType = JSSymbol.SymbolTypes.OuterDuplicate; symbol.ResolvedSymbol = argumentsSymbol; } } switch (symbol.SymbolType) { case JSSymbol.SymbolTypes.Local: if ( symbolScope.HasUnknownSubFunction || symbolScope.HasEval || symbolScope.IsEvalFunction //We don't see the full picture in eval function, the local may be defined outside as well, besides all locals are added to parent scope ) { //In these cases we have uncertainty in access sites, so we should threat all as potential ClosedOnLocals symbol.SymbolType = JSSymbol.SymbolTypes.ClosedOnLocal; goto case JSSymbol.SymbolTypes.ClosedOnLocal; } else { symbolScope.HasLocalSymbol = true; } break; case JSSymbol.SymbolTypes.ClosedOnLocal: Trace.Assert(symbolScope.IsFunction, "At this point closure on non function level variables is not supported"); symbolScope.HasClosedOnSymbol = true; if (symbolScope.IsProgram) { ///This is in fact a global variable declataion: ///- We know this code is certainly executed. ///- We look for this symbol in the global context when analyzing subfunctions ///- If we add this to global context after creating subfunction objects (during execution), the heavy propagate algorithm in the Propertymap will be executed ///So we add the symbol to the global object to ensure it exsits there //symbol.SymbolType = JSSymbol.SymbolTypes.Global; symbol.AssignFieldId(); var prop = mdr.Runtime.Instance.GlobalContext.Map.GetPropertyDescriptorByFieldId(symbol.FieldId); if (prop == null || prop.IsUndefined) { mdr.Runtime.Instance.GlobalContext.AddOwnPropertyDescriptorByFieldId( symbol.FieldId , mdr.PropertyDescriptor.Attributes.Data // | mdr.PropertyDescriptor.Attributes.NotConfigurable //TODO: why do we need this? ); } } break; case JSSymbol.SymbolTypes.HiddenLocal: //Nothing to do for these types break; case JSSymbol.SymbolTypes.OuterDuplicate: //this can happen since NodeFactory may have already hoisted some locals to outer function scop break; case JSSymbol.SymbolTypes.ParentLocal: Trace.Fail("Since we are resolving functions bottom-up, and scopes top-down we should not see any symbol type of {0} here", symbol.SymbolType); break; case JSSymbol.SymbolTypes.Arguments: Debug.Assert(symbolScope.HasArgumentsSymbol, "{0} symbol exists, but scope does have the correct attributes", JSFunctionArguments.Name); break; case JSSymbol.SymbolTypes.Global: Debug.Assert( symbolScope.IsProgram && mdr.Runtime.Instance.GlobalContext.HasOwnProperty(symbol.Name) , "we should only see this after resolving symbols and symbol {0} should be global but is not in the global context", symbol.Name); break; case JSSymbol.SymbolTypes.Unknown: //NOTE: we should never try to resolve "undefined" since it can be easily assigned to at runtime!!!! if (symbolScope.HasEval && symbolScope.IsFunction) { break; //The symbol may be added later by the eval! } ResolveMethod(symbol); break; default: throw new InvalidOperationException(string.Format("{0} has unexpected symbol type {1} in {2}. This case should not have happened.", symbol.Name, symbol.SymbolType, _currFuncMetadata.FullName)); } }
private IIdentifier MakeIdentifier(JSSymbol symbol, int offset) { return MakeReadIdentifierExpression(symbol, offset); }
internal LocalBuilder Declare(Type type, JSSymbol symbol) { return Declare(Declare(type/*, symbol.Name*/), symbol); }
private ReadIdentifierExpression MakeReadIdentifierExpression(JSSymbol symbol, int offset) { return new ReadIdentifierExpression(symbol, offset); }
internal LocalBuilder Get(JSSymbol symbol) { LocalBuilder local; _symbolLocals.TryGetValue(symbol, out local); return local; }
internal void ReadSymbol(JSSymbol symbol, ref mdr.DValue result, ref mdr.CallFrame callFrame) { switch (symbol.SymbolType) { case JSSymbol.SymbolTypes.Local: case JSSymbol.SymbolTypes.HiddenLocal: case JSSymbol.SymbolTypes.Arguments: { if (symbol.IsParameter) if (Arguments != null) //result = Arguments.Elements[symbol.ParameterIndex]; Arguments.GetField(symbol.ParameterIndex, ref result); //TODO: optimize this for faster access else result = callFrame.Arg(symbol.ParameterIndex); else result = SymbolValues[symbol.ValueIndex]; break; } case JSSymbol.SymbolTypes.ClosedOnLocal: case JSSymbol.SymbolTypes.ParentLocal: case JSSymbol.SymbolTypes.Global: { var pd = GetPropertyDescriptor(symbol); pd.Get(Context, ref result); break; } case JSSymbol.SymbolTypes.Unknown: { var pd = Context.GetPropertyDescriptorByFieldId(symbol.FieldId); pd.Get(Context, ref result); break; } case JSSymbol.SymbolTypes.OuterDuplicate: ReadSymbol(symbol.ResolvedSymbol, ref result, ref callFrame); break; default: Trace.Fail("Could not interpret symbol {0} with type {1}", symbol.Name, symbol.SymbolType); break; } }
void InlineInvocation(CallExpression invocation, JSFunctionMetadata targetFuncMetadata) { _targetFuncMetadata = targetFuncMetadata; _call = invocation; Debug.WriteLine("Trying to inline function {0}", _targetFuncMetadata.Declaration); _targetFuncMetadata.Analyze(); //Just to make sure it is analyzed if (JSRuntime.Instance.Configuration.ProfileStats) JSRuntime.Instance.Counters.GetCounter("Attempted Inline").Count++; if (!CanInline(_targetFuncMetadata)) return; if (JSRuntime.Instance.Configuration.ProfileStats) JSRuntime.Instance.Counters.GetCounter("Succeeded Inline").Count++; _functionsBeingInlined.AddLast(_targetFuncMetadata); _round++; throw new NotImplementedException(); //TODO: we need to update this algorithm based on the recent changes to the scope _newScope = new Scope(_currentTargetScope); _returnValueSymbol = _newScope.AddSymbol(RenameSymbol("retVal")); _returnValueSymbol.SymbolType = JSSymbol.SymbolTypes.HiddenLocal; _call.InlinedIR = new InlinedInvocation( _targetFuncMetadata , _newScope , BuildInlinedBody() , new ReadIdentifierExpression(_returnValueSymbol) ); _call.InlinedIR.AddUser(_call); Debug.WriteLine("Inlined function {0}", _targetFuncMetadata.Declaration); _functionsBeingInlined.RemoveLast(); }
private WriteIdentifierExpression MakeWriteIdentifierExpression(JSSymbol symbol, Expression value) { return new WriteIdentifierExpression(symbol, MakeGuardedCast(value)); }
public void SetType(JSSymbol symbol, mdr.ValueTypes type) { symbol.ValueType = type; }
//############################################################################################################################### // Symbols' local handling internal LocalBuilder Declare(mdr.ValueTypes type, JSSymbol symbol) { return(Declare(Types.TypeOf(type), symbol)); }
internal LocalBuilder Declare(Type type, JSSymbol symbol) { return(Declare(Declare(type /*, symbol.Name*/), symbol)); }
int _symbolIndexOffset; //We may have reserved values like context, arguments ... in the CallFrame.Values /// <summary> /// Returns the index of the symbols in the CallFrame.Values /// </summary> int GetIndex(JSSymbol symbol) { return symbol.Index + _symbolIndexOffset; }
private mdr.PropertyDescriptor GetPropertyDescriptor(JSSymbol symbol) { mdr.PropertyDescriptor pd; if (SymbolValues[symbol.ValueIndex].ValueType == mdr.ValueTypes.Undefined) { //first time visit pd = Context.GetPropertyDescriptorByFieldId(symbol.FieldId); SymbolValues[symbol.ValueIndex].Set(pd); } else pd = (mdr.PropertyDescriptor)SymbolValues[symbol.ValueIndex].AsObject(); return pd; }
private WriteIdentifierExpression MakeWriteIdentifierExpression(JSSymbol symbol, Expression value) { return(new WriteIdentifierExpression(symbol, MakeGuardedCast(value))); }
private static void ResolveMethod(JSSymbol symbol) { var containerFunction = symbol.ContainerScope.ContainerFunction; bool shouldResolveAtRuntime = false; for (var outerScope = symbol.ContainerScope.OuterScope; outerScope != null; outerScope = outerScope.OuterScope) { var resolvedSymbol = outerScope.GetSymbol(symbol.Name); if (resolvedSymbol != null) { if (outerScope.ContainerFunction == containerFunction) { ///We just need to know everywhere instead of symbol, we should refer to resolved symbol ///the rest will be taken care of as we keep analyzing the upper scopes symbol.SymbolType = JSSymbol.SymbolTypes.OuterDuplicate; if (resolvedSymbol.SymbolType == JSSymbol.SymbolTypes.OuterDuplicate) { Debug.Assert(resolvedSymbol.ResolvedSymbol != null, "symbol {0} must have already been resolved!"); symbol.ResolvedSymbol = resolvedSymbol.ResolvedSymbol; } else symbol.ResolvedSymbol = resolvedSymbol; } else { //here we are sure the symbol is found in a parent function scope switch (resolvedSymbol.SymbolType) { case JSSymbol.SymbolTypes.Local: case JSSymbol.SymbolTypes.ClosedOnLocal: symbol.ResolvedSymbol = resolvedSymbol; if (outerScope.IsProgram) { symbol.SymbolType = JSSymbol.SymbolTypes.Global; } else { symbol.SymbolType = JSSymbol.SymbolTypes.ParentLocal; } symbol.ContainerScope.HasParentLocalSymbol = true; //We need this anyways since we want to know if a context is needed at runtime at all lock (resolvedSymbol) //In case sub-functions are processed in parallel { resolvedSymbol.SymbolType = JSSymbol.SymbolTypes.ClosedOnLocal; resolvedSymbol.NonLocalWritersCount += symbol.Writers.Count; resolvedSymbol.AssignFieldId(); } symbol.AssignFieldId(); break; case JSSymbol.SymbolTypes.Unknown: if (outerScope.HasEval) { shouldResolveAtRuntime = true; outerScope = null; //to stop the loop } //we will take care of this after this loop break; case JSSymbol.SymbolTypes.ParentLocal: ///We could have made a shortcut here and setup the symbol info, but we would still need to walk up until we find the actual symbol declaration ///So, the optimization here would be just not calling GetSymbol int some parent functions. ///To simplify code, for now, we just ignore this until we reach the declared symbol Debug.Assert( !outerScope.HasEval && !outerScope.IsProgram && resolvedSymbol.ResolvedSymbol != null , "symbol {0} in {1} must resolve at runtime", resolvedSymbol.Name, resolvedSymbol.ContainerScope.ContainerFunction.Declaration); Debug.Assert(resolvedSymbol.ResolvedSymbol != null, "symbol {0} in {1} must have been already resolved", resolvedSymbol.Name, resolvedSymbol.ContainerScope.ContainerFunction.Declaration); symbol.ResolvedSymbol = resolvedSymbol.ResolvedSymbol; symbol.SymbolType = JSSymbol.SymbolTypes.ParentLocal; symbol.ContainerScope.HasParentLocalSymbol = true; symbol.AssignFieldId(); outerScope = null; //stop the loop break; case JSSymbol.SymbolTypes.Global: Debug.Assert( outerScope.IsProgram || !outerScope.HasEval , "symbol {0} in {1} must resolve at runtime", resolvedSymbol.Name, resolvedSymbol.ContainerScope.ContainerFunction.Declaration); //symbol is a global that ParentFunction is also using! symbol.SymbolType = JSSymbol.SymbolTypes.Global; if (!outerScope.IsProgram) { symbol.ResolvedSymbol = resolvedSymbol.ResolvedSymbol; //may or maynot be null! } symbol.AssignFieldId(); outerScope = null; //end the loop break; default: throw new InvalidOperationException(string.Format("{0} has unexpected symbol type {1} in {2}. This case should not have happened.", resolvedSymbol.Name, resolvedSymbol.SymbolType, resolvedSymbol.ContainerScope.ContainerFunction.Declaration)); } } break; } else if (outerScope.HasEval && outerScope.IsFunction) { ///We did not find this symbol in the parent scopes all the way to the function scope and this scope has eval, so should not search any more ///note that if any of the inner scopes of a function scope has eval, then the function scope itself will also has eval shouldResolveAtRuntime = true; break; //The symbol may be added later by the eval!, end the loop } } if ( symbol.SymbolType == JSSymbol.SymbolTypes.Unknown && !shouldResolveAtRuntime ) { //This could be a builtin name if (JSRuntime.Instance.GlobalContext.HasOwnProperty(symbol.Name)) { symbol.SymbolType = JSSymbol.SymbolTypes.Global; symbol.AssignFieldId(); } } }
internal void WriteSymbol(JSSymbol symbol, ref mdr.DValue result, ref mdr.CallFrame callFrame) { switch (symbol.SymbolType) { case JSSymbol.SymbolTypes.Local: case JSSymbol.SymbolTypes.HiddenLocal: case JSSymbol.SymbolTypes.Arguments: { if (symbol.IsParameter) if (Arguments != null) Arguments.Elements[symbol.ParameterIndex].Set(ref result); else callFrame.SetArg(symbol.ParameterIndex, ref result); else SymbolValues[symbol.ValueIndex] = result; break; } case JSSymbol.SymbolTypes.ClosedOnLocal: case JSSymbol.SymbolTypes.ParentLocal: case JSSymbol.SymbolTypes.Global: { var pd = GetPropertyDescriptor(symbol); pd.Set(Context, ref result); break; } case JSSymbol.SymbolTypes.Unknown: { var pd = Context.GetPropertyDescriptorByFieldId(symbol.FieldId); if (pd.IsUndefined) JSRuntime.Instance.GlobalContext.SetFieldByFieldId(symbol.FieldId, ref result); else pd.Set(Context, ref result); break; } case JSSymbol.SymbolTypes.OuterDuplicate: WriteSymbol(symbol.ResolvedSymbol, ref result, ref callFrame); break; default: Trace.Fail("Could not interpret symbol {0} with type {1}", symbol.Name, symbol.SymbolType); break; } }
mdr.ValueTypes GetType(JSSymbol symbol) { var symbolValueType = symbol.ValueType; //TODO: we actually need to read this from cgInfo return symbolValueType; }