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) { base.engine = engine; this.formal_parameters = formal_parameters; this.argumentsSlotNumber = 0; this.body = null; this.method = TypeReflector.GetTypeReflectorFor(Globals.TypeRefs.ToReferenceContext(t)).GetMethod(method_name, BindingFlags.Public | BindingFlags.Static); this.parameterInfos = this.method.GetParameters(); if (!Microsoft.JScript.CustomAttribute.IsDefined(this.method, typeof(JSFunctionAttribute), false)) { this.isMethod = true; } else { JSFunctionAttributeEnum attributeValue = ((JSFunctionAttribute) Microsoft.JScript.CustomAttribute.GetCustomAttributes(this.method, typeof(JSFunctionAttribute), false)[0]).attributeValue; this.isExpandoMethod = (attributeValue & JSFunctionAttributeEnum.IsExpandoMethod) != JSFunctionAttributeEnum.None; } 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(); base.noExpando = false; this.clsCompliance = CLSComplianceSpec.NotAttributed; }
internal EnumDeclaration(Context context, IdentifierLiteral id, TypeExpression baseType, Block body, FieldAttributes attributes, CustomAttributeList customAttributes) : base(context, id, new TypeExpression(new ConstantWrapper(Typeob.Enum, null)), new TypeExpression[0], body, attributes, false, false, true, false, customAttributes) { this.baseType = (baseType != null) ? baseType : new TypeExpression(new ConstantWrapper(Typeob.Int32, null)); base.needsEngine = false; base.attributes &= TypeAttributes.NestedFamORAssem; TypeExpression expression = new TypeExpression(new ConstantWrapper(base.classob, base.context)); AST ast = new ConstantWrapper(-1, null); AST ast2 = new ConstantWrapper(1, null); JSMemberField[] fields = base.fields; for (int i = 0; i < fields.Length; i++) { FieldInfo info = fields[i]; JSVariableField field = (JSVariableField) info; field.attributeFlags = FieldAttributes.Literal | FieldAttributes.Static | FieldAttributes.Public; field.type = expression; if (field.value == null) { field.value = ast = new Plus(ast.context, ast, ast2); } else { ast = (AST) field.value; } field.value = new DeclaredEnumValue(field.value, field.Name, base.classob); } }
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 length = parameter_declarations.Length; this.formal_parameters = new string[length]; for (int i = 0; i < length; 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 field = (JSLocalField) this.own_scope.AddNewField("arguments", null, FieldAttributes.PrivateScope); field.type = new TypeExpression(new ConstantWrapper(Typeob.Object, funcContext)); field.isDefined = true; this.hasArgumentsObject = true; } else { this.hasArgumentsObject = false; } this.implementedIface = null; this.implementedIfaceMethod = null; this.isMethod = isMethod; this.isExpandoMethod = (customAttributes != null) && customAttributes.ContainsExpandoAttribute(); this.isStatic = this.own_scope.isStatic = (attributes & MethodAttributes.Static) != MethodAttributes.PrivateScope; this.suppressIL = false; this.noVersionSafeAttributeSpecified = true; this.fields = this.own_scope.GetLocalFields(); this.enclosing_scope = enclosing_scope; this.must_save_stack_locals = false; 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; base.noExpando = false; this.clsCompliance = CLSComplianceSpec.NotAttributed; this.engineLocal = null; this.partiallyEvaluated = false; }
internal StaticInitializer(Context context, Block body, FunctionScope own_scope) : base(context) { this.func = new FunctionObject(null, new ParameterDeclaration[0], null, body, own_scope, Globals.ScopeStack.Peek(), context, MethodAttributes.Private|MethodAttributes.Static); this.func.isMethod = true; this.func.hasArgumentsObject = false; this.completion = new Completion(); }
internal FunctionExpression (AST parent, string name, FormalParameterList p, string return_type, Block body, Location location) : base (parent, location) { func_obj = new FunctionObject (name, p, return_type, body, location); }
internal FunctionExpression(Context context, AST id, ParameterDeclaration[] formal_parameters, TypeExpression return_type, Block body, FunctionScope own_scope, FieldAttributes attributes) : base(context){ if (attributes != (FieldAttributes)0){ this.context.HandleError(JSError.SyntaxError); attributes = (FieldAttributes)0; } ScriptObject enclosingScope = Globals.ScopeStack.Peek(); this.name = id.ToString(); if (this.name.Length == 0) this.name = "anonymous "+(uniqueNumber++).ToString(CultureInfo.InvariantCulture); else this.AddNameTo(enclosingScope); this.func = new FunctionObject(this.name, formal_parameters, return_type, body, own_scope, enclosingScope, this.context, MethodAttributes.Static|MethodAttributes.Public); }
//--------------------------------------------------------------------------------------- // JSParser // // create a parser with a context. The context is the code that has to be compiled. // Typically used by the runtime //--------------------------------------------------------------------------------------- public JSParser(Context context){ this.sourceContext = context; this.currentToken = context.Clone(); this.scanner = new JSScanner(this.currentToken); this.noSkipTokenSet = new NoSkipTokenSet(); this.errorToken = null; this.program = null; this.blockType = new ArrayList(16); this.labelTable = new SimpleHashtable(16); this.finallyEscaped = 0; this.Globals = context.document.engine.Globals; this.Severity = 5; }
internal Class(Context context, AST id, TypeExpression superTypeExpression, TypeExpression[] interfaces, Block body, FieldAttributes attributes, bool isAbstract, bool isFinal, bool isStatic, bool isInterface, CustomAttributeList customAttributes) : base(context) { this.name = id.ToString(); this.superTypeExpression = superTypeExpression; this.interfaces = interfaces; this.body = body; this.enclosingScope = (ScriptObject)Globals.ScopeStack.Peek(1); this.attributes = TypeAttributes.Class|TypeAttributes.Serializable; this.SetAccessibility(attributes); if (isAbstract) this.attributes |= TypeAttributes.Abstract; this.isAbstract = isAbstract || isInterface; this.isAlreadyPartiallyEvaluated = false; if (isFinal) this.attributes |= TypeAttributes.Sealed; if (isInterface) this.attributes |= TypeAttributes.Interface | TypeAttributes.Abstract; this.isCooked = false; this.cookedType = null; this.isExpando = false; this.isInterface = isInterface; this.isStatic = isStatic; this.needsEngine = !isInterface; this.validOn = (AttributeTargets)0; this.allowMultiple = true; this.classob = (ClassScope)Globals.ScopeStack.Peek(); this.classob.name = this.name; this.classob.owner = this; this.implicitDefaultConstructor = null; if (!isInterface && !(this is EnumDeclaration)) this.SetupConstructors(); this.EnterNameIntoEnclosingScopeAndGetOwnField(id, isStatic); this.fields = this.classob.GetMemberFields(); this.superClass = null; this.superIR = null; this.superMembers = null; this.firstIndex = null; this.fieldInitializer = null; this.customAttributes = customAttributes; this.clsCompliance = CLSComplianceSpec.NotAttributed; this.generateCodeForExpando = false; this.expandoItemProp = null; this.getHashTableMethod = null; this.getItem = null; this.setItem = null; }
internal EnumDeclaration(Context context, IdentifierLiteral id, TypeExpression baseType, Block body, FieldAttributes attributes, CustomAttributeList customAttributes) : base(context, id, new TypeExpression(new ConstantWrapper(typeof(Enum), null)), new TypeExpression[0], body, attributes, false, false, true, false, customAttributes){ this.baseType = baseType != null ? baseType : new TypeExpression(new ConstantWrapper(Typeob.Int32, null)); this.needsEngine = false; this.attributes &= TypeAttributes.VisibilityMask; TypeExpression thisType = new TypeExpression(new ConstantWrapper(this.classob, this.context)); AST currentValue = new ConstantWrapper(-1, null); AST one = new ConstantWrapper(1, null); foreach (FieldInfo f in this.fields){ JSVariableField field = (JSVariableField)f; field.attributeFlags = FieldAttributes.Public|FieldAttributes.Static|FieldAttributes.Literal; field.type = thisType; if (field.value == null) field.value = currentValue = new Plus(currentValue.context, currentValue, one); else currentValue = (AST)field.value; field.value = new EnumWrapper(field.value, field.Name, this.classob); } }
internal FunctionObject(string name, FormalParameterList p, string ret_type, Block body, Location location) { this._prototype = ObjectConstructor.Ctr.ConstructObject (); // // FIXME // 1) Must collect the attributes given. // 2) Check if they are semantically correct. // 3) Assign those values to 'attr'. // this.attr = MethodAttributes.Public | MethodAttributes.Static; this.name = name; this.parameters = p; this.type_annot = ret_type; // // FIXME: Must check that return_type it's a valid type, // and assign that to 'return_type' field. // this.return_type = typeof (void); this.body = body; this.location = location; }
internal ScriptBlock(Context context, Block statement_block) : base(context) { this.statement_block = statement_block; this.own_scope = (GlobalScope) base.Engine.ScriptObjectStackTop(); this.fields = null; }
//--------------------------------------------------------------------------------------- // ParseForStatement // // ForStatement : // 'for' '(' OptionalExpressionNoIn ';' OptionalExpression ';' OptionalExpression ')' // 'for' '(' 'var' VariableDeclarationListNoIn ';' OptionalExpression ';' OptionalExpression ')' // 'for' '(' LeftHandSideExpression 'in' Expression')' // 'for' '(' 'var' Identifier OptionalInitializerNoIn 'in' Expression')' // // OptionalExpressionNoIn : // <empty> | // ExpressionNoIn // same as Expression but does not process 'in' as an operator // // OptionalInitializerNoIn : // <empty> | // InitializerNoIn // same as initializer but does not process 'in' as an operator //--------------------------------------------------------------------------------------- private AST ParseForStatement(){ this.blockType.Add(BlockType.Loop); AST forNode = null; try{ Context forCtx = this.currentToken.Clone(); GetNextToken(); if (JSToken.LeftParen != this.currentToken.token) ReportError(JSError.NoLeftParen); GetNextToken(); bool isForIn = false, recoveryInForIn = false; AST lhs = null, initializer = null, condOrColl = null, increment = null; try{ if (JSToken.Var == this.currentToken.token){ isForIn = true; initializer = ParseIdentifierInitializer(JSToken.In, (FieldAttributes)0, null, JSToken.Var); // a list of variable initializers is allowed only in a for(;;) AST var = null; while (JSToken.Comma == this.currentToken.token){ isForIn = false; var = ParseIdentifierInitializer(JSToken.In, (FieldAttributes)0, null, JSToken.Var); initializer = new Comma(initializer.context.CombineWith(var.context), initializer, var); } // if it could still be a for..in, now it's time to get the 'in' if (isForIn){ if (JSToken.In == this.currentToken.token){ GetNextToken(); condOrColl = ParseExpression(); }else isForIn = false; } }else{ if (JSToken.Semicolon != this.currentToken.token){ bool isLHS; initializer = ParseUnaryExpression(out isLHS, false); if (isLHS && JSToken.In == this.currentToken.token){ isForIn = true; lhs = initializer; initializer = null; GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try{ condOrColl = ParseExpression(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = null; throw exc; }else{ if (exc._partiallyComputedNode == null) condOrColl = new ConstantWrapper(true, CurrentPositionContext()); // what could we put here? else condOrColl = exc._partiallyComputedNode; } if (exc._token == JSToken.RightParen){ GetNextToken(); recoveryInForIn = true; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } }else initializer = ParseExpression(initializer, false, isLHS, JSToken.In); }else initializer = new EmptyLiteral(CurrentPositionContext()); } }catch(RecoveryTokenException exc){ // error is too early abort for exc._partiallyComputedNode = null; throw exc; } // at this point we know whether or not is a for..in if (isForIn){ if (!recoveryInForIn){ if (JSToken.RightParen != this.currentToken.token) ReportError(JSError.NoRightParen); forCtx.UpdateWith(this.currentToken); GetNextToken(); } AST body = null; try{ body = ParseStatement(); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode == null) body = new Block(CurrentPositionContext()); else body = exc._partiallyComputedNode; exc._partiallyComputedNode = new ForIn(forCtx, lhs, initializer, condOrColl, body); throw exc; } forNode = new ForIn(forCtx, lhs, initializer, condOrColl, body); }else{ this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try{ if (JSToken.Semicolon != this.currentToken.token){ ReportError(JSError.NoSemicolon); if (JSToken.Colon == this.currentToken.token){ this.noSkipTokenSet.Add(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet); try{ SkipTokensAndThrow(); }catch(RecoveryTokenException exc){ if (JSToken.Semicolon == this.currentToken.token) this.errorToken = null; else throw exc; }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet); } } } GetNextToken(); if (JSToken.Semicolon != this.currentToken.token){ condOrColl = ParseExpression(); if (JSToken.Semicolon != this.currentToken.token) ReportError(JSError.NoSemicolon); }else condOrColl = new ConstantWrapper(true, CurrentPositionContext()); GetNextToken(); if (JSToken.RightParen != this.currentToken.token) increment = ParseExpression(); else increment = new EmptyLiteral(CurrentPositionContext()); if (JSToken.RightParen != this.currentToken.token) ReportError(JSError.NoRightParen); forCtx.UpdateWith(this.currentToken); GetNextToken(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = null; throw exc; }else{ // discard any partial info, just genrate empty condition and increment and keep going exc._partiallyComputedNode = null; if (condOrColl == null) condOrColl = new ConstantWrapper(true, CurrentPositionContext()); if (increment == null) increment = new EmptyLiteral(CurrentPositionContext()); } if (exc._token == JSToken.RightParen){ GetNextToken(); } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } AST body = null; try{ body = ParseStatement(); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode == null) body = new Block(CurrentPositionContext()); else body = exc._partiallyComputedNode; exc._partiallyComputedNode = new For(forCtx, initializer, condOrColl, increment, body); throw exc; } forNode = new For(forCtx, initializer, condOrColl, increment, body); } }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } return forNode; }
//--------------------------------------------------------------------------------------- // ParseDoStatement // // DoStatement: // 'do' Statement 'while' '(' Expression ')' //--------------------------------------------------------------------------------------- private DoWhile ParseDoStatement(){ Context doCtx = null; // this.currentToken.Clone(); AST body = null; AST condition = null; this.blockType.Add(BlockType.Loop); try{ GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_DoWhileBodyNoSkipTokenSet); try{ body = ParseStatement(); }catch(RecoveryTokenException exc){ // make up a block for the do while if (exc._partiallyComputedNode != null) body = exc._partiallyComputedNode; else body = new Block(CurrentPositionContext()); if (IndexOfToken(NoSkipTokenSet.s_DoWhileBodyNoSkipTokenSet, exc) == -1){ // we have to pass the exception to someone else, make as much as you can from the 'do while' exc._partiallyComputedNode = new DoWhile(CurrentPositionContext(), body, new ConstantWrapper(false, CurrentPositionContext())); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_DoWhileBodyNoSkipTokenSet); } if (JSToken.While != this.currentToken.token){ ReportError(JSError.NoWhile); } doCtx = this.currentToken.Clone(); GetNextToken(); if (JSToken.LeftParen != this.currentToken.token){ ReportError(JSError.NoLeftParen); } GetNextToken(); // catch here so the body of the do_while is not thrown away this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try{ condition = ParseExpression(); if (JSToken.RightParen != this.currentToken.token){ ReportError(JSError.NoRightParen); doCtx.UpdateWith(condition.context); }else doCtx.UpdateWith(this.currentToken); GetNextToken(); }catch(RecoveryTokenException exc){ // make up a condition if (exc._partiallyComputedNode != null) condition = exc._partiallyComputedNode; else condition = new ConstantWrapper(false, CurrentPositionContext()); if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = new DoWhile(doCtx, body, condition); throw exc; }else{ if (JSToken.RightParen == this.currentToken.token) GetNextToken(); } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } if (JSToken.Semicolon == this.currentToken.token){ // JScript 5 allowed statements like // do{print(++x)}while(x<10) print(0) // even though that does not strictly follow the automatic semicolon insertion // rules for the required semi after the while(). For backwards compatibility // we should continue to support this. doCtx.UpdateWith(this.currentToken); GetNextToken(); } }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } return new DoWhile(doCtx, body, condition); }
//--------------------------------------------------------------------------------------- // ParseWhileStatement // // WhileStatement : // 'while' '(' Expression ')' Statement //--------------------------------------------------------------------------------------- private While ParseWhileStatement(){ Context whileCtx = this.currentToken.Clone(); AST condition = null; AST body = null; this.blockType.Add(BlockType.Loop); try{ GetNextToken(); if (JSToken.LeftParen != this.currentToken.token){ ReportError(JSError.NoLeftParen); } GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try{ condition = ParseExpression(); if (JSToken.RightParen != this.currentToken.token){ ReportError(JSError.NoRightParen); whileCtx.UpdateWith(condition.context); }else whileCtx.UpdateWith(this.currentToken); GetNextToken(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1){ // abort the while there is really no much to do here exc._partiallyComputedNode = null; throw exc; }else{ // make up a condition if (exc._partiallyComputedNode != null) condition = exc._partiallyComputedNode; else condition = new ConstantWrapper(false, CurrentPositionContext()); if (JSToken.RightParen == this.currentToken.token) GetNextToken(); } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } try{ body = ParseStatement(); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null) body = exc._partiallyComputedNode; else body = new Block(CurrentPositionContext()); exc._partiallyComputedNode = new While(whileCtx, condition, body); throw exc; } }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } return new While(whileCtx, condition, body); }
//--------------------------------------------------------------------------------------- // ParseStatement // // OptionalStatement: // Statement | // <empty> // // Statement : // Block | // VariableStatement | // EmptyStatement | // ExpressionStatement | // IfStatement | // IterationStatement | // ContinueStatement | // BreakStatement | // ReturnStatement | // WithStatement | // LabeledStatement | // SwitchStatement | // ThrowStatement | // TryStatement | // QualifiedDeclaration | // Class | // FunctionDeclaration // // IterationStatement : // 'for' '(' ForLoopControl ')' | ===> ForStatement // 'do' Statement 'while' '(' Expression ')' | ===> DoStatement // 'while' '(' Expression ')' Statement ===> WhileStatement // //--------------------------------------------------------------------------------------- // ParseStatement deals with the end of statement issue (EOL vs ';') so if any of the // ParseXXX routine does it as well, it should return directly from the switch statement // without any further execution in the ParseStatement private AST ParseStatement(){ AST statement = null; String id = null; switch (this.currentToken.token){ case JSToken.EndOfFile: EOFError(JSError.ErrEOF); throw new EndOfFile(); // abort parsing, get back to the main parse routine case JSToken.Debugger: statement = new DebugBreak(this.currentToken.Clone()); GetNextToken(); break; case JSToken.Semicolon: // make an empty statement statement = new Block(this.currentToken.Clone()); GetNextToken(); return statement; case JSToken.RightCurly: ReportError(JSError.SyntaxError); SkipTokensAndThrow(); break; case JSToken.LeftCurly: return ParseBlock(); case JSToken.Var: case JSToken.Const: return ParseVariableStatement((FieldAttributes)0, null, this.currentToken.token); case JSToken.If: return ParseIfStatement(); case JSToken.For: return ParseForStatement(); case JSToken.Do: return ParseDoStatement(); case JSToken.While: return ParseWhileStatement(); case JSToken.Continue: statement = ParseContinueStatement(); if (null == statement) return new Block(CurrentPositionContext()); else return statement; case JSToken.Break: statement = ParseBreakStatement(); if (null == statement) return new Block(CurrentPositionContext()); else return statement; case JSToken.Return: statement = ParseReturnStatement(); if (null == statement) return new Block(CurrentPositionContext()); else return statement; case JSToken.With: return ParseWithStatement(); case JSToken.Switch: return ParseSwitchStatement(); case JSToken.Super: case JSToken.This: Context superCtx = this.currentToken.Clone(); if (JSToken.LeftParen == this.scanner.PeekToken()) statement = ParseConstructorCall(superCtx); else goto default; break; case JSToken.Throw: statement = ParseThrowStatement(); if (statement == null) return new Block(CurrentPositionContext()); else break; case JSToken.Try: return ParseTryStatement(); case JSToken.Internal: case JSToken.Public: case JSToken.Static: case JSToken.Private: case JSToken.Protected: case JSToken.Abstract: case JSToken.Final: bool parsedOK; statement = ParseAttributes(null, false, false, out parsedOK); if (!parsedOK){ statement = ParseExpression(statement, false, true, JSToken.None); statement = new Expression(statement.context.Clone(), statement); break; }else return statement; case JSToken.Package: Context packageContext = this.currentToken.Clone(); statement = ParsePackage(packageContext); if (statement is Package){ // handle common error of using import in function ReportError(JSError.PackageInWrongContext, packageContext, true); // make an empty statement statement = new Block(packageContext); } break; case JSToken.Interface: case JSToken.Class: return ParseClass((FieldAttributes)0, false, this.currentToken.Clone(), false, false, null); case JSToken.Enum: return ParseEnum((FieldAttributes)0, this.currentToken.Clone(), null); case JSToken.Function: return ParseFunction((FieldAttributes)0, false, this.currentToken.Clone(), false, false, false, false, null); //Parse a function as a statement case JSToken.Else: ReportError(JSError.InvalidElse); SkipTokensAndThrow(); break; case JSToken.Import: // handle common error of using import in function ReportError(JSError.InvalidImport, true); // make an empty statement statement = new Block(this.currentToken.Clone()); try{ ParseImportStatement(); }catch(RecoveryTokenException){ } break; default: this.noSkipTokenSet.Add(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); bool exprError = false; try{ bool bAssign, canBeAttribute = true; statement = ParseUnaryExpression(out bAssign, ref canBeAttribute, false); if (canBeAttribute){ // look for labels if (statement is Lookup){ if (JSToken.Colon == this.currentToken.token){ // can be a label id = statement.ToString(); if (null != this.labelTable[id]){ // there is already a label with that name. Ignore the current label ReportError(JSError.BadLabel, statement.context.Clone(), true); id = null; GetNextToken(); // skip over ':' return new Block(CurrentPositionContext()); }else{ GetNextToken(); this.labelTable[id] = this.blockType.Count; if (JSToken.EndOfFile != this.currentToken.token) statement = ParseStatement(); else statement = new Block(CurrentPositionContext()); this.labelTable.Remove(id); return statement; } } } // look for custom attributes if (JSToken.Semicolon != this.currentToken.token && !this.scanner.GotEndOfLine()){ bool parsed; statement = ParseAttributes(statement, false, false, out parsed); if (parsed) return statement; } } statement = ParseExpression(statement, false, bAssign, JSToken.None); statement = new Expression(statement.context.Clone(), statement); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null) statement = exc._partiallyComputedNode; if (statement == null){ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); exprError = true; SkipTokensAndThrow(); } if (IndexOfToken(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = statement; throw exc; } }finally{ if (!exprError) this.noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); } break; } if (JSToken.Semicolon == this.currentToken.token){ statement.context.UpdateWith(this.currentToken); GetNextToken(); }else if (!this.scanner.GotEndOfLine() && JSToken.RightCurly != this.currentToken.token && JSToken.EndOfFile != this.currentToken.token) ReportError(JSError.NoSemicolon, true); return statement; }
private void ParseAst(JS.AST ast, string sp) { if (ast == null) { return; } if (CheckWorker()) { return; } //_logView.LogStr("JSM->" + sp + ast.ToString() + "\t\t" + ast.GetType().Name); if (ast is JS.FunctionDeclaration) { JS.Function func = ast as JS.Function; ParseAstList(func.func_obj.body.elems, sp + " "); } else if (ast is JS.Assign) { JS.Assign ass = ast as JS.Assign; ParseAst(ass.left, sp + "l "); ParseAst(ass.right, sp + "r "); } else if (ast is JS.Binary) { JS.Binary bin = ast as JS.Binary; string[] parts = bin.ToString().Split('.'); if (parts.Length > 1) { if (parts[parts.Length - 2] == "This") { string calledId = parts[parts.Length - 1] + "()"; //dup // If we have a method of this name in this file/class if (_addedNodes.ContainsKey(calledId)) { // Create an edge. // A definite functional assignment link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Aqua); } else { // It's a call to a method outside this class/file. // //_logView.LogStr("skipped assign ref -->" + _methodNodeId + " --------> " + calledId); // } } else if (parts[parts.Length - 2] == "self") { string calledId = parts[parts.Length - 1] + "()"; //dup // If we have a method of this name in this file/class if (_addedNodes.ContainsKey(calledId)) { // Get the graph node that we're linking to. //Node calledNode = _addedNodes[calledId]; // Create an edge. // A definite functional assignment link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Aqua); } else { // It's a call to a method outside this class/file. _logView.LogStr("skipped assign ref -->" + _methodNodeId + " --------> " + calledId); } } } } else if (ast is JS.Expression) { JS.Expression expr = ast as JS.Expression; ParseAstList(expr.exprs, sp + " "); } else if (ast is JS.FunctionExpression) { JS.FunctionExpression expr = ast as JS.FunctionExpression; ParseAstList(expr.func_obj.body.elems, sp + " "); } else if (ast is JS.For) { JS.For fr = ast as JS.For; ParseAst(fr.stms, sp + " "); } else if (ast is JS.If) { JS.If iff = ast as JS.If; ParseAst(iff.false_stm, sp + "f "); ParseAst(iff.true_stm, sp + "t "); } else if (ast is JS.Block) { JS.Block block = ast as JS.Block; ParseAstList(block.elems, sp + " "); } else if (ast is JS.VariableStatement) { JS.VariableStatement var = ast as JS.VariableStatement; //var. ParseAstList(var.var_decls, sp + " "); } else if (ast is JS.Return) { JS.Return ret = ast as JS.Return; ParseAst(ret.expression, sp + " "); } else if (ast is JS.VariableDeclaration) { JS.VariableDeclaration var = ast as JS.VariableDeclaration; Microsoft.JScript.New newval = var.val as Microsoft.JScript.New; if (newval != null && newval.exp != null) { //_logView.LogStr("new:" + newval.exp.ToString()); string[] parts = newval.exp.ToString().Split('.'); if (parts.Length > 0) { string calledId = parts[parts.Length - 1] + "()"; // If we have a method of this name in this file/class, then // we have a possible constructor link. if (_addedNodes.ContainsKey(calledId)) { // Create an edge. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Green); } } } else { if (var.val != null) { string valStr = var.val.ToString(); string[] parts = valStr.Split('.'); if (parts.Length > 1 && parts[0] == "self") { // dup.. string calledId = parts[parts.Length - 1] + "()"; // If we have a method of this name in this file/class, then // we have a possible constructor link. if (_addedNodes.ContainsKey(calledId)) { // Create an edge. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Green); } } } } //ParseAstList(var.var_decls, sp + " "); } else if (ast is JS.Call) { JS.Call call = ast as JS.Call; string[] parts = call.ToString().Split(' '); string[] bits = parts[0].Split('.'); string calledId = bits[bits.Length - 1] + "()"; bool methodInThisClass = true; if (bits.Length > 1) { if ((bits[bits.Length - 2] != "This") && (bits[bits.Length - 2] != "self")) { methodInThisClass = false; } } // If we have a method of this name in this file/class if (_addedNodes.ContainsKey(calledId)) { // Create an edge. if (methodInThisClass) { // A definite link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Black); } else { // A tentative link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Gray); } } else { // It's a call to a method outside this class/file. // //_logView.LogStr("skipped -------->" + _methodNodeId + " --------> " + parts[0]); // } } }
Block ParseFunctionBody(AST parent) { ++nesting_of_function; Block pn = new Block (parent, new Location (ts.SourceName, ts.LineNumber)); try { int tt; while ((tt = ts.PeekToken ()) > Token.EOF && tt != Token.RC) { AST n; if (tt == Token.FUNCTION) { ts.GetToken (); n = Function (parent, FunctionType.Statement); } else n = Statement (parent); pn.Add (n); } } catch (ParserException) { ok = false; } finally { --nesting_of_function; } return pn; }
internal FunctionDeclaration(Context context, AST ifaceId, IdentifierLiteral id, ParameterDeclaration[] formal_parameters, TypeExpression return_type, Block body, FunctionScope own_scope, FieldAttributes attributes, bool isMethod, bool isGetter, bool isSetter, bool isAbstract, bool isFinal, CustomAttributeList customAttributes) : base(context) { MethodAttributes methodAttributes = (MethodAttributes)0; if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public) methodAttributes = MethodAttributes.Public; else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private) methodAttributes = MethodAttributes.Private; else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly) methodAttributes = MethodAttributes.Assembly; else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family) methodAttributes = MethodAttributes.Family; else if ((attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem) methodAttributes = MethodAttributes.FamORAssem; else methodAttributes = MethodAttributes.Public; if ((attributes & FieldAttributes.Static) != 0 || !isMethod) methodAttributes |= MethodAttributes.Static; else methodAttributes |= MethodAttributes.Virtual | MethodAttributes.NewSlot; if (isAbstract) methodAttributes |= MethodAttributes.Abstract; if (isFinal) methodAttributes |= MethodAttributes.Final; this.name = id.ToString(); this.isMethod = isMethod; if (ifaceId != null){ if (isMethod){ this.ifaceId = new TypeExpression(ifaceId); methodAttributes &= ~MethodAttributes.MemberAccessMask; methodAttributes |= MethodAttributes.Private|MethodAttributes.Final; }else{ this.declaringObject = new Member(ifaceId.context, ifaceId, id); this.name = this.declaringObject.ToString(); } } ScriptObject enclosingScope = Globals.ScopeStack.Peek(); if (attributes == 0 && !isAbstract && !isFinal){ if (enclosingScope is ClassScope) attributes |= FieldAttributes.Public; }else{ if (!(enclosingScope is ClassScope)){ this.context.HandleError(JSError.NotInsideClass); attributes = (FieldAttributes)0; methodAttributes = MethodAttributes.Public; } } if (enclosingScope is ActivationObject){ this.inFastScope = ((ActivationObject)enclosingScope).fast; // if later on originalName != this.name this is a property getter/setter String originalName = this.name; // mangle the name if (isGetter){ methodAttributes |= MethodAttributes.SpecialName; this.name = "get_" + this.name; if (return_type == null) return_type = new TypeExpression(new ConstantWrapper(Typeob.Object, context)); }else if (isSetter){ methodAttributes |= MethodAttributes.SpecialName; this.name = "set_" + this.name; return_type = new TypeExpression(new ConstantWrapper(Typeob.Void, context)); } attributes &= FieldAttributes.FieldAccessMask; // create the function object this.func = new FunctionObject(this.name, formal_parameters, return_type, body, own_scope, enclosingScope, this.context, methodAttributes, customAttributes, this.isMethod); if (this.declaringObject != null) return; // check whether the function name (possibly mangled) is in use already String fieldName = this.name; if (this.ifaceId != null) fieldName = ifaceId.ToString()+"."+fieldName; JSVariableField localField = (JSVariableField)((ActivationObject)enclosingScope).name_table[fieldName]; if (localField != null && (!(localField is JSMemberField) || !(((JSMemberField)localField).value is FunctionObject) || this.func.isExpandoMethod)){ if (originalName != this.name) localField.originalContext.HandleError(JSError.ClashWithProperty); else{ id.context.HandleError(JSError.DuplicateName, this.func.isExpandoMethod); if (localField.value is FunctionObject) ((FunctionObject)localField.value).suppressIL = true; } } // create or update the proper field if (this.isMethod){ if (!(localField is JSMemberField) || !(((JSMemberField)localField).value is FunctionObject) || originalName != this.name){ this.field = ((ActivationObject)enclosingScope).AddNewField(fieldName, this.func, attributes|FieldAttributes.Literal); if (originalName == this.name) // if it is a property do not assign the type ((JSVariableField)this.field).type = new TypeExpression(new ConstantWrapper(Typeob.FunctionWrapper, this.context)); }else this.field = ((JSMemberField)localField).AddOverload(this.func, attributes|FieldAttributes.Literal); }else if (enclosingScope is FunctionScope){ if (this.inFastScope) attributes |= FieldAttributes.Literal; this.field = ((FunctionScope)enclosingScope).AddNewField(this.name, attributes, this.func); if (this.field is JSLocalField){ JSLocalField locField = (JSLocalField)this.field; if (this.inFastScope){ locField.type = new TypeExpression(new ConstantWrapper(Typeob.ScriptFunction, this.context)); locField.attributeFlags |= FieldAttributes.Literal; } locField.debugOn = this.context.document.debugOn; locField.isDefined = true; } }else if (this.inFastScope){ this.field = ((ActivationObject)enclosingScope).AddNewField(this.name, this.func, attributes|FieldAttributes.Literal); ((JSVariableField)this.field).type = new TypeExpression(new ConstantWrapper(Typeob.ScriptFunction, this.context)); //Do not use typeof(Closure) for the field, since that has the arguments and callee properties, which are not //accessible in fast mode }else //enclosingScope is GlobalObject this.field = ((ActivationObject)enclosingScope).AddNewField(this.name, this.func, attributes|FieldAttributes.Static); ((JSVariableField)this.field).originalContext = context; // if it is a property create/update the PropertyInfo and assign the getter/setter if (originalName != this.name){ String propertyFieldName = originalName; if (this.ifaceId != null) propertyFieldName = ifaceId.ToString()+"."+originalName; FieldInfo prop = (FieldInfo)((ClassScope)enclosingScope).name_table[propertyFieldName]; if (prop != null){ // check whether a property was defined already if (prop.IsLiteral){ Object val = ((JSVariableField)prop).value; if (val is JSProperty) this.enclosingProperty = (JSProperty)val; } if (this.enclosingProperty == null) id.context.HandleError(JSError.DuplicateName, true); // the matching name was not a property } if (this.enclosingProperty == null){ this.enclosingProperty = new JSProperty(originalName); prop = ((ActivationObject)enclosingScope).AddNewField(propertyFieldName, this.enclosingProperty, attributes|FieldAttributes.Literal); ((JSMemberField)prop).originalContext = this.context; }else{ if ((isGetter && this.enclosingProperty.getter != null) || (isSetter && this.enclosingProperty.setter != null)) id.context.HandleError(JSError.DuplicateName, true); // duplicated setter or getter } if (isGetter) this.enclosingProperty.getter = new JSFieldMethod(this.field, enclosingScope); else this.enclosingProperty.setter = new JSFieldMethod(this.field, enclosingScope); } }else{ //Might get here if function declaration is inside of an eval. this.inFastScope = false; this.func = new FunctionObject(this.name, formal_parameters, return_type, body, own_scope, enclosingScope, this.context, MethodAttributes.Public, null, false); this.field = ((StackFrame)enclosingScope).AddNewField(this.name, new Closure(this.func), attributes|FieldAttributes.Static); } }
//--------------------------------------------------------------------------------------- // ParseVariableStatement // // VariableStatement : // ('var' | 'const') VariableDeclarationList // // VariableDeclarationList : // VariableDeclaration | // VariableDeclaration ',' VariableDeclarationList // // VariableDeclaration : // Identifier Type Initializer // // Type : // <empty> | // ':' TypeExpression // // Initializer : // <empty> | // '=' AssignmentExpression //--------------------------------------------------------------------------------------- private AST ParseVariableStatement(FieldAttributes visibility, CustomAttributeList customAttributes, JSToken kind){ Block varList = new Block(this.currentToken.Clone()); bool single = true; AST vdecl = null; for (;;){ this.noSkipTokenSet.Add(NoSkipTokenSet.s_EndOfLineToken); try{ vdecl = ParseIdentifierInitializer(JSToken.None, visibility, customAttributes, kind); }catch(RecoveryTokenException exc){ // an exception is passing by, possibly bringing some info, save the info if any if (exc._partiallyComputedNode != null){ if (!single){ varList.Append(exc._partiallyComputedNode); varList.context.UpdateWith(exc._partiallyComputedNode.context); exc._partiallyComputedNode = varList; } } if (IndexOfToken(NoSkipTokenSet.s_EndOfLineToken, exc) == -1) throw exc; else{ if (single) vdecl = exc._partiallyComputedNode; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfLineToken); } if (JSToken.Semicolon == this.currentToken.token || JSToken.RightCurly == this.currentToken.token){ if (JSToken.Semicolon == this.currentToken.token){ vdecl.context.UpdateWith(this.currentToken); GetNextToken(); } break; }else if (JSToken.Comma == this.currentToken.token){ single = false; varList.Append(vdecl); continue; }else if (this.scanner.GotEndOfLine()){ break; }else{ // assume the variable statement was terminated and move on ReportError(JSError.NoSemicolon, true); break; } } if (single) return vdecl; else{ varList.Append(vdecl); varList.context.UpdateWith(vdecl.context); return varList; } }
//--------------------------------------------------------------------------------------- // ParseTryStatement // // TryStatement : // 'try' Block CatchList Finally // // CatchList : // <empty> | // CatchList Catch // // Catch : // 'catch' '(' Identifier Type ')' Block // // Finally : // <empty> | // 'finally' Block //--------------------------------------------------------------------------------------- private AST ParseTryStatement(){ Context tryCtx = this.currentToken.Clone(); Context tryEndContext = null; AST body = null; AST id = null; AST handler = null; AST finally_block = null; RecoveryTokenException excInFinally = null; TypeExpression type = null; this.blockType.Add(BlockType.Block); try{ bool catchOrFinally = false; bool foundCatchAll = false; GetNextToken(); if (JSToken.LeftCurly != this.currentToken.token) ReportError(JSError.NoLeftCurly); this.noSkipTokenSet.Add(NoSkipTokenSet.s_NoTrySkipTokenSet); try{ body = ParseBlock(out tryEndContext); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_NoTrySkipTokenSet, exc) == -1) // do nothing and just return the containing block, if any throw exc; else body = exc._partiallyComputedNode; }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_NoTrySkipTokenSet); } while (JSToken.Catch == this.currentToken.token){ this.noSkipTokenSet.Add(NoSkipTokenSet.s_NoTrySkipTokenSet); try{ if (handler != null){ body = new Try(tryCtx, body, id, type, handler, null, false, tryEndContext); id = null; type = null; handler = null; } catchOrFinally = true; GetNextToken(); if (JSToken.LeftParen != this.currentToken.token) ReportError(JSError.NoLeftParen); GetNextToken(); if (JSToken.Identifier != this.currentToken.token){ string identifier = JSKeyword.CanBeIdentifier(this.currentToken.token); if (null != identifier){ ForceReportInfo(JSError.KeywordUsedAsIdentifier); id = new Lookup(identifier, this.currentToken.Clone()); }else{ ReportError(JSError.NoIdentifier); id = new Lookup("##Exc##" + s_cDummyName++, CurrentPositionContext()); } }else id = new Lookup(this.scanner.GetIdentifier(), this.currentToken.Clone()); GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try{ if (JSToken.Colon == this.currentToken.token) type = ParseTypeExpression(); else{ if (foundCatchAll) //no point in having another ForceReportInfo(id.context, JSError.UnreachableCatch); foundCatchAll = true; } if (JSToken.RightParen != this.currentToken.token) ReportError(JSError.NoRightParen); GetNextToken(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = null; // rethrow throw exc; }else{ type = (TypeExpression)exc._partiallyComputedNode; if (this.currentToken.token == JSToken.RightParen) GetNextToken(); } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } if (JSToken.LeftCurly != this.currentToken.token) ReportError(JSError.NoLeftCurly); handler = ParseBlock(); tryCtx.UpdateWith(handler.context); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode == null) handler = new Block(CurrentPositionContext()); else handler = exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_NoTrySkipTokenSet, exc) == -1){ Debug.Assert((type == null) ? exc._partiallyComputedNode == null : true); if (type != null) exc._partiallyComputedNode = new Try(tryCtx, body, id, type, handler, null, false, tryEndContext); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_NoTrySkipTokenSet); } } try{ if (JSToken.Finally == this.currentToken.token){ GetNextToken(); this.blockType.Add(BlockType.Finally); try{ finally_block = ParseBlock(); catchOrFinally = true; }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } tryCtx.UpdateWith(finally_block.context); } }catch(RecoveryTokenException exc){ excInFinally = exc; // thrown later so we can execute code below } if (!catchOrFinally){ ReportError(JSError.NoCatch, true); finally_block = new Block(CurrentPositionContext()); // make a dummy empty block } }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } bool isFinallyEscaped = false; if (this.finallyEscaped > 0){ this.finallyEscaped--; isFinallyEscaped = true; } if (excInFinally != null){ excInFinally._partiallyComputedNode = new Try(tryCtx, body, id, type, handler, finally_block, isFinallyEscaped, tryEndContext); throw excInFinally; }else return new Try(tryCtx, body, id, type, handler, finally_block, isFinallyEscaped, tryEndContext); }
//--------------------------------------------------------------------------------------- // ParseStaticInitializer // // StaticInitializer : // '{' FunctionBody '}' //--------------------------------------------------------------------------------------- private AST ParseStaticInitializer(Context initContext){ Block body = null; FunctionScope scope = new FunctionScope(Globals.ScopeStack.Peek()); scope.isStatic = true; // make a new state and save the old one ArrayList blockType = this.blockType; this.blockType = new ArrayList(16); SimpleHashtable labelTable = this.labelTable; this.labelTable = new SimpleHashtable(16); this.blockType.Add(BlockType.Block); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try{ Globals.ScopeStack.Push(scope); //Give declarations a place to go while building AST // parse the block locally to get the exact end of function body = new Block(this.currentToken.Clone()); GetNextToken(); while (JSToken.RightCurly != this.currentToken.token){ try{ body.Append(ParseStatement()); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null) body.Append(exc._partiallyComputedNode); if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) throw exc; } } }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = new StaticInitializer(initContext, body, scope); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.blockType = blockType; this.labelTable = labelTable; Globals.ScopeStack.Pop(); } body.context.UpdateWith(this.currentToken); initContext.UpdateWith(this.currentToken); GetNextToken(); return new StaticInitializer(initContext, body, scope); }
//--------------------------------------------------------------------------------------- // ParsePackage // // Package : // 'package' QualifiedIdentifier '{' ClassList '}' // // ClassList : // <empty> | // Class ClassList | // Attributes Class ClassList | // Attributes Enum ClassList //--------------------------------------------------------------------------------------- // Because 'package' is not a reserved word in JS5 we have to deal with an ambiguity // in the grammar. A source sequence like the following // package // x // { } // can be legally parsed in two ways: // Identifier Identifier Block or // Package // we give Package priority in this situation. // Here is how we deal with some possible cases: // 1- ** package <no line break> QualifiedIdentifier ** is parsed unambiguously as a package production regardless of what comes after Identifier // 2- ** package <no line break> NotOneOf(Operator | '[' | '.' | '(' | Identifier) '{' ** is parsed as a package production with an error // 3- ** package <line break> '{' ** is parsed as a package (anonymous) with an error // 4- ** package <line break> Not(Identifier) ** is never parsed as a package private AST ParsePackage(Context packageContext){ GetNextToken(); AST qualid = null; bool gotLineBreak = this.scanner.GotEndOfLine(); // erroneous package production if (JSToken.Identifier != this.currentToken.token){ if (JSScanner.CanParseAsExpression(this.currentToken.token)){ // it's an expression. Report a warning. package and this.currentToken can be an expression (i.e. 'package +') ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); qualid = new Lookup("package", packageContext); // get the member expression qualid = MemberExpression(qualid, null); bool isLeftHandSide; qualid = ParsePostfixExpression(qualid, out isLeftHandSide); qualid = ParseExpression(qualid, false, isLeftHandSide, JSToken.None); return new Expression(qualid.context.Clone(), qualid); }else if (!gotLineBreak){ if (JSToken.Increment == this.currentToken.token || JSToken.Decrement == this.currentToken.token){ // it's a postfix expression. Report a warning ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); bool dummy; qualid = new Lookup("package", packageContext); qualid = ParsePostfixExpression(qualid, out dummy); qualid = ParseExpression(qualid, false, false, JSToken.None); return new Expression(qualid.context.Clone(), qualid); } }else{ // it's an expression. Report a warning which, as a side effect, will make the current token be the next token fetched ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); return new Lookup("package", packageContext); } }else{ // it is an identifier, parse it as a qualified identifier this.errorToken = this.currentToken; // this will make GetNextToken() in ParseQualifiedIdentifier() return this.currentToken qualid = ParseQualifiedIdentifier(JSError.NoIdentifier); } // if we are here we have: // ** package QualifiedIdentifier ** or // ** package SomeNonSenseToken **, that is a token that does not make an expression Context nonSenseToken = null; if (JSToken.LeftCurly != this.currentToken.token && qualid == null){ // we want to peek and see whether the next token is a LeftCurly nonSenseToken = this.currentToken.Clone(); GetNextToken(); } if (JSToken.LeftCurly == this.currentToken.token){ // sounds like a package, possibly with an error. If qualid is not null is actually a good package, otherwise we treat it // as an anonymous package and keep going. if (qualid == null){ if (nonSenseToken == null) nonSenseToken = this.currentToken.Clone(); ReportError(JSError.NoIdentifier, nonSenseToken, true); } }else{ if (qualid == null){ // this is pretty screwy, let's ignore the package keyword for a start ReportError(JSError.SyntaxError, packageContext); if (JSScanner.CanStartStatement(nonSenseToken.token)){ // this is tricky we assign nonSenseToken to this.currentToken and call ParseStatement, because we know it is a statement start token. // The parser should then call GetNextToken() which will return the this.currentToken that is assigned to this.errorToken this.currentToken = nonSenseToken; return ParseStatement(); }else{ //ReportError(JSError.SyntaxError, nonSenseToken); if (JSScanner.CanStartStatement(this.currentToken.token)){ this.errorToken = null; return ParseStatement(); }else{ ReportError(JSError.SyntaxError); SkipTokensAndThrow(); } } }else{ if (gotLineBreak){ // we are here with the following: 'package' <line break> QalifiedIdentifier' however we do not have a left curly. // if the token in our hand can start an expression we go with two expressions, otherwise we accept it as a package //if (JSScanner.CanParseAsExpression(this.currentToken.token)){ ReportError(JSError.KeywordUsedAsIdentifier, packageContext.Clone(), true); Block block = new Block(packageContext.Clone()); block.Append(new Lookup("package", packageContext)); qualid = MemberExpression(qualid, null); bool isLeftHandSide; qualid = ParsePostfixExpression(qualid, out isLeftHandSide); qualid = ParseExpression(qualid, false, true, JSToken.None); block.Append(new Expression(qualid.context.Clone(), qualid)); block.context.UpdateWith(qualid.context); return block; //} } // the package production rule is entered regardless of the presence of a left curly. ReportError(JSError.NoLeftCurly); } } PackageScope pscope = new PackageScope(Globals.ScopeStack.Peek()); Globals.ScopeStack.Push(pscope); //Give declarations a place to go while building AST try{ string name = (qualid != null) ? qualid.ToString() : "anonymous package"; pscope.name = name; packageContext.UpdateWith(this.currentToken); ASTList classList = new ASTList(packageContext); GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet); try{ while (this.currentToken.token != JSToken.RightCurly){ AST ast = null; try{ switch (this.currentToken.token){ case JSToken.Interface: case JSToken.Class: classList.Append(ParseClass((FieldAttributes)0, false, this.currentToken.Clone(), false, false, null)); break; case JSToken.Enum: classList.Append(ParseEnum((FieldAttributes)0, this.currentToken.Clone(), null)); break; case JSToken.Internal: case JSToken.Public: case JSToken.Static: case JSToken.Private: case JSToken.Protected: case JSToken.Abstract: case JSToken.Final: bool parsedOK; ast = ParseAttributes(null, true, false, out parsedOK); if (parsedOK){ if (ast is Class){ classList.Append(ast); break; } } ReportError(JSError.OnlyClassesAllowed, ast.context.Clone(), true); SkipTokensAndThrow(); break; case JSToken.Identifier: bool bAssign, canBeAttribute = true; ast = ParseUnaryExpression(out bAssign, ref canBeAttribute, false); if (canBeAttribute){ bool parsed; ast = ParseAttributes(ast, true, false, out parsed); if (parsed){ if (ast is Class){ classList.Append(ast); break; } } } ReportError(JSError.OnlyClassesAllowed, ast.context.Clone(), true); SkipTokensAndThrow(); break; case JSToken.EndOfFile: EOFError(JSError.ErrEOF); throw new EndOfFile(); // abort parsing, get back to the main parse routine case JSToken.Semicolon: // ignore any spurious semicolon GetNextToken(); break; case JSToken.Import: // handle common error of using import in package ReportError(JSError.InvalidImport, true); try{ ParseImportStatement(); }catch(RecoveryTokenException){ } break; case JSToken.Package: // handle common error of using package in package Context nestedPackageContext = this.currentToken.Clone(); AST statement = ParsePackage(nestedPackageContext); if (statement is Package) ReportError(JSError.PackageInWrongContext, nestedPackageContext, true); break; default: ReportError(JSError.OnlyClassesAllowed, (ast != null) ? ast.context.Clone() : CurrentPositionContext(), true); SkipTokensAndThrow(); break; } }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null && exc._partiallyComputedNode is Class){ classList.Append((Class)exc._partiallyComputedNode); exc._partiallyComputedNode = null; } if (IndexOfToken(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet, exc) == -1) throw exc; } } }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1){ ReportError(JSError.NoRightCurly, CurrentPositionContext()); exc._partiallyComputedNode = new Package(name, qualid, classList, packageContext); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_PackageBodyNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } GetNextToken(); return new Package(name, qualid, classList, packageContext); }finally{ Globals.ScopeStack.Pop(); } }
private AST ParseFunction(FieldAttributes visibilitySpec, bool inExpression, Context fncCtx, bool isMethod, bool isAbstract, bool isFinal, bool isInterface, CustomAttributeList customAttributes, Call function){ IdentifierLiteral name = null; AST interfaceName = null; ArrayList formalParameters = null; TypeExpression returnType = null; Block body = null; bool isGetter = false; bool isSetter = false; if (function == null){ GetNextToken(); if (isMethod) if (JSToken.Get == this.currentToken.token){ isGetter = true; GetNextToken(); }else if (JSToken.Set == this.currentToken.token){ isSetter = true; GetNextToken(); } // get the function name or make an anonymous function if in expression "position" if (JSToken.Identifier == this.currentToken.token){ name = new IdentifierLiteral(this.scanner.GetIdentifier(), this.currentToken.Clone()); GetNextToken(); if (JSToken.AccessField == this.currentToken.token){ if (isInterface) // "function IBar.foo()" is illegal in an interface declaration ReportError(JSError.SyntaxError, true); GetNextToken(); if (JSToken.Identifier == this.currentToken.token){ interfaceName = new Lookup(name.context); name = new IdentifierLiteral(this.scanner.GetIdentifier(), this.currentToken.Clone()); GetNextToken(); while (JSToken.AccessField == this.currentToken.token){ GetNextToken(); if (JSToken.Identifier == this.currentToken.token){ interfaceName = new Member(interfaceName.context.CombineWith(this.currentToken), interfaceName, new ConstantWrapper(name.ToString(), name.context)); name = new IdentifierLiteral(this.scanner.GetIdentifier(), this.currentToken.Clone()); GetNextToken(); }else ReportError(JSError.NoIdentifier, true); } }else ReportError(JSError.NoIdentifier, true); } }else{ string identifier = JSKeyword.CanBeIdentifier(this.currentToken.token); if (null != identifier){ ForceReportInfo(JSError.KeywordUsedAsIdentifier, isMethod); name = new IdentifierLiteral(identifier, this.currentToken.Clone()); GetNextToken(); }else{ if (!inExpression){ identifier = this.currentToken.GetCode(); ReportError(JSError.NoIdentifier, true); GetNextToken(); }else identifier = ""; name = new IdentifierLiteral(identifier, CurrentPositionContext()); } } }else{ // function was passed in, this is an error condition name = function.GetName(); } // make a new state and save the old one ArrayList blockType = this.blockType; this.blockType = new ArrayList(16); SimpleHashtable labelTable = this.labelTable; this.labelTable = new SimpleHashtable(16); FunctionScope fscope = new FunctionScope(Globals.ScopeStack.Peek(), isMethod); Globals.ScopeStack.Push(fscope); //Give declarations a place to go while building AST try{ formalParameters = new ArrayList(); Context paramArrayContext = null; if (function == null){ // get the formal parameters if (JSToken.LeftParen != this.currentToken.token) ReportError(JSError.NoLeftParen); GetNextToken(); // create the list of arguments and update the context while (JSToken.RightParen != this.currentToken.token){ if (paramArrayContext != null){ ReportError(JSError.ParamListNotLast, paramArrayContext, true); paramArrayContext = null; } String id = null; TypeExpression typeExpr = null; this.noSkipTokenSet.Add(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet); try{ if (JSToken.ParamArray == this.currentToken.token){ paramArrayContext = this.currentToken.Clone(); GetNextToken(); } if (JSToken.Identifier != this.currentToken.token && (id = JSKeyword.CanBeIdentifier(this.currentToken.token)) == null){ if (JSToken.LeftCurly == this.currentToken.token){ ReportError(JSError.NoRightParen); break; }else if (JSToken.Comma == this.currentToken.token){ // We're missing an argument (or previous argument was malformed and // we skipped to the comma.) Keep trying to parse the argument list -- // we will skip the comma below. ReportError(JSError.SyntaxError, true); }else{ ReportError(JSError.SyntaxError, true); SkipTokensAndThrow(); } }else{ if (null == id) id = this.scanner.GetIdentifier(); else ForceReportInfo(JSError.KeywordUsedAsIdentifier); Context paramCtx = this.currentToken.Clone(); GetNextToken(); if (JSToken.Colon == this.currentToken.token){ typeExpr = ParseTypeExpression(); if (null != typeExpr) paramCtx.UpdateWith(typeExpr.context); } CustomAttributeList custAttrs = null; if (paramArrayContext != null){ custAttrs = new CustomAttributeList(paramArrayContext); custAttrs.Append(new CustomAttribute(paramArrayContext, new Lookup("...", paramArrayContext), new ASTList(null))); } formalParameters.Add(new ParameterDeclaration(paramCtx, id, typeExpr, custAttrs)); } // got an arg, it should be either a ',' or ')' if (JSToken.RightParen == this.currentToken.token) break; else if (JSToken.Comma != this.currentToken.token){ // deal with error in some "intelligent" way if (JSToken.LeftCurly == this.currentToken.token){ ReportError(JSError.NoRightParen); break; }else{ if (JSToken.Identifier == this.currentToken.token && typeExpr == null){ // it's possible that the guy was writing the type in C/C++ style (i.e. int x) ReportError(JSError.NoCommaOrTypeDefinitionError); }else ReportError(JSError.NoComma); } } GetNextToken(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet, exc) == -1) throw exc; }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet); } } fncCtx.UpdateWith(this.currentToken); // if it is a getter/setter must have 0/1 arg only if (isGetter && formalParameters.Count != 0){ ReportError(JSError.BadPropertyDeclaration, true); isGetter = false; }else if (isSetter && formalParameters.Count != 1){ ReportError(JSError.BadPropertyDeclaration, true); isSetter = false; } GetNextToken(); // check the return type if (JSToken.Colon == this.currentToken.token){ if (isSetter) ReportError(JSError.SyntaxError); this.noSkipTokenSet.Add(NoSkipTokenSet.s_StartBlockNoSkipTokenSet); try{ returnType = ParseTypeExpression(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_StartBlockNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = null; throw exc; }else{ if (exc._partiallyComputedNode != null) returnType = (TypeExpression)exc._partiallyComputedNode; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_StartBlockNoSkipTokenSet); } if (isSetter) returnType = null; } }else{ // function was passed in, this is an error condition function.GetParameters(formalParameters); } // read the function body of non-abstract functions. if (JSToken.LeftCurly != this.currentToken.token && (isAbstract || (isMethod && GuessIfAbstract()))){ if (!isAbstract){ isAbstract = true; ReportError(JSError.ShouldBeAbstract, fncCtx, true); } body = new Block(this.currentToken.Clone()); }else{ if (JSToken.LeftCurly != this.currentToken.token) ReportError(JSError.NoLeftCurly, true); else if (isAbstract) ReportError(JSError.AbstractWithBody, fncCtx, true); this.blockType.Add(BlockType.Block); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try{ // parse the block locally to get the exact end of function body = new Block(this.currentToken.Clone()); GetNextToken(); while (JSToken.RightCurly != this.currentToken.token){ try{ body.Append(ParseStatement()); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null){ body.Append(exc._partiallyComputedNode); } if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) throw exc; } } body.context.UpdateWith(this.currentToken); fncCtx.UpdateWith(this.currentToken); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1){ Globals.ScopeStack.Pop(); //Pop current scope so that FunctionDeclaration sees proper scope stack try{ ParameterDeclaration[] foParameters = new ParameterDeclaration[formalParameters.Count]; formalParameters.CopyTo(foParameters); if (inExpression) exc._partiallyComputedNode = new FunctionExpression(fncCtx, name, foParameters, returnType, body, fscope, visibilitySpec); else exc._partiallyComputedNode = new FunctionDeclaration(fncCtx, interfaceName, name, foParameters, returnType, body, fscope, visibilitySpec, isMethod, isGetter, isSetter, isAbstract, isFinal, customAttributes); if (customAttributes != null) customAttributes.SetTarget(exc._partiallyComputedNode); }finally{ Globals.ScopeStack.Push(fscope); //Push it back so that the next finally can pop it } throw exc; } }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } GetNextToken(); } }finally{ // restore state this.blockType = blockType; this.labelTable = labelTable; Globals.ScopeStack.Pop(); } ParameterDeclaration[] fParameters = new ParameterDeclaration[formalParameters.Count]; formalParameters.CopyTo(fParameters); AST func; if (inExpression) func = new FunctionExpression(fncCtx, name, fParameters, returnType, body, fscope, visibilitySpec); else func = new FunctionDeclaration(fncCtx, interfaceName, name, fParameters, returnType, body, fscope, visibilitySpec, isMethod, isGetter, isSetter, isAbstract, isFinal, customAttributes); if (customAttributes != null) customAttributes.SetTarget(func); return func; }
internal void Init (Block body, FormalParameterList p) { func_obj.body = body; func_obj.parameters = p; }
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) { }
AST Statements(AST parent) { int tt; Block pn = new Block (parent, new Location (ts.SourceName, ts.LineNumber)); while ((tt = ts.PeekToken ()) > Token.EOF && tt != Token.RC) pn.Add (Statement (pn)); return pn; }
//--------------------------------------------------------------------------------------- // ParseSwitchStatement // // SwitchStatement : // 'switch' '(' Expression ')' '{' CaseBlock '}' // // CaseBlock : // CaseList DefaultCaseClause CaseList // // CaseList : // <empty> | // CaseClause CaseList // // CaseClause : // 'case' Expression ':' OptionalStatements // // DefaultCaseClause : // <empty> | // 'default' ':' OptionalStatements //--------------------------------------------------------------------------------------- private AST ParseSwitchStatement(){ Context switchCtx = this.currentToken.Clone(); AST expr = null; ASTList cases = null; this.blockType.Add(BlockType.Switch); try{ // read switch(expr) GetNextToken(); if (JSToken.LeftParen != this.currentToken.token) ReportError(JSError.NoLeftParen); GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet); try{ expr = ParseExpression(); if (JSToken.RightParen != this.currentToken.token){ ReportError(JSError.NoRightParen); } GetNextToken(); if (JSToken.LeftCurly != this.currentToken.token){ ReportError(JSError.NoLeftCurly); } GetNextToken(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1 && IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1 ){ // give up exc._partiallyComputedNode = null; throw exc; }else{ if (exc._partiallyComputedNode == null) expr = new ConstantWrapper(true, CurrentPositionContext()); else expr = exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) != -1){ if (exc._token == JSToken.RightParen) GetNextToken(); if (JSToken.LeftCurly != this.currentToken.token){ ReportError(JSError.NoLeftCurly); } GetNextToken(); } } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } // parse the switch body cases = new ASTList(this.currentToken.Clone()); bool defaultStatement = false; this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); try{ while (JSToken.RightCurly != this.currentToken.token){ SwitchCase caseClause = null; AST caseValue = null; Context caseCtx = this.currentToken.Clone(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_CaseNoSkipTokenSet); try{ if (JSToken.Case == this.currentToken.token){ // get the case GetNextToken(); caseValue = ParseExpression(); }else if (JSToken.Default == this.currentToken.token){ // get the default if (defaultStatement) // we report an error but we still accept the default ReportError(JSError.DupDefault, true); else defaultStatement = true; GetNextToken(); }else{ // This is an error, there is no case or default. Assume a default was missing and keep going defaultStatement = true; ReportError(JSError.BadSwitch); } if (JSToken.Colon != this.currentToken.token) ReportError(JSError.NoColon); // read the statements inside the case or default GetNextToken(); }catch(RecoveryTokenException exc){ // right now we can only get here for the 'case' statement if (IndexOfToken(NoSkipTokenSet.s_CaseNoSkipTokenSet, exc) == -1){ // ignore the current case or default exc._partiallyComputedNode = null; throw exc; }else{ caseValue = exc._partiallyComputedNode; if (exc._token == JSToken.Colon) GetNextToken(); } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_CaseNoSkipTokenSet); } this.blockType.Add(BlockType.Block); try{ Block statements = new Block(this.currentToken.Clone()); this.noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try{ while (JSToken.RightCurly != this.currentToken.token && JSToken.Case != this.currentToken.token && JSToken.Default != this.currentToken.token){ try{ statements.Append(ParseStatement()); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null){ statements.Append(exc._partiallyComputedNode); exc._partiallyComputedNode = null; } if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) throw exc; } } }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1){ if (null == caseValue) caseClause = new SwitchCase(caseCtx, statements); else caseClause = new SwitchCase(caseCtx, caseValue, statements); cases.Append(caseClause); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet); } if (JSToken.RightCurly == this.currentToken.token) statements.context.UpdateWith(this.currentToken); if (null == caseValue){ caseCtx.UpdateWith(statements.context); caseClause = new SwitchCase(caseCtx, statements); }else{ caseCtx.UpdateWith(statements.context); caseClause = new SwitchCase(caseCtx, caseValue, statements); } cases.Append(caseClause); }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } } }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1){ //save what you can a rethrow switchCtx.UpdateWith(CurrentPositionContext()); exc._partiallyComputedNode = new Switch(switchCtx, expr, cases); throw exc; } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } switchCtx.UpdateWith(this.currentToken); GetNextToken(); }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } return new Switch(switchCtx, expr, cases); }
internal DeleteInfo(int index, Block block) { this.index = index; this.block = block; }
//--------------------------------------------------------------------------------------- // ParseWithStatement // // WithStatement : // 'with' '(' Expression ')' Statement //--------------------------------------------------------------------------------------- private With ParseWithStatement(){ Context withCtx = this.currentToken.Clone(); AST obj = null; AST block = null; this.blockType.Add(BlockType.Block); try{ GetNextToken(); if (JSToken.LeftParen != this.currentToken.token) ReportError(JSError.NoLeftParen); GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try{ obj = ParseExpression(); if (JSToken.RightParen != this.currentToken.token){ withCtx.UpdateWith(obj.context); ReportError(JSError.NoRightParen); }else withCtx.UpdateWith(this.currentToken); GetNextToken(); }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1){ // give up exc._partiallyComputedNode = null; throw exc; }else{ if (exc._partiallyComputedNode == null) obj = new ConstantWrapper(true, CurrentPositionContext()); else obj = exc._partiallyComputedNode; withCtx.UpdateWith(obj.context); if (exc._token == JSToken.RightParen) GetNextToken(); } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } try{ block = ParseStatement(); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode == null) block = new Block(CurrentPositionContext()); else block = exc._partiallyComputedNode; exc._partiallyComputedNode = new With(withCtx, obj, block); } }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } return new With(withCtx, obj, block); }
Block ParseBlock(out Context closingBraceContext){ closingBraceContext = null; this.blockType.Add(BlockType.Block); Block codeBlock = new Block(this.currentToken.Clone()); GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); try{ try{ while (JSToken.RightCurly != this.currentToken.token){ try{ codeBlock.Append(ParseStatement()); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null) codeBlock.Append(exc._partiallyComputedNode); if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) throw exc; } } }catch(RecoveryTokenException exc){ if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1){ exc._partiallyComputedNode = codeBlock; throw exc; } } }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); this.noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); this.blockType.RemoveAt(this.blockType.Count - 1); } closingBraceContext = this.currentToken.Clone(); // update the block context codeBlock.context.UpdateWith(this.currentToken); GetNextToken(); return codeBlock; }
//--------------------------------------------------------------------------------------- // ParseClassBody // // ClassBody : // '{' OptionalClassMembers '}' //--------------------------------------------------------------------------------------- Block ParseClassBody(bool isEnum, bool isInterface){ this.blockType.Add(BlockType.Block); Block codeBlock = new Block(this.currentToken.Clone()); try{ GetNextToken(); this.noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); JSToken[] noSkip = null; if (isEnum) noSkip = NoSkipTokenSet.s_EnumBodyNoSkipTokenSet; else if (isInterface) noSkip = NoSkipTokenSet.s_InterfaceBodyNoSkipTokenSet; else noSkip = NoSkipTokenSet.s_ClassBodyNoSkipTokenSet; try{ while (JSToken.RightCurly != this.currentToken.token){ if (JSToken.EndOfFile == this.currentToken.token){ ReportError(JSError.NoRightCurly, true); SkipTokensAndThrow(); } this.noSkipTokenSet.Add(noSkip); try{ AST classMember = isEnum ? ParseEnumMember() : ParseClassMember(isInterface); if (classMember != null) codeBlock.Append(classMember); }catch(RecoveryTokenException exc){ if (exc._partiallyComputedNode != null) codeBlock.Append(exc._partiallyComputedNode); if (IndexOfToken(noSkip, exc) == -1){ exc._partiallyComputedNode = null; throw exc; } }finally{ this.noSkipTokenSet.Remove(noSkip); } } }catch(RecoveryTokenException exc){ exc._partiallyComputedNode = codeBlock; throw exc; }finally{ this.noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } codeBlock.context.UpdateWith(this.currentToken); GetNextToken(); }finally{ this.blockType.RemoveAt(this.blockType.Count - 1); } return codeBlock; }